概述
首先让我们通过一个例子来了解什么是井字游戏
-
有一个n*n的棋盘,棋盘上的每个区块只有在空的情况下才可以打叉或画圆 。
-
Max 两个人同时进行游戏,每个人轮流玩。
-
第一个玩家在棋盘上的任何区块上标记一个十字,第二个玩家在棋盘上的任何区块上标记一个圆。
-
游戏的目的是让整行、整列或整个对角线上都有十字或圆圈的符号。
-
双方都要努力阻止对方实现这一目标。谁先达到这个目标,谁就赢了。
-
如果棋盘上的所有方块都已满了,而且没有任何一方未能在整行、整列或对角线上标记其符号,则游戏结果为平局。
-
在一个玩家赢得游戏后,不允许再有任何动作。
让我们通过一个例子来理解这个游戏。假设一个3*3的网格。点('.')代表一个空块
Player 1 Move with Symbol * at Position X:1 Y:1
...
.*.
...
Player 2 Move with Symbol o at Position X:1 Y:2
...
.*o
...
Player 1 Move with Symbol * at Position X:2 Y:0
...
.*o
*..
Player 2 Move with Symbol o at Position X:0 Y:2
..o
.*o
*..
Player 1 Move with Symbol * at Position X:2 Y:2
..o
.*o
*.*
Player 2 Move with Symbol o at Position X:0 Y:0
o.o
.*o
*.*
Player 1 Move with Symbol * at Position X:2 Y:1
o.o
.*o
***
First Player Win
o.o
.*o
***
在上面的游戏中,第一个玩家赢了,因为第三行都被符号十字--**'*'**占据了。
程序
以下是完整的工作代码
symbol.go
package main
type Symbol uint8
const (
Cross Symbol = iota
Circle
Dot
)
iPlayer.go
package main
type iPlayer interface {
getSymbol() Symbol
getNextMove() (int, int, error)
getID() int
}
humanPlayer.go
package main
import "fmt"
var (
MovesPlayer1 = [4][2]int{{1, 1}, {2, 0}, {2, 2}, {2, 1}}
MovesPlayer2 = [4][2]int{{1, 2}, {0, 2}, {0, 0}, {0, 0}}
)
type humanPlayer struct {
symbol Symbol
index int
id int
}
func (h *humanPlayer) getSymbol() Symbol {
return h.symbol
}
func (h *humanPlayer) getNextMove() (int, int, error) {
if h.symbol == Cross {
h.index = h.index + 1
return MovesPlayer1[h.index-1][0], MovesPlayer1[h.index-1][1], nil
} else if h.symbol == Circle {
h.index = h.index + 1
return MovesPlayer2[h.index-1][0], MovesPlayer2[h.index-1][1], nil
}
return 0, 0, fmt.Errorf("Invalid Symbol")
}
func (h *humanPlayer) getID() int {
return h.id
}
computerPlayer.go
package main
type computerPlayer struct {
symbol Symbol
id int
}
func (c *computerPlayer) getSymbol() Symbol {
return c.symbol
}
func (c *computerPlayer) getNextMove() (int, int, error) {
//To be implemented
return 0, 0, nil
}
func (c *computerPlayer) getID() int {
return c.id
}
gameStatus.go
package main
type GameStatus uint8
const (
GameInProgress GameStatus = iota
GameDraw
FirstPlayerWin
SecondPlayerWin
)
棋盘.go
package main
import "fmt"
type board struct {
square [][]Symbol
dimension int
}
func (b *board) printBoard() {
for i := 0; i < b.dimension; i++ {
for j := 0; j < b.dimension; j++ {
if b.square[i][j] == Dot {
fmt.Print(".")
} else if b.square[i][j] == Cross {
fmt.Print("*")
} else {
fmt.Print("o")
}
}
fmt.Println("")
}
}
func (b *board) markSymbol(i, j int, symbol Symbol) (bool, Symbol, error) {
if i > b.dimension || j > b.dimension {
return false, Dot, fmt.Errorf("index input is greater than dimension")
}
if b.square[i][j] != Dot {
return false, Dot, fmt.Errorf("input square already marked")
}
if symbol != Cross && symbol != Circle {
return false, Dot, fmt.Errorf("incorrect Symbol")
}
b.square[i][j] = symbol
win := b.checkWin(i, j, symbol)
return win, symbol, nil
}
func (b *board) checkWin(i, j int, symbol Symbol) bool {
//Check Row
rowMatch := true
for k := 0; k < b.dimension; k++ {
if b.square[i][k] != symbol {
rowMatch = false
}
}
if rowMatch {
return rowMatch
}
//Check Row
columnMatch := true
for k := 0; k < b.dimension; k++ {
if b.square[k][j] != symbol {
columnMatch = false
}
}
if columnMatch {
return columnMatch
}
//Check diagonal
diagonalMatch := false
if i == j {
diagonalMatch = true
for k := 0; k < b.dimension; k++ {
if b.square[k][k] != symbol {
diagonalMatch = false
}
}
}
return diagonalMatch
}
game.go
package main
import "fmt"
type game struct {
board *board
firstPlayer iPlayer
secondPlayer iPlayer
firstPlayerTurn bool
moveIndex int
gameStatus GameStatus
}
func initGame(b *board, p1, p2 iPlayer) *game {
game := &game{
board: b,
firstPlayer: p1,
secondPlayer: p2,
firstPlayerTurn: true,
gameStatus: GameInProgress,
}
return game
}
func (g *game) play() error {
var win bool
var symbol Symbol
for {
if g.firstPlayerTurn {
x, y, err := g.firstPlayer.getNextMove()
if err != nil {
return err
}
win, symbol, err = g.board.markSymbol(x, y, g.firstPlayer.getSymbol())
if err != nil {
return err
}
g.firstPlayerTurn = false
g.printMove(g.firstPlayer, x, y)
} else {
x, y, err := g.secondPlayer.getNextMove()
if err != nil {
return err
}
win, symbol, err = g.board.markSymbol(x, y, g.secondPlayer.getSymbol())
if err != nil {
return err
}
g.firstPlayerTurn = true
g.printMove(g.secondPlayer, x, y)
}
g.moveIndex = g.moveIndex + 1
g.setGameStatus(win, symbol)
if g.gameStatus != GameInProgress {
break
}
}
return nil
}
func (g *game) setGameStatus(win bool, symbol Symbol) {
if win {
if g.firstPlayer.getSymbol() == symbol {
g.gameStatus = FirstPlayerWin
return
} else if g.secondPlayer.getSymbol() == symbol {
g.gameStatus = SecondPlayerWin
return
}
}
if g.moveIndex == g.board.dimension*g.board.dimension {
g.gameStatus = GameDraw
return
}
g.gameStatus = GameInProgress
}
func (g *game) printMove(player iPlayer, x, y int) {
symbolString := ""
symbol := player.getSymbol()
if symbol == Cross {
symbolString = "*"
} else if symbol == Circle {
symbolString = "o"
}
fmt.Printf("Player %d Move with Symbol %s at Position X:%d Y:%d\n", player.getID(), symbolString, x, y)
g.board.printBoard()
fmt.Println("")
}
func (g *game) printResult() {
switch g.gameStatus {
case GameInProgress:
fmt.Println("Game in Between")
case GameDraw:
fmt.Println("Game Drawn")
case FirstPlayerWin:
fmt.Println("First Player Win")
case SecondPlayerWin:
fmt.Println("Second Player Win")
default:
fmt.Println("Invalid Game Status")
}
g.board.printBoard()
}
输出
在上面的程序中,我们在humanPlayer.go文件中固定了两个棋手的动作。下面是基于这些动作的输出。
Player 1 Move with Symbol * at Position X:1 Y:1
...
.*.
...
Player 2 Move with Symbol o at Position X:1 Y:2
...
.*o
...
Player 1 Move with Symbol * at Position X:2 Y:0
...
.*o
*..
Player 2 Move with Symbol o at Position X:0 Y:2
..o
.*o
*..
Player 1 Move with Symbol * at Position X:2 Y:2
..o
.*o
*.*
Player 2 Move with Symbol o at Position X:0 Y:0
o.o
.*o
*.*
Player 1 Move with Symbol * at Position X:2 Y:1
o.o
.*o
***
First Player Win
o.o
.*o
***
完整的工作代码。
以下是一个文件中的全部工作代码
main.go
package main
import "fmt"
type Symbol uint8
const (
Cross Symbol = iota
Circle
Dot
)
type GameStatus uint8
const (
GameInProgress GameStatus = iota
GameDraw
FirstPlayerWin
SecondPlayerWin
)
type iPlayer interface {
getSymbol() Symbol
getNextMove() (int, int, error)
getID() int
}
var (
MovesPlayer1 = [4][2]int{{1, 1}, {2, 0}, {2, 2}, {2, 1}}
MovesPlayer2 = [4][2]int{{1, 2}, {0, 2}, {0, 0}, {0, 0}}
)
type humanPlayer struct {
symbol Symbol
index int
id int
}
func (h *humanPlayer) getSymbol() Symbol {
return h.symbol
}
func (h *humanPlayer) getNextMove() (int, int, error) {
if h.symbol == Cross {
h.index = h.index + 1
return MovesPlayer1[h.index-1][0], MovesPlayer1[h.index-1][1], nil
} else if h.symbol == Circle {
h.index = h.index + 1
return MovesPlayer2[h.index-1][0], MovesPlayer2[h.index-1][1], nil
}
return 0, 0, fmt.Errorf("Invalid Symbol")
}
func (h *humanPlayer) getID() int {
return h.id
}
type computerPlayer struct {
symbol Symbol
id int
}
func (c *computerPlayer) getSymbol() Symbol {
return c.symbol
}
func (c *computerPlayer) getNextMove() (int, int, error) {
//To be implemented
return 0, 0, nil
}
func (c *computerPlayer) getID() int {
return c.id
}
type board struct {
square [][]Symbol
dimension int
}
func (b *board) printBoard() {
for i := 0; i < b.dimension; i++ {
for j := 0; j < b.dimension; j++ {
if b.square[i][j] == Dot {
fmt.Print(".")
} else if b.square[i][j] == Cross {
fmt.Print("*")
} else {
fmt.Print("o")
}
}
fmt.Println("")
}
}
func (b *board) markSymbol(i, j int, symbol Symbol) (bool, Symbol, error) {
if i > b.dimension || j > b.dimension {
return false, Dot, fmt.Errorf("index input is greater than dimension")
}
if b.square[i][j] != Dot {
return false, Dot, fmt.Errorf("input square already marked")
}
if symbol != Cross && symbol != Circle {
return false, Dot, fmt.Errorf("incorrect Symbol")
}
b.square[i][j] = symbol
win := b.checkWin(i, j, symbol)
return win, symbol, nil
}
func (b *board) checkWin(i, j int, symbol Symbol) bool {
//Check Row
rowMatch := true
for k := 0; k < b.dimension; k++ {
if b.square[i][k] != symbol {
rowMatch = false
}
}
if rowMatch {
return rowMatch
}
//Check Row
columnMatch := true
for k := 0; k < b.dimension; k++ {
if b.square[k][j] != symbol {
columnMatch = false
}
}
if columnMatch {
return columnMatch
}
//Check diagonal
diagonalMatch := false
if i == j {
diagonalMatch = true
for k := 0; k < b.dimension; k++ {
if b.square[k][k] != symbol {
diagonalMatch = false
}
}
}
return diagonalMatch
}
type game struct {
board *board
firstPlayer iPlayer
secondPlayer iPlayer
firstPlayerTurn bool
moveIndex int
gameStatus GameStatus
}
func initGame(b *board, p1, p2 iPlayer) *game {
game := &game{
board: b,
firstPlayer: p1,
secondPlayer: p2,
firstPlayerTurn: true,
gameStatus: GameInProgress,
}
return game
}
func (g *game) play() error {
var win bool
var symbol Symbol
for {
if g.firstPlayerTurn {
x, y, err := g.firstPlayer.getNextMove()
if err != nil {
return err
}
win, symbol, err = g.board.markSymbol(x, y, g.firstPlayer.getSymbol())
if err != nil {
return err
}
g.firstPlayerTurn = false
g.printMove(g.firstPlayer, x, y)
} else {
x, y, err := g.secondPlayer.getNextMove()
if err != nil {
return err
}
win, symbol, err = g.board.markSymbol(x, y, g.secondPlayer.getSymbol())
if err != nil {
return err
}
g.firstPlayerTurn = true
g.printMove(g.secondPlayer, x, y)
}
g.moveIndex = g.moveIndex + 1
g.setGameStatus(win, symbol)
if g.gameStatus != GameInProgress {
break
}
}
return nil
}
func (g *game) setGameStatus(win bool, symbol Symbol) {
if win {
if g.firstPlayer.getSymbol() == symbol {
g.gameStatus = FirstPlayerWin
return
} else if g.secondPlayer.getSymbol() == symbol {
g.gameStatus = SecondPlayerWin
return
}
}
if g.moveIndex == g.board.dimension*g.board.dimension {
g.gameStatus = GameDraw
return
}
g.gameStatus = GameInProgress
}
func (g *game) printMove(player iPlayer, x, y int) {
symbolString := ""
symbol := player.getSymbol()
if symbol == Cross {
symbolString = "*"
} else if symbol == Circle {
symbolString = "o"
}
fmt.Printf("Player %d Move with Symbol %s at Position X:%d Y:%d\n", player.getID(), symbolString, x, y)
g.board.printBoard()
fmt.Println("")
}
func (g *game) printResult() {
switch g.gameStatus {
case GameInProgress:
fmt.Println("Game in Between")
case GameDraw:
fmt.Println("Game Drawn")
case FirstPlayerWin:
fmt.Println("First Player Win")
case SecondPlayerWin:
fmt.Println("Second Player Win")
default:
fmt.Println("Invalid Game Status")
}
g.board.printBoard()
}
func main() {
board := &board{
square: [][]Symbol{{Dot, Dot, Dot}, {Dot, Dot, Dot}, {Dot, Dot, Dot}},
dimension: 3,
}
player1 := &humanPlayer{
symbol: Cross,
id: 1,
}
player2 := &humanPlayer{
symbol: Circle,
id: 2,
}
game := initGame(board, player1, player2)
game.play()
game.printResult()
}
输出
在上面的程序中,我们在humanPlayer类中固定了两个玩家的动作。下面是基于这些动作的输出。
Player 1 Move with Symbol * at Position X:1 Y:1
...
.*.
...
Player 2 Move with Symbol o at Position X:1 Y:2
...
.*o
...
Player 1 Move with Symbol * at Position X:2 Y:0
...
.*o
*..
Player 2 Move with Symbol o at Position X:0 Y:2
..o
.*o
*..
Player 1 Move with Symbol * at Position X:2 Y:2
..o
.*o
*.*
Player 2 Move with Symbol o at Position X:0 Y:0
o.o
.*o
*.*
Player 1 Move with Symbol * at Position X:2 Y:1
o.o
.*o
***
First Player Win
o.o
.*o
***