Files
2019-10-30 19:41:13 +03:00

161 lines
2.9 KiB
Go

// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// For more tutorials : https://learngoprogramming.com
// In-person training : https://www.linkedin.com/in/inancgumus/
// Follow me on twitter: https://twitter.com/inancgumus
package main
import (
"strings"
)
type state int
const (
maxTurn = 9
wrongPosition = -2
statePlaying state = iota
stateWon
stateTie
stateAlreadyPlayed
stateWrongPosition
)
type game struct {
board [][]string
turn int
player string
skin // embed the skin
logger // embed the logger
}
func newGame(s skin, l logger) *game {
return &game{
player: s.mark1,
board: [][]string{
{s.empty, s.empty, s.empty},
{s.empty, s.empty, s.empty},
{s.empty, s.empty, s.empty},
},
skin: s,
logger: l,
}
}
func (g *game) play(pos int) state {
if st := g.move(pos); st != statePlaying {
return st
}
g.turn++ // increment the turn
// first check the winner then check the tie
// or the last mover won't win
switch {
case g.won():
return stateWon
case g.turn == maxTurn:
return stateTie
}
g.changePlayer()
return statePlaying
}
func (g *game) move(pos int) state {
row, col := position(pos)
if row+col == wrongPosition {
return stateWrongPosition
}
if g.board[row][col] != g.empty {
return stateAlreadyPlayed
}
// put the player's mark on the board
g.board[row][col] = g.player
return statePlaying
}
// we can detect the winning state just by comparing the strings
// because, the game board is a bunch of strings
func (g *game) won() (won bool) {
for _, m := range [2]string{g.mark1, g.mark2} {
b, mmm := g.board, strings.Repeat(m, 3)
won = /* horizontals */
strings.Join(b[0], "") == mmm ||
strings.Join(b[1], "") == mmm ||
strings.Join(b[2], "") == mmm ||
/* verticals */
b[0][0]+b[1][0]+b[2][0] == mmm ||
b[0][1]+b[1][1]+b[2][1] == mmm ||
b[0][2]+b[1][2]+b[2][2] == mmm ||
/* diagonals */
b[0][0]+b[1][1]+b[2][2] == mmm ||
b[0][2]+b[1][1]+b[2][0] == mmm
if won {
return true
}
}
return false
}
// this method should have a pointer receiver
// because, it changes the game value
func (g *game) changePlayer() {
if g.player == g.mark1 {
g.player = g.mark2
} else {
g.player = g.mark1
}
}
func (g *game) print() {
g.Println()
g.Println(g.header)
for i, line := range g.board {
g.Print(g.separator)
for _, m := range line {
g.Printf("%2s%s", m, g.separator)
}
if i+1 != len(g.board) {
g.Printf("\n%s\n", g.middle)
}
}
g.Printf("\n%s\n", g.footer)
}
// this function doesn't depend on the game state
// so, make it a function instead of a method
func position(pos int) (row, col int) {
switch {
case pos >= 1 && pos <= 3:
row = 0
case pos >= 4 && pos <= 6:
row = 1
case pos >= 7 && pos <= 9:
row = 2
default:
return -1, -1
}
return row, pos - row*3 - 1
}