summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--aesthetics.go73
-rw-r--r--backend.go42
-rw-r--r--main.go351
3 files changed, 352 insertions, 114 deletions
diff --git a/aesthetics.go b/aesthetics.go
index 1881ec6..343b468 100644
--- a/aesthetics.go
+++ b/aesthetics.go
@@ -1,5 +1,8 @@
package main
+// aesthetics.go contains all the functions which
+// display or setup visuals without the use of tview.
+
import (
"strconv"
)
@@ -20,7 +23,7 @@ import (
// 8 ~ ~ ~ ~ ◀ ▬ ▬ ▷ ~ ~
// 9 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
// Only E8, F8, and G8 were hit.
-func (plyr player) PrimaryDisplay() string {
+func (plyr player) DisplayPrimary() string {
text := "\n A B C D E F G H I J \n"
for i := 0; i < 10; i++ {
text += strconv.Itoa(i)
@@ -42,49 +45,49 @@ func (plyr player) PrimaryDisplay() string {
return text
}
-// printTarget displays an ASCII version of the target battleship board
-// The target board is the one which shows a player what he knows about
-// his opponent. This is the board a player would play on.
-// Here is an example
-// A B C D E F G H I J
-// 0 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// 1 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// 2 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// 3 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// 4 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// 5 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// 6 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// 7 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// 8 ~ ~ ~ ~ ▣ ▣ ▣ ~ ~ ~
-// 9 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
-// Since the boat in F8, E8, G8 is not sunk, The player does not see
-// it's shape. Upon sinking it, he will be able to see it.
-func (plyr player) TargetDisplay() string {
+func (plyr *player) DisplayTarget() string {
+ // First row labelling the columns
text := "\n A B C D E F G H I J \n"
- for i := 0; i < 10; i++ {
- text += strconv.Itoa(i)
- text += " "
- for j := 0; j < 10; j++ {
- switch plyr.target[i][j][0] {
+ // For every row (r)
+ for r := 0; r < 10; r++ {
+ // Add the row number at the start of each line
+ text += strconv.Itoa(r)
+ // For every column (c)
+ for c := 0; c < 10; c++ {
+ // Separate each character with a space
+ text += " "
+ // First thing: is the coordinate hit or not?
+ switch plyr.target[r][c][0] {
+ // The coordinate is NOT hit:
case 0:
+ // Add the `~` symbol
text += boatchars[1][0]
- case 1:
- if plyr.gains[plyr.prey.primary[i][j][0]] {
- text += boatchars[0][plyr.prey.primary[i][j][1]]
- } else {
- switch plyr.prey.primary[i][j][0] {
- case 0:
- text += boatchars[0][0]
+ // If the coordinate is NOT hit:
+ default:
+ // Is the coordinate water?
+ switch plyr.prey.primary[r][c][0] {
+ // It IS water:
+ case 6:
+ // Add the `◌` symbol
+ text += boatchars[0][0]
+ // It's NOT water:
+ default:
+ // Is the ID of the boat at that coordinate marked as a gain?
+ switch plyr.gains[plyr.prey.primary[r][c][0]] {
+ // It IS marked as a gain:
+ case true:
+ // Show the boat as it's meant to be:
+ // (hit boatchars: with `position` marked in the opponents primary)
+ text += boatchars[0][plyr.prey.primary[r][c][1]]
default:
- text += mistery_hit
+ // Nope, you're getting a censored tile: `▣`
+ text += misteryHit
}
}
}
- text += " "
}
text += "\n"
}
- // fmt.Println(text)
return text
}
@@ -107,4 +110,4 @@ var boatchars = [2][7]string{
// This constant keeps information about boats that aren't totally sunk secret.
// It therefore substitues the shape of a boat on the target board when it is unsunk.
-const mistery_hit = `▣`
+const misteryHit = `▣`
diff --git a/backend.go b/backend.go
index d1cd758..868739d 100644
--- a/backend.go
+++ b/backend.go
@@ -5,9 +5,9 @@ import (
)
type parameters struct {
- vs_cpu bool
- ssh_game bool
- debug bool
+ vsCPU bool
+ sshGame bool
+ debug bool
}
type userError struct {
@@ -55,6 +55,7 @@ var boatlist = [5][2][5]int{
// target stores info the player knows about the ennemy.
// gains keeps track of the ships the player has managed to sink.
type player struct {
+ name string
primary [10][10][3]int
// Primary Boat Tile Vector
// [y][x][boatID, position, hitStatus]int
@@ -89,15 +90,25 @@ type player struct {
prey *player
}
+func (plyr *player) InitBoard(opponent *player) {
+ plyr.prey = opponent
+
+ for i := 0; i < 10; i++ {
+ for j := 0; j < 10; j++ {
+ plyr.primary[i][j] = [3]int{6, 0, 0}
+ plyr.target[i][j] = [2]int{0, 6}
+
+ }
+ }
+}
+
// initBoat places a boat on a players primary grid.
// boat sizes are defined by the boatlist variable.
// Consult it for more info.
-func initBoat(plyr *player, boat [4]int) {
- boatID := boat[0]
- boat_length := len(boatlist[boatID][0])
- x, y := boat[2], boat[3]
- if boat[1] == 0 { // Boat is HORIZONTAL
- for i := 0; i < boat_length; i++ {
+func (plyr *player) InitBoat(boatID, orientation, x, y int) {
+ boatLength := len(boatlist[boatID][0])
+ if orientation == 0 { // Boat is HORIZONTAL
+ for i := 0; i < boatLength; i++ {
char := boatlist[boatID][0][i]
if char == 0 { // Compensating for boatlist having
break // zeros at the end of slices
@@ -108,7 +119,7 @@ func initBoat(plyr *player, boat [4]int) {
x++ // HORIZONTAL: Therefore the loop increments the x axis
}
} else { // Boat is VERTICAL
- for i := 0; i < boat_length; i++ {
+ for i := 0; i < boatLength; i++ {
char := boatlist[boatID][1][i]
if char == 0 {
break
@@ -120,12 +131,12 @@ func initBoat(plyr *player, boat [4]int) {
}
}
-func (plyr *player) Hit(coord [2]int) bool {
- x, y := coord[0], coord[1]
+func (plyr *player) Hit(x, y int) bool {
plyr.prey.primary[y][x][2] = 1
+ // We change the coord status to hit and add the id of
+ // the boat since we know it now.
plyr.target[y][x] = [2]int{1, plyr.prey.primary[y][x][0]}
-
- if BoatID := plyr.prey.primary[y][x][0]; BoatID > 0 {
+ if BoatID := plyr.prey.primary[y][x][0]; BoatID < 6 {
switch plyr.prey.primary[y][x][1] {
case 1, 2, 5: // If the hit boat was vertical
// We suppose the boat is sunk since it only takes one unhit coordinate to prove this wrong
@@ -161,3 +172,6 @@ func (plyr *player) Hit(coord [2]int) bool {
return false // Returns false if water was hit
}
}
+
+const horizontal = 0
+const vertical = 1
diff --git a/main.go b/main.go
index 933b63c..1dbef92 100644
--- a/main.go
+++ b/main.go
@@ -2,81 +2,302 @@ package main
import (
"fmt"
+ tc "github.com/gdamore/tcell"
+ tv "github.com/rivo/tview"
+ "strconv"
+ "strings"
)
func main() {
settings := parameters{
- debug: true,
- ssh_game: false,
+ debug: true,
+ sshGame: false,
}
+ // SETUP:
+ var playerOne = player{name: "Ben"}
+ var playerTwo = player{name: "Hugo"}
+ // Setting up prey for when using Hit function
+ playerOne.InitBoard(&playerTwo)
+ playerTwo.InitBoard(&playerOne)
+
if settings.debug {
- // SETUP:
- var player_one = player{}
- var player_two = player{}
- // Setting up prey for when using Hit function
- player_one.prey = &player_two
- player_two.prey = &player_one
-
- if settings.debug {
- fmt.Println("# #---TESTING SEQUENCE---# #")
-
- player_one.primary = [10][10][3]int{} // Empty plyr1 board
- // Initialising boats on the board work* (see initBoat TODOS)
- initBoat(&player_one, [4]int{0, 0, 0, 0}) // Index 0 -> Carrier
- initBoat(&player_one, [4]int{1, 0, 6, 9}) // Index 1 -> Battleship
- initBoat(&player_one, [4]int{2, 1, 4, 3}) // Index 2 -> Destroyer
- initBoat(&player_one, [4]int{3, 1, 7, 1}) // Index 3 -> Submarine
- initBoat(&player_one, [4]int{4, 1, 1, 8}) // Index 4 -> PatrolBoat
- // fmt.Println("Player 1:")
- // fmt.Print(player_one.PrimaryDisplay())
-
- // Initialising the board works
- // fmt.Println("Empty:")
- // fmt.Print(player_one.PrimaryDisplay())
-
- player_two.primary = [10][10][3]int{} // Empty plyr2 board
- initBoat(&player_two, [4]int{0, 0, 3, 3}) // Index 0 -> Carrier
- initBoat(&player_two, [4]int{1, 0, 4, 8}) // Index 1 -> Battleship
- initBoat(&player_two, [4]int{2, 1, 2, 4}) // Index 2 -> Destroyer
- initBoat(&player_two, [4]int{3, 1, 9, 0}) // Index 3 -> Submarine
- initBoat(&player_two, [4]int{4, 1, 7, 4}) // Index 4 -> PatrolBoat
-
- fmt.Println("Player 2:")
- fmt.Print(player_two.PrimaryDisplay())
-
- // fmt.Println("Hit B2:")
- // hit_coord := [2]int{1, 2} // Water hit at B2
- // fmt.Print("There was a boat: ")
- // fmt.Println(player_one.Hit(hit_coord))
- // fmt.Println(player_two.PrimaryDisplay())
- // fmt.Println("Hit H4:")
- // hit_coord = [2]int{7, 4} // PatrolBoat hit at H4
- // fmt.Print("There was a boat: ")
- // fmt.Println(player_one.Hit(hit_coord))
- // fmt.Println(player_two.PrimaryDisplay())
- // fmt.Println("Player 1 TargetDisplay:")
- // fmt.Println(player_one.TargetDisplay())
- // fmt.Println("Hit H5:")
- // hit_coord = [2]int{7, 5} // PatrolBoat hit at H4
- // fmt.Print("There was a boat: ")
- // fmt.Println(player_one.Hit(hit_coord))
- // fmt.Println(player_two.PrimaryDisplay())
- // fmt.Println("Player 1 TargetDisplay:")
- // fmt.Println(player_one.TargetDisplay())
-
- fmt.Println("Hit: E8, F8, G8")
- player_one.Hit([2]int{4, 8})
- player_one.Hit([2]int{5, 8})
- player_one.Hit([2]int{6, 8})
- fmt.Println(player_two.PrimaryDisplay())
- fmt.Println(player_one.TargetDisplay())
- player_one.Hit([2]int{6, 8})
- fmt.Println(player_two.PrimaryDisplay())
- fmt.Println(player_one.TargetDisplay())
+ // PLACING PLAYER ONE BOATS:
+ // Carrier: (ID=0), horizontal, (1,1)
+ playerOne.InitBoat(0, horizontal, 1, 1)
+ // Battleship: (ID=1), horizontal, (0,9)
+ playerOne.InitBoat(1, horizontal, 0, 9)
+ // Destroyer: (ID=2), vertical, (5,6)
+ playerOne.InitBoat(2, vertical, 5, 6)
+ // Submarine: (ID=3), horizontal, (6,2)
+ playerOne.InitBoat(3, horizontal, 6, 2)
+ // Patrol Boat: (ID=4), vertical, (1,5)
+ playerOne.InitBoat(4, vertical, 1, 5)
+
+ // PLACING PLAYER TWO BOATS:
+ // Carrier: (ID=0), vertical, (9,0)
+ playerTwo.InitBoat(0, vertical, 9, 0)
+ // Battleship: (ID=1), horizontal, (1,8)
+ playerTwo.InitBoat(1, horizontal, 1, 8)
+ // Destroyer: (ID=2), vertical, (5,3)
+ playerTwo.InitBoat(2, vertical, 5, 3)
+ // Submarine: (ID=3), horizontal, (2,2)
+ playerTwo.InitBoat(3, horizontal, 2, 2)
+ // Patrol Boat: (ID=4), vertical, (7,6)
+ playerTwo.InitBoat(4, vertical, 6, 6)
+
+ // HITTING PLAYER ONE AT DIFFERENT COORDINATES
+ playerTwo.Hit(5, 6) // (F,6)
+ playerTwo.Hit(5, 7) // (F,7)
+ playerTwo.Hit(5, 8) // (F,8)
+ playerTwo.Hit(3, 4) // (D,4)
+ playerTwo.Hit(3, 9) // (D,9)
+ playerTwo.Hit(6, 6) // (G,6)
+ playerTwo.Hit(2, 9) // (C,9)
+
+ // HITTING PLAYER TWO AT DIFFERENT COORDINATES
+ playerOne.Hit(1, 4) // (B,4)
+ playerOne.Hit(1, 8) // (B,8)
+ playerOne.Hit(2, 7) // (C,7)
+ playerOne.Hit(2, 8) // (C,8)
+ playerOne.Hit(3, 8) // (D,8)
+ playerOne.Hit(4, 8) // (E,8)
+ playerOne.Hit(9, 2) // (I,2)
+
+ // Display both primary boards in stdout
+ fmt.Println("Player One (vue de ses propres pièces):", playerOne.DisplayPrimary())
+ fmt.Println("Player Two (vue de ses propres pièces):", playerTwo.DisplayPrimary())
+ fmt.Println("Player One (vue des pièces de son ennemi):", playerOne.DisplayTarget())
+ fmt.Println("Player Two (vue des pièces de son ennemi):", playerTwo.DisplayTarget())
+
+ /* TVIEW UI SETUP:
+ ┌------------------------------------------------------┐
+ |dashboard |
+ |┌----------------------------------------------------┐|
+ ||headerBox ||
+ |└----------------------------------------------------┘|
+ |┌----------------------------------------------------┐|
+ ||bottomFlex ||
+ ||┌-------------------┐ ┌----------------------------┐||
+ ||| infoFlex | | playFlex |||
+ |||┌-----------------┐| |┌--------------------------┐|||
+ |||| keybindingsBox || || targetFlex ||||
+ |||| || ||┌------------------------┐||||
+ |||| || ||| targetBox | gainsBox |||||
+ |||| || ||| | |||||
+ |||| || ||| | |||||
+ |||| || ||└------------------------┘||||
+ |||| || |└--------------------------┘|||
+ |||| || |┌--------------------------┐|||
+ |||└-----------------┘| || primaryFlex ||||
+ |||┌-----------------┐| ||┌------------------------┐||||
+ |||| logBox || ||| primaryBox | lossesBox |||||
+ |||| || ||| | |||||
+ |||| || ||| | |||||
+ |||| || ||└------------------------┘||||
+ |||└-----------------┘| |└--------------------------┘|||
+ ||└-------------------┘ └----------------------------┘||
+ |└----------------------------------------------------┘|
+ └------------------------------------------------------┘
+
+ DASHBOARD:
+ flex structure containing everything.
+ - Direction: rows
+
+ BOTTOMFLEX:
+ flex structure containing everything but the headerBox
+ - Direction: columns
+
+ INFOFLEX:
+ flex structure containing general info related boxes:
+ - keybindingsBox
+ - logBox
+ - Direction: rows
+
+ PLAYFLEX:
+ flex structure containing everything related to playing the game:
+ - targetFlex
+ - primaryFlex
+ - Direction: rows
+
+ TARGETFLEX:
+ flex structure containing everything the player knows about his target:
+ - targetBox
+ - gainsBox
+ - Direction: columns
+
+ PRIMARYFLEX:
+ flex structure containing everything the player knows about himself:
+ - primaryBox
+ - lossesBox
+ - Direction: columns
+
+ HEADERBOX:
+ box which displays in it's title the current player.
+
+ KEYBINDINGSBOX:
+ simple box containing a list of all the keybindings one can use.
+
+ LOGBOX:
+ box which shows a log of the past moves each player made.
+
+ TARGETBOX:
+ box containing the board where the player attacks his opponent
+ this box is focused by default and is of type table as it can be navigated.
+
+ GAINSBOX:
+ box showing the names of all the ennemies boats which are sunk.
+ each sunk boat has a small display of the boat going with it.
+
+ PRIMARYBOX:
+ box containing the board showing the players boat layout.
+
+ LOSSESBOX:
+ box showing the names of all the players boats which are sunk.
+ each sunk boat has a small display of the boat going with it.
+
+ COMMANDBOX:
+ box containing an input field which is to be used as a command prompt.
+ coordinates can be inputed directly and typing quit will exit the game.
+
+ */
+
+ // Initializing the application
+ app := tv.NewApplication()
+
+ headerBox := tv.NewBox().SetTitle(playerOne.name).
+ SetBorder(true)
+
+ keybindingsBox := tv.NewBox().
+ SetTitle("Keybindings:").
+ SetBorder(true)
+ // TODO: Add text/documentation to box
+
+ logBox := tv.NewBox().
+ SetTitle("Log:").
+ SetBorder(true)
+
+ targetBox := tv.NewTable()
+ RedrawTarget(&playerOne, targetBox)
+ targetBox.SetFixed(1, 1).
+ SetSelectable(true, true).
+ SetDoneFunc(func(key tc.Key) {
+ if key == tc.KeyEscape {
+ // TODO: change this for a dopdown prompt "Are you sure? (Y/N)"
+ app.Stop()
+ }
+ }).
+ SetBorder(true).
+ SetTitle("The enemy:")
+
+ primaryBox := tv.NewTable().
+ SetBorder(true).
+ SetTitle("You:")
+
+ gainsBox := tv.NewList().
+ SetBorder(true).
+ SetTitle("Gains:")
+
+ lossesBox := tv.NewList().
+ SetBorder(true).
+ SetTitle("Losses:")
+
+ commandBox := tv.NewInputField().
+ SetBorder(true).
+ SetTitle("Command:")
+
+ targetFlex := tv.NewFlex().SetDirection(tv.FlexColumn).
+ AddItem(targetBox, 26, 0, true).
+ AddItem(gainsBox, 26, 0, false)
+
+ primaryFlex := tv.NewFlex().SetDirection(tv.FlexColumn).
+ AddItem(primaryBox, 26, 0, false).
+ AddItem(lossesBox, 26, 0, false)
+
+ playFlex := tv.NewFlex().SetDirection(tv.FlexRow).
+ AddItem(targetFlex, 13, 0, true).
+ AddItem(primaryFlex, 13, 0, false).
+ AddItem(commandBox, 0, 1, false)
+
+ infoFlex := tv.NewFlex().SetDirection(tv.FlexRow).
+ AddItem(keybindingsBox, 0, 3, false).
+ AddItem(logBox, 0, 1, false)
+
+ bottomFlex := tv.NewFlex().SetDirection(tv.FlexColumn).
+ AddItem(infoFlex, 0, 1, false).
+ AddItem(playFlex, 52, 0, true)
+
+ dashboard := tv.NewFlex().SetDirection(tv.FlexRow).
+ AddItem(headerBox, 2, 1, false).
+ AddItem(bottomFlex, 0, 1, true)
+
+ if err := app.SetRoot(dashboard, true).Run(); err != nil {
+ panic(err)
+ }
+ }
+}
+
+func RedrawTarget(plyr *player, table *tv.Table) {
+ // generating slice string for the table:
+ // We initialize a slice containing all the cells
+ // The first row will be the label of the columns
+ boardData := strings.Split(" /A/B/C/D/E/F/G/H/I/J", "/")
+ // For every row (r)
+ for r := 0; r < 10; r++ {
+ // Each row starts with the row label/number
+ // A space makes the table centered by indenting it...
+ str := " " + strconv.Itoa(r)
+ boardData = append(boardData, str)
+ for c := 0; c < 10; c++ {
+ // First thing: is the coordinate hit or not?
+ switch plyr.target[r][c][0] {
+ // The coordinate is NOT hit:
+ case 0:
+ // Add the `~` symbol
+ boardData = append(boardData, boatchars[1][0])
+ // If the coordinate is NOT hit:
+ default:
+ // Is the coordinate water?
+ switch plyr.prey.primary[r][c][0] {
+ // It IS water:
+ case 6:
+ // Add the `◌` symbol
+ boardData = append(boardData, boatchars[0][0])
+ // It's NOT water:
+ default:
+ // Is the ID of the boat at that coordinate marked as a gain?
+ switch plyr.gains[plyr.prey.primary[r][c][0]] {
+ // It IS marked as a gain:
+ case true:
+ // Show the boat as it's meant to be:
+ // (hit boatchars: with `position` marked in the opponents primary)
+ boardData = append(boardData, boatchars[0][plyr.prey.primary[r][c][1]])
+ default:
+ // Nope, you're getting a censored tile: `▣`
+ boardData = append(boardData, misteryHit)
+ }
+ }
+ }
+ }
+ }
+ table.Clear()
+ for r := 0; r < 11; r++ {
+ for c := 0; c < 11; c++ {
+ color, selectable := tc.ColorDarkBlue, true
+ if r < 1 || c < 1 {
+ color, selectable = tc.ColorPurple, false
+ }
+ if boardData[r*11+c] != `~` {
+ selectable = false
+ }
+ table.SetCell(r, c,
+ tv.NewTableCell(boardData[r*11+c]).
+ SetTextColor(color).
+ SetAlign(tv.AlignCenter).
+ SetSelectable(selectable))
}
}
}