diff options
-rw-r--r-- | aesthetics.go | 73 | ||||
-rw-r--r-- | backend.go | 42 | ||||
-rw-r--r-- | main.go | 351 |
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 = `▣` @@ -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 @@ -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)) } } } |