Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Rican7/fba6c1ee2d9fa8bd1354 to your computer and use it in GitHub Desktop.
Save Rican7/fba6c1ee2d9fa8bd1354 to your computer and use it in GitHub Desktop.
This is an "Object Oriented" rewrite of my already rewritten Go(lang) battleship game. I wanted to see how the OOP design worked in Go. Its different. Original, more functional approach: https://gist.github.com/Rican7/fac67bb68672be09678e
package main
import (
"bytes"
"fmt"
"math/rand"
"strings"
"time"
)
/**
* Types
*/
// A location on the board
type Point struct {
X, Y int
}
// A mark on the board, represented by a location with a representation
type Mark struct {
Point
checked bool
representation string
}
// The game board
type Board struct {
spots [][]Mark
}
// The game
type Game struct {
board Board
turns int
enemy Point
}
/**
* Type Functions
*/
// Create a new Mark
func NewMark(location Point, checked bool) *Mark {
mark := Mark{location, false, ""}
mark.SetChecked(checked)
return &mark
}
// Get the string representation of a mark
func (m Mark) Representation() string {
return m.representation
}
// Set the string representation of a mark
func (m *Mark) SetRepresentation(rep string) {
m.representation = strings.ToUpper(rep[:len(rep)-1])
}
// Check if the mark has been "checked" as used
func (m Mark) Checked() bool {
return m.checked
}
// Set the mark's "checked" status
func (m *Mark) SetChecked(checked bool) {
m.checked = checked
if checked {
m.representation = "X"
} else {
m.representation = "O"
}
}
// Get a mark as a string
func (m Mark) String() string {
return m.representation
}
// Create a new board
func NewBoard(rows, columns int) *Board {
// Set a default size
if rows <= 0 || columns <= 0 {
return nil
}
board := Board{make([][]Mark, rows)}
// Fill the board with default marks
for i := 0; i < rows; i++ {
board.spots[i] = make([]Mark, columns)
for idx := range board.spots[i] {
board.spots[i][idx] = *NewMark(Point{(i + 1), (idx + 1)}, false)
}
}
return &board
}
// Get the number of rows on the board
func (b Board) GetRowCount() int {
return len(b.spots)
}
// Get the number of columns on the board
func (b Board) GetColumnCount() int {
return len(b.spots[0])
}
// Get a mark on the board by a Point location
func (b Board) GetMarkByLocation(location Point) *Mark {
return &b.spots[location.Y][location.X]
}
// Check if a given location is within the bounds of the board
func (b Board) IsLocationWithinBounds(location Point) bool {
if (location.X < 0 || location.X > (b.GetColumnCount()-1)) ||
(location.Y < 0 || location.Y > (b.GetRowCount()-1)) {
return false
}
return true
}
// Generate a random location that fits on the board
func (b Board) GenerateRandomLocation() *Point {
maxRowIndex := (b.GetRowCount() - 1)
maxColumnIndex := (b.GetColumnCount() - 1)
r := rand.New(rand.NewSource(time.Now().UnixNano()))
return &Point{X: r.Intn(maxColumnIndex), Y: r.Intn(maxRowIndex)}
}
// Get a board as a string
func (b Board) String() string {
var buffer bytes.Buffer
for i := 0; i < b.GetRowCount(); i++ {
for j := 0; j < b.GetColumnCount(); j++ {
mark := b.GetMarkByLocation(Point{j, i})
buffer.WriteString(mark.String())
buffer.WriteString(" ")
}
buffer.WriteString("\r\n")
}
return buffer.String()
}
// Create a new Game
// Pass a "nil" enemy to generate one randomly
func NewGame(board Board, turns int, enemy *Point) *Game {
// Generate a default enemy if nil passed
if nil == enemy {
enemy = board.GenerateRandomLocation()
}
return &Game{board: board, turns: turns, enemy: *enemy}
}
// Get the game's board
func (g Game) Board() Board {
return g.board
}
// Get the number of turns in the game
func (g Game) Turns() int {
return g.turns
}
// Get the enemy location
func (g Game) GetEnemyLocation() Point {
return g.enemy
}
// Check if a guessed location is the location of the enemy
func (g Game) checkGuess(guess Point) bool {
if g.GetEnemyLocation() == guess {
fmt.Println("Congratulations! You sunk my battleship!")
return true
} else {
if !g.Board().IsLocationWithinBounds(guess) {
fmt.Println("Oops, that's not even in the ocean.")
} else if g.Board().GetMarkByLocation(guess).Checked() {
fmt.Println("You guessed that one already.")
} else {
fmt.Println("You missed my battleship!")
g.Board().GetMarkByLocation(guess).SetChecked(true)
}
}
fmt.Println()
return false
}
// Play the game!
func (g Game) Play() bool {
won := false
for turn := 0; turn < g.Turns(); turn++ {
fmt.Println(g.Board())
guess := GetGuessPoint()
won = g.checkGuess(*guess)
if won {
break
}
fmt.Println("Finished turn", (turn + 1))
}
return won
}
// Get the user's guess
func GetGuessPoint() *Point {
var x, y int
// Get our guess
fmt.Println("Guess the row: ")
fmt.Scanf("%d", &y)
fmt.Println("Guess the column: ")
fmt.Scanf("%d", &x)
fmt.Println()
return &Point{X: x, Y: y}
}
/**
* Program Entrance
*/
func main() {
// Intro
fmt.Println("Let's play Battleship!")
fmt.Println()
// Construct our game
board := NewBoard(5, 5)
game := NewGame(*board, 5, nil)
// Play!!
won := game.Play()
if won {
fmt.Println("Woot!")
} else {
fmt.Println("Sorry... Game over")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment