mirror of
https://github.com/gabehf/go-battleship.git
synced 2026-03-17 19:26:33 -07:00
Initial Commit
This commit is contained in:
commit
0045790ec4
10 changed files with 1013 additions and 0 deletions
304
board/board.go
Normal file
304
board/board.go
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
package board
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/ghfarrell/go-battleship/cprint"
|
||||
)
|
||||
|
||||
type Point struct {
|
||||
Row rune
|
||||
Col int
|
||||
}
|
||||
|
||||
type Ship struct {
|
||||
Name string
|
||||
Length int
|
||||
Coords []Point
|
||||
}
|
||||
|
||||
/*
|
||||
Enemy board's state is - for an empty space, O for a missed
|
||||
shot, and X for a hit
|
||||
Friendly board's state is - for an empty space, O for a boat
|
||||
and X for a boat space that has been hit
|
||||
*/
|
||||
type Board struct {
|
||||
Board map[Point]byte
|
||||
Ships []Ship
|
||||
}
|
||||
|
||||
func (b *Board) Initialize() {
|
||||
rand.Seed(int64(time.Now().Nanosecond()))
|
||||
b.Board = make(map[Point]byte, 100)
|
||||
for c := 'a'; c <= 'j'; c++ {
|
||||
for i := 1; i <= 10; i++ {
|
||||
b.Board[CoordToPoint(c, i)] = '-'
|
||||
}
|
||||
}
|
||||
b.Ships = make([]Ship, 5)
|
||||
b.Ships[0].Name = "Carrier"
|
||||
b.Ships[1].Name = "Battleship"
|
||||
b.Ships[2].Name = "Cruiser"
|
||||
b.Ships[3].Name = "Submarine"
|
||||
b.Ships[4].Name = "Destroyer"
|
||||
b.Ships[0].Length = 5
|
||||
b.Ships[1].Length = 4
|
||||
b.Ships[2].Length = 3
|
||||
b.Ships[3].Length = 3
|
||||
b.Ships[4].Length = 2
|
||||
for _, s := range b.Ships {
|
||||
s.Coords = make([]Point, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
helper func turns 0 to false and anything else to true
|
||||
for ai placing ship logic
|
||||
*/
|
||||
func itob(i int) bool {
|
||||
if i == 0 {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
helper func turns "v" or "h" to true and false respectively
|
||||
for the ship placement logic
|
||||
*/
|
||||
func HVToBool(c rune) bool {
|
||||
if c == 'h' || c == 'H' {
|
||||
return false
|
||||
} else if c == 'v' || c == 'V' {
|
||||
return true
|
||||
} else {
|
||||
cprint.Printf["Red"]("Invalid orientation! Defaulting to vertical\n")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func RandPoint() Point {
|
||||
rows := []rune{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
|
||||
r := rows[rand.Intn(10)]
|
||||
c := rand.Intn(10) + 1
|
||||
return Point{r, c}
|
||||
}
|
||||
|
||||
func InputPoint() (Point, error) {
|
||||
var row rune
|
||||
var col int
|
||||
_, err := fmt.Scanf("%c%d\n", &row, &col)
|
||||
if err != nil {
|
||||
cprint.Printf["Red"]("Bad input. Enter coordinate as [row][col] i.e. b5\n")
|
||||
}
|
||||
if unicode.IsUpper(row) {
|
||||
row = unicode.ToLower(row)
|
||||
}
|
||||
if col < 1 || col > 10 || row < 'a' || row > 'j' {
|
||||
return Point{}, fmt.Errorf("Invalid coordinate")
|
||||
} else {
|
||||
return Point{row, col}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (p Point) IsValid() bool {
|
||||
if p.Row > 'j' || p.Row < 'a' || p.Col > 10 || p.Col < 1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (p Point) NudgeRight(i int) Point {
|
||||
return Point{p.Row, p.Col + i}
|
||||
}
|
||||
func (p Point) NudgeUp(i int) Point {
|
||||
return Point{p.Row - rune(i), p.Col}
|
||||
}
|
||||
func (p Point) NudgeDown(i int) Point {
|
||||
return Point{p.Row + rune(i), p.Col}
|
||||
}
|
||||
func (p Point) NudgeLeft(i int) Point {
|
||||
return Point{p.Row, p.Col - i}
|
||||
}
|
||||
|
||||
func CoordToPoint(b rune, i int) Point {
|
||||
if unicode.IsUpper(b) {
|
||||
b = unicode.ToLower(b)
|
||||
}
|
||||
return Point{b, i}
|
||||
}
|
||||
|
||||
func (b Board) print() {
|
||||
fmt.Println(" 1 2 3 4 5 6 7 8 9 10")
|
||||
for c := 'a'; c <= 'j'; c++ {
|
||||
fmt.Printf("%c", c)
|
||||
for i := 1; i <= 10; i++ {
|
||||
cprint.Printf["White"](" " + string(b.Board[CoordToPoint(c, i)]))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func (b Board) GameOver() bool {
|
||||
for c := 'a'; c <= 'j'; c++ {
|
||||
for i := 1; i <= 10; i++ {
|
||||
if b.Board[CoordToPoint(c, i)] == 'O' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (b Board) shipExistsAt(coords []Point) bool {
|
||||
for _, p := range coords {
|
||||
if b.Board[p] != '-' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ok so for some stupid reason that i cant figure out, even if i pass the ship
|
||||
// parameter here by reference the new coordinate array that the ship contains
|
||||
// WILL NOT be updated when the function returns, and i literally have no idea why
|
||||
// so thats why it returns the Ship with the new array as well as the error
|
||||
func (b *Board) PlaceShip(s Ship, p Point, vertical bool) (Ship, error) {
|
||||
switch vertical {
|
||||
case true:
|
||||
if p.Row+rune(s.Length) > 'j' {
|
||||
break
|
||||
}
|
||||
for i := 0; i < s.Length; i++ {
|
||||
s.Coords = append(s.Coords, Point{p.Row + rune(i), p.Col})
|
||||
}
|
||||
if !b.shipExistsAt(s.Coords) {
|
||||
for _, c := range s.Coords {
|
||||
b.Board[c] = 'O'
|
||||
}
|
||||
} else {
|
||||
s.Coords = make([]Point, 0)
|
||||
return s, fmt.Errorf("Ship already exists at that point")
|
||||
}
|
||||
return s, nil
|
||||
case false:
|
||||
if p.Col+s.Length > 10 {
|
||||
break
|
||||
}
|
||||
for i := 0; i < s.Length; i++ {
|
||||
s.Coords = append(s.Coords, Point{p.Row, p.Col + i})
|
||||
}
|
||||
if !b.shipExistsAt(s.Coords) {
|
||||
for _, c := range s.Coords {
|
||||
b.Board[c] = 'O'
|
||||
}
|
||||
} else {
|
||||
s.Coords = make([]Point, 0)
|
||||
return s, fmt.Errorf("Ship already exists at that point")
|
||||
}
|
||||
return s, nil
|
||||
default:
|
||||
|
||||
s.Coords = make([]Point, 0)
|
||||
return s, fmt.Errorf("Coordinate out of bounds")
|
||||
}
|
||||
|
||||
s.Coords = make([]Point, 0)
|
||||
return s, fmt.Errorf("Coordinate out of bounds.")
|
||||
}
|
||||
|
||||
func (b *Board) PlaceShips() {
|
||||
stdin := bufio.NewReader(os.Stdin)
|
||||
var vert rune
|
||||
var vertBool bool
|
||||
for i := range b.Ships {
|
||||
s := b.Ships[i]
|
||||
for {
|
||||
b.print()
|
||||
cprint.Printf["Yellow"]("Where do you want your %s? (Length %d): ", s.Name, s.Length)
|
||||
p, err := InputPoint()
|
||||
if err != nil {
|
||||
cprint.Printf["Red"]("Error: " + err.Error() + "\n")
|
||||
}
|
||||
cprint.Printf["White"]("Placed Horizontally or Vertically (h or v): ")
|
||||
_, err = fmt.Scanf("%c", &vert)
|
||||
stdin.ReadString('\n')
|
||||
if err != nil {
|
||||
stdin.ReadString('\n')
|
||||
cprint.Printf["Red"]("Bad input! Enter 'v', 'h', 'vertical', or 'horizontal'\n")
|
||||
}
|
||||
vertBool = HVToBool(vert)
|
||||
if b.Ships[i], err = b.PlaceShip(s, p, vertBool); err == nil {
|
||||
s := b.Ships[i]
|
||||
ClearScreen()
|
||||
if vertBool {
|
||||
cprint.Printf["Cyan"]("%s placed vertically at coordinate [%c%d]!\n", s.Name, p.Row, p.Col)
|
||||
} else {
|
||||
cprint.Printf["Cyan"]("%s placed horizontally at coordinate [%c%d]!\n", s.Name, p.Row, p.Col)
|
||||
}
|
||||
|
||||
break
|
||||
} else {
|
||||
cprint.Printf["Red"]("Error: " + err.Error() + "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Board) AutoPlaceShips() {
|
||||
var err error
|
||||
for i := range b.Ships {
|
||||
for {
|
||||
s := b.Ships[i]
|
||||
p := RandPoint()
|
||||
v := rand.Intn(2)
|
||||
b.Ships[i], err = b.PlaceShip(s, p, itob(v))
|
||||
s = b.Ships[i]
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b Board) Sunk(s Ship) bool {
|
||||
for _, p := range s.Coords {
|
||||
if b.Board[p] == 'O' { //there are still spots yet to be hit on the ship
|
||||
time.Sleep(5)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
returns whether or not the shot hit, and if the ship has been sunk,
|
||||
it returns the sunken ship. otherwise, it returns an empty Ship
|
||||
*/
|
||||
func (b Board) CheckForHit(c Point) (hit bool, ship Ship) {
|
||||
if b.Board[c] == 'O' {
|
||||
hit = true
|
||||
for _, s := range b.Ships {
|
||||
for _, p := range s.Coords {
|
||||
if p == c {
|
||||
ship = s
|
||||
break
|
||||
}
|
||||
}
|
||||
if ship.Name == s.Name {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hit = false
|
||||
ship = Ship{}
|
||||
}
|
||||
return hit, ship
|
||||
}
|
||||
33
board/clearscreen.go
Normal file
33
board/clearscreen.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package board
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ghfarrell/go-battleship/cprint"
|
||||
)
|
||||
|
||||
// clearscreen and printlogo functions have to be in this package so that I can use
|
||||
// them in the ship placement loop :/
|
||||
func PrintLogo() {
|
||||
cprint.Printf["Magenta"](" /$$$$$$$ /$$$$$$ /$$$$$$$$/$$$$$$$$/$$ /$$$$$$$$ \n")
|
||||
cprint.Printf["Magenta"]("| $$__ $$ /$$__ $$|__ $$__/__ $$__/ $$ | $$_____/ \n")
|
||||
cprint.Printf["Magenta"]("| $$ \\ $$| $$ \\ $$ | $$ | $$ | $$ | $$ \n")
|
||||
cprint.Printf["Magenta"]("| $$$$$$$ | $$$$$$$$ | $$ | $$ | $$ | $$$$$ \n")
|
||||
cprint.Printf["Magenta"]("| $$__ $$| $$__ $$ | $$ | $$ | $$ | $$__/ \n")
|
||||
cprint.Printf["Magenta"]("| $$ \\ $$| $$ | $$ | $$ | $$ | $$ | $$ \n")
|
||||
cprint.Printf["Magenta"]("| $$$$$$$/| $$ | $$ | $$ | $$ | $$$$$$$$| $$$$$$$$| \n")
|
||||
cprint.Printf["Magenta"]("|_______/ |__/ |__/ |__/ |__/ |________/|________/ \n")
|
||||
cprint.Printf["Magenta"](" /$$$$$$ /$$ /$$ /$$$$$$ /$$$$$$$\n")
|
||||
cprint.Printf["Magenta"](" /$$__ $$| $$ | $$|_ $$_/| $$__ $$\n")
|
||||
cprint.Printf["Magenta"]("| $$ \\__/| $$ | $$ | $$ | $$ \\ $$\n")
|
||||
cprint.Printf["Magenta"]("| $$$$$$ | $$$$$$$$ | $$ | $$$$$$$/\n")
|
||||
cprint.Printf["Magenta"](" \\____ $$| $$__ $$ | $$ | $$____/ \n")
|
||||
cprint.Printf["Magenta"](" /$$ \\ $$| $$ | $$ | $$ | $$ \n")
|
||||
cprint.Printf["Magenta"](" $$$$$$/| $$ | $$ /$$$$$$| $$ \n")
|
||||
cprint.Printf["Magenta"](" \\______/ |__/ |__/|______/|__/ \n")
|
||||
cprint.Printf["Red"]("Press ctrl + c at any time to quit.\n\n")
|
||||
}
|
||||
func ClearScreen() {
|
||||
fmt.Print("\033[H\033[2J")
|
||||
PrintLogo()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue