5 Commits

Author SHA1 Message Date
6b9c29d01d Version 3.0.0 2019-11-20 20:31:41 +01:00
64f8780e96 Make "makeMove" return void.
When doing moves the result must no longer be discarded.
2019-11-20 20:29:01 +01:00
6c983e0795 Check if the game has already ended before making a move. 2019-11-20 19:43:43 +01:00
bf2b59f720 Bump Version 2019-11-20 19:06:51 +01:00
be953892dc Refactor constuctors and Mark enum 2019-11-20 19:05:22 +01:00
4 changed files with 174 additions and 183 deletions

View File

@@ -1,6 +1,6 @@
# Package # Package
version = "1.0.0" version = "3.0.0"
author = "luxick" author = "luxick"
description = "Game library for meta tic tac toe" description = "Game library for meta tic tac toe"
license = "GPL-2.0" license = "GPL-2.0"

View File

@@ -1,25 +1,25 @@
import sets, strformat, options, sequtils import sets, options, sequtils
type type
## A game board is a simple 2 dimensional array. ## A game board is a 2 dimensional array.
## Markers of any type can be placed inside the cells ## Markers of any type can be placed inside the cells
Board*[T] = array[3, array[3, T]] Board*[T] = array[3, array[3, T]]
## A Position in a board ## A Position on a board
Coordinate* = tuple[x: int, y:int] Coordinate* = tuple[x: int, y:int]
Player* = ref object Player* = ref object
mark*: Mark mark*: Mark
name*: string name*: string
Mark* = enum Mark* {.pure.} = enum
mPlayer1, ## The mark of the fist player Player1, ## The mark of the fist player
mPlayer2, ## The mark of the second player Player2, ## The mark of the second player
mFree, ## This mark signals that a cell is empty Free, ## This mark signals that a cell is empty
mDraw ## Special mark to indicate the game has ended in a draw Draw ## Special mark to indicate the game has ended in a draw
GameState* = ref object GameState* = ref object
## Contains all state for a pint in the game ## Contains all state for a game
board*: Board[Board[Mark]] board*: Board[Board[Mark]]
players*: array[2, Player] players*: array[2, Player]
currentPlayer*: Player currentPlayer*: Player
@@ -28,20 +28,11 @@ type
turn*: int turn*: int
IllegalMoveError* = object of Exception IllegalMoveError* = object of Exception
IIlegalStateError* = object of Exception
proc `$`*(player: Player): string = ###################################################
$player.name # Constructors
###################################################
proc `$`*(player: ref Player): string =
$player.name
proc `$`*(game: GameState): string =
&"""
Game is in turn {$game.turn}
Players are: '{$game.players[0]}' and '{$game.players[1]}'
Current player: '{$game.currentPlayer}'
Game state is: {$game.result}
"""
proc newBoard[T](initial: T): Board[T] = proc newBoard[T](initial: T): Board[T] =
## Create a new game board filled with the initial value ## Create a new game board filled with the initial value
@@ -51,23 +42,15 @@ proc newBoard[T](initial: T): Board[T] =
[initial, initial, initial] [initial, initial, initial]
] ]
proc newMetaBoard[T](val: T): Board[Board[T]] = proc newGameState*(player1: var Player, player2: var Player): GameState =
## Create the meta board, composed out of 9 normal boards. ## Initializes a new game state with the passed players
[ player1.mark = Mark.Player1
[newBoard(val), newBoard(val), newBoard(val)], player2.mark = Mark.Player2
[newBoard(val), newBoard(val), newBoard(val)],
[newBoard(val), newBoard(val), newBoard(val)]
]
proc newGame*(player1: var Player, player2: var Player): GameState =
## Create a new game board
player1.mark = mPlayer1
player2.mark = mPlayer2
GameState( GameState(
players: [player1, player2], players: [player1, player2],
currentPlayer: player1, currentPlayer: player1,
result: mFree, result: Mark.Free,
board: newMetaBoard(mFree)) board: newBoard(newBoard(Mark.Free))) # The meta board is a board of boards
################################################### ###################################################
# Board Checking # Board Checking
@@ -75,28 +58,42 @@ proc newGame*(player1: var Player, player2: var Player): GameState =
proc selectResult(states: HashSet[Mark]): Mark = proc selectResult(states: HashSet[Mark]): Mark =
## Analyse a set of results and select the correct one ## Analyse a set of results and select the correct one
if states.contains(mPlayer1) and if states.contains(Mark.Player1) and
states.contains(mPlayer2): states.contains(Mark.Player2):
raise newException(Exception, "Both players cannot win at the same time") raise newException(IIlegalStateError, "Both players cannot win at the same time")
if states.contains(mPlayer1): if states.contains(Mark.Player1):
return mPlayer1 return Mark.Player1
if states.contains(mPlayer2): if states.contains(Mark.Player2):
return mPlayer2 return Mark.Player2
if states.contains(mFree): if states.contains(Mark.Free):
return mFree return Mark.Free
return mDraw return Mark.Draw
proc transposed(board: Board): Board =
## Flip a board along the axis from top left to bottom right
result = newBoard(Mark.Free);
for x in 0 .. 2:
for y in 0 .. 2:
result[x][y] = board[y][x]
proc flipped(board: Board): Board =
## Flip a board along the center horizonal axis
result = newBoard(Mark.Free)
for x in 0 .. 2:
result[x] = board[2-x]
proc checkRow(row: array[3, Mark]): Mark = proc checkRow(row: array[3, Mark]): Mark =
## Evaluates a single row of a board
var tokens = toHashSet(row) var tokens = toHashSet(row)
# A player has won # A player has won
if tokens.len == 1 and not tokens.contains(mFree): if tokens.len == 1 and not tokens.contains(Mark.Free):
return tokens.pop() return tokens.pop()
# The row is full # The row is full
if tokens.len == 2 and not tokens.contains(mFree): if tokens.len == 2 and not tokens.contains(Mark.Free):
return mDraw return Mark.Draw
# There are still cells free in this row # There are still cells free in this row
return mFree return Mark.Free
proc checkRows(board: Board[Mark]): Mark = proc checkRows(board: Board[Mark]): Mark =
## Iterate over all rows of this board and return the result. ## Iterate over all rows of this board and return the result.
@@ -109,40 +106,31 @@ proc checkRows(board: Board[Mark]): Mark =
proc checkDiagonals(board: Board[Mark]): Mark = proc checkDiagonals(board: Board[Mark]): Mark =
var states: HashSet[Mark] var states: HashSet[Mark]
states.init() states.init()
var topToBottom: array[3, Mark] # Construct a temporary row from the diagonal indices, so checkRow cann be used
var bottomToTop: array[3, Mark] var row: array[3, Mark]
for x in 0 .. 2: # Flip the board so the other diagonal is checked too.
topToBottom[x] = board[x][x] for b in [board, board.flipped]:
bottomToTop[x] = board[2-x][x] for x in 0 .. 2:
states.incl(checkRow(topToBottom)) row[x] = b[x][x]
states.incl(checkRow(bottomToTop)) states.incl(checkRow(row))
return states.selectResult() return states.selectResult()
proc checkBoard*(board: Board[Mark]): Mark = proc checkBoard*(board: Board[Mark]): Mark =
## Perform a check on a single sub board to see its result ## Perform a check on a single sub board to see its result
# Create a seconded, transposed board.
# This way 'checkRows' can be used to check the columns
var transposed = newBoard(mFree);
for x in 0 .. 2:
for y in 0 .. 2:
transposed[x][y] = board[y][x]
var states: HashSet[Mark] var states: HashSet[Mark]
states.init() states.init()
states.incl(checkDiagonals(board)) states.incl(checkDiagonals(board))
for b in [board, transposed]: # Create a seconded, transposed board.
# This way 'checkRows' can be used to check the columns
for b in [board, board.transposed]:
states.incl(checkRows(b)) states.incl(checkRows(b))
return selectResult(states) return selectResult(states)
proc checkBoard*(board: Board[Board[Mark]]): Mark = proc checkBoard*(board: Board[Board[Mark]]): Mark =
## Perform a check on a metaboard to see the overall game result ## Perform a check on a metaboard to see the overall game result
# This temporary board will hold the intermediate results from each sub board # This temporary board will hold the intermediate results from each sub board
var subResults = newBoard(mFree) var subResults = newBoard(Mark.Free)
var states: HashSet[Mark]
states.init()
for x in 0 .. 2: for x in 0 .. 2:
for y in 0 .. 2: for y in 0 .. 2:
subResults[x][y] = checkBoard(board[x][y]) subResults[x][y] = checkBoard(board[x][y])
@@ -152,16 +140,18 @@ proc checkBoard*(board: Board[Board[Mark]]): Mark =
# Process Player Moves # Process Player Moves
################################################### ###################################################
proc makeMove*(state: GameState, cell: Coordinate): GameState = proc makeMove*(state: GameState, cell: Coordinate): void =
if state.result != Mark.Free:
raise newException(IllegalMoveError, "The game has already ended")
if cell.x > 2 or cell.y > 2: if cell.x > 2 or cell.y > 2:
raise newException(IndexError, "Move target not in bounds of the board") raise newException(IndexError, "Move target not in bounds of the board")
if state.currentBoard.isNone: if state.currentBoard.isNone:
raise newException(ValueError, "No board value passed") raise newException(IllegalMoveError, "No board value passed")
let board = state.currentBoard.get() let board = state.currentBoard.get()
var currBoard = state.board[board.x][board.y] var currBoard = state.board[board.x][board.y]
if currBoard[cell.x][cell.y] != mFree: if currBoard[cell.x][cell.y] != Mark.Free:
raise newException(IllegalMoveError, "Chosen cell is not free") raise newException(IllegalMoveError, "Chosen cell is not free")
state.board[board.x][board.y][cell.x][cell.y] = state.currentPlayer.mark state.board[board.x][board.y][cell.x][cell.y] = state.currentPlayer.mark
@@ -169,24 +159,25 @@ proc makeMove*(state: GameState, cell: Coordinate): GameState =
state.result = checkBoard(state.board) state.result = checkBoard(state.board)
# Exit early. The game has ended. # Exit early. The game has ended.
if state.result != mFree: if state.result != Mark.Free:
return state state.currentBoard = none(Coordinate)
return
let nextBoard = checkBoard(state.board[cell.x][cell.y]) let nextBoard = checkBoard(state.board[cell.x][cell.y])
if nextBoard == mFree: if nextBoard == Mark.Free:
state.currentBoard = cell.some() state.currentBoard = cell.some()
else: else:
state.currentBoard = none(Coordinate) state.currentBoard = none(Coordinate)
state.currentPlayer = state.players.filter(proc (p: Player): bool = state.currentPlayer = state.players.filter(proc (p: Player): bool =
p.mark != state.currentPlayer.mark)[0] p.mark != state.currentPlayer.mark)[0]
return state
proc makeMove*(state: GameState, cell: Coordinate, boardChoice: Coordinate): GameState = proc makeMove*(state: GameState, cell: Coordinate, boardChoice: Coordinate): void =
if checkBoard(state.board[boardChoice.x][boardChoice.y]) != mFree: if state.result != Mark.Free:
raise newException(IllegalMoveError, "The game has already ended")
if checkBoard(state.board[boardChoice.x][boardChoice.y]) != Mark.Free:
raise newException(IllegalMoveError, "Player must choose an open board to play in") raise newException(IllegalMoveError, "Player must choose an open board to play in")
if state.currentBoard.isSome: if state.currentBoard.isSome:
raise newException(IllegalMoveError, "Player does not have free choice for board") raise newException(IllegalMoveError, "Player does not have free choice for board")
state.currentBoard = boardChoice.some() state.currentBoard = boardChoice.some()
return state.makeMove(cell) state.makeMove(cell)

View File

@@ -14,146 +14,146 @@ suite "Test the board result checker":
var var
player1 = Player(name: "Adam") player1 = Player(name: "Adam")
player2 = Player(name: "Eve") player2 = Player(name: "Eve")
game: GameState = newGame(player1, player2) game: GameState = newGameState(player1, player2)
test "winning row": test "winning row":
game.board[0][0] = [ game.board[0][0] = [
[mFree, mFree, mFree], [Mark.Free, Mark.Free, Mark.Free],
[mPlayer1, mPlayer1, mPlayer1], [Mark.Player1, Mark.Player1, Mark.Player1],
[mFree, mFree, mFree] [Mark.Free, Mark.Free, Mark.Free]
] ]
check checkBoard(game.board[0][0]) == mPlayer1 check checkBoard(game.board[0][0]) == Mark.Player1
test "winning column": test "winning column":
game.board[0][0] = [ game.board[0][0] = [
[mPlayer2, mFree, mPlayer1], [Mark.Player2, Mark.Free, Mark.Player1],
[mPlayer2, mPlayer1, mFree], [Mark.Player2, Mark.Player1, Mark.Free],
[mPlayer2, mPlayer1, mFree] [Mark.Player2, Mark.Player1, Mark.Free]
] ]
check checkBoard(game.board[0][0]) == mPlayer2 check checkBoard(game.board[0][0]) == Mark.Player2
test "winning diagonals": test "winning diagonals":
game.board[0][0] = [ game.board[0][0] = [
[mFree, mFree, mPlayer2], [Mark.Free, Mark.Free, Mark.Player2],
[mFree, mPlayer2, mFree], [Mark.Free, Mark.Player2, Mark.Free],
[mPlayer2, mFree, mFree] [Mark.Player2, Mark.Free, Mark.Free]
] ]
check(checkBoard(game.board[0][0]) == mPlayer2) check(checkBoard(game.board[0][0]) == Mark.Player2)
game.board[0][0] = [ game.board[0][0] = [
[mPlayer1, mPlayer2, mPlayer2], [Mark.Player1, Mark.Player2, Mark.Player2],
[mPlayer2, mPlayer1, mPlayer1], [Mark.Player2, Mark.Player1, Mark.Player1],
[mPlayer2, mFree, mPlayer1] [Mark.Player2, Mark.Free, Mark.Player1]
] ]
check(checkBoard(game.board[0][0]) == mPlayer1) check(checkBoard(game.board[0][0]) == Mark.Player1)
test "board is a draw": test "board is a draw":
game.board[0][0] = [ game.board[0][0] = [
[mPlayer1, mPlayer2, mPlayer1], [Mark.Player1, Mark.Player2, Mark.Player1],
[mPlayer2, mPlayer1, mPlayer1], [Mark.Player2, Mark.Player1, Mark.Player1],
[mPlayer2, mPlayer1, mPlayer2] [Mark.Player2, Mark.Player1, Mark.Player2]
] ]
check checkBoard(game.board[0][0]) == mDraw check checkBoard(game.board[0][0]) == Mark.Draw
test "board is open": test "board is open":
game.board[0][0] = [ game.board[0][0] = [
[mPlayer1, mPlayer2, mFree], [Mark.Player1, Mark.Player2, Mark.Free],
[mPlayer1, mFree, mPlayer1], [Mark.Player1, Mark.Free, Mark.Player1],
[mFree, mPlayer1, mPlayer2] [Mark.Free, Mark.Player1, Mark.Player2]
] ]
check checkBoard(game.board[0][0]) == mFree check checkBoard(game.board[0][0]) == Mark.Free
test "free inital metaboard": test "free inital metaboard":
check checkBoard(game.board) == mFree check checkBoard(game.board) == Mark.Free
test "winning metaboard row": test "winning metaboard row":
game.board[1][0] = [ game.board[1][0] = [
[mPlayer1, mPlayer2, mFree], [Mark.Player1, Mark.Player2, Mark.Free],
[mPlayer1, mPlayer1, mPlayer1], [Mark.Player1, Mark.Player1, Mark.Player1],
[mFree, mPlayer1, mPlayer2] [Mark.Free, Mark.Player1, Mark.Player2]
] ]
game.board[1][1] = [ game.board[1][1] = [
[mFree, mFree, mPlayer1], [Mark.Free, Mark.Free, Mark.Player1],
[mFree, mPlayer1, mFree], [Mark.Free, Mark.Player1, Mark.Free],
[mPlayer1, mFree, mFree] [Mark.Player1, Mark.Free, Mark.Free]
] ]
game.board[1][2] = [ game.board[1][2] = [
[mFree, mFree, mPlayer1], [Mark.Free, Mark.Free, Mark.Player1],
[mFree, mFree, mPlayer1], [Mark.Free, Mark.Free, Mark.Player1],
[mFree, mFree, mPlayer1] [Mark.Free, Mark.Free, Mark.Player1]
] ]
check checkBoard(game.board) == mPlayer1 check checkBoard(game.board) == Mark.Player1
test "winning metaboard column": test "winning metaboard column":
game.board[0][1] = [ game.board[0][1] = [
[mPlayer1, mPlayer2, mFree], [Mark.Player1, Mark.Player2, Mark.Free],
[mPlayer1, mPlayer1, mPlayer1], [Mark.Player1, Mark.Player1, Mark.Player1],
[mFree, mPlayer1, mPlayer2] [Mark.Free, Mark.Player1, Mark.Player2]
] ]
game.board[1][1] = [ game.board[1][1] = [
[mFree, mFree, mFree], [Mark.Free, Mark.Free, Mark.Free],
[mPlayer1, mPlayer1, mPlayer1], [Mark.Player1, Mark.Player1, Mark.Player1],
[mFree, mFree, mFree] [Mark.Free, Mark.Free, Mark.Free]
] ]
game.board[2][1] = [ game.board[2][1] = [
[mPlayer1, mFree, mFree], [Mark.Player1, Mark.Free, Mark.Free],
[mPlayer1, mFree, mFree], [Mark.Player1, Mark.Free, Mark.Free],
[mPlayer1, mFree, mFree] [Mark.Player1, Mark.Free, Mark.Free]
] ]
check checkBoard(game.board) == mPlayer1 check checkBoard(game.board) == Mark.Player1
test "winning metaboard diagonal": test "winning metaboard diagonal":
game.board[0][0] = [ game.board[0][0] = [
[mPlayer2, mPlayer2, mFree], [Mark.Player2, Mark.Player2, Mark.Free],
[mPlayer2, mPlayer2, mPlayer2], [Mark.Player2, Mark.Player2, Mark.Player2],
[mFree, mPlayer2, mPlayer2] [Mark.Free, Mark.Player2, Mark.Player2]
] ]
game.board[1][1] = [ game.board[1][1] = [
[mFree, mFree, mFree], [Mark.Free, Mark.Free, Mark.Free],
[mPlayer2, mPlayer2, mPlayer2], [Mark.Player2, Mark.Player2, Mark.Player2],
[mFree, mFree, mFree] [Mark.Free, Mark.Free, Mark.Free]
] ]
game.board[2][2] = [ game.board[2][2] = [
[mPlayer2, mFree, mFree], [Mark.Player2, Mark.Free, Mark.Free],
[mPlayer2, mFree, mFree], [Mark.Player2, Mark.Free, Mark.Free],
[mPlayer2, mFree, mFree] [Mark.Player2, Mark.Free, Mark.Free]
] ]
check checkBoard(game.board) == mPlayer2 check checkBoard(game.board) == Mark.Player2
test "winning metaboard with some boards in draw": test "winning metaboard with some boards in draw":
let winner = [ let winner = [
[mFree, mFree, mFree], [Mark.Free, Mark.Free, Mark.Free],
[mPlayer2, mPlayer2, mPlayer2], [Mark.Player2, Mark.Player2, Mark.Player2],
[mFree, mFree, mFree] [Mark.Free, Mark.Free, Mark.Free]
] ]
let drawn = [ let drawn = [
[mPlayer2, mPlayer2, mPlayer1], [Mark.Player2, Mark.Player2, Mark.Player1],
[mPlayer1, mPlayer1, mPlayer2], [Mark.Player1, Mark.Player1, Mark.Player2],
[mPlayer2, mPlayer1, mPlayer2] [Mark.Player2, Mark.Player1, Mark.Player2]
] ]
game.board[0][0] = winner game.board[0][0] = winner
game.board[1][1] = winner game.board[1][1] = winner
game.board[2][2] = winner game.board[2][2] = winner
game.board[1][0] = drawn game.board[1][0] = drawn
game.board[2][0] = drawn game.board[2][0] = drawn
check checkBoard(game.board) == mPlayer2 check checkBoard(game.board) == Mark.Player2
test "metaboard is drawn": test "metaboard is drawn":
let drawn = [ let drawn = [
[mPlayer2, mPlayer2, mPlayer1], [Mark.Player2, Mark.Player2, Mark.Player1],
[mPlayer1, mPlayer1, mPlayer2], [Mark.Player1, Mark.Player1, Mark.Player2],
[mPlayer2, mPlayer1, mPlayer2] [Mark.Player2, Mark.Player1, Mark.Player2]
] ]
for x in 0 .. 2: for x in 0 .. 2:
for y in 0 .. 2: for y in 0 .. 2:
game.board[x][y] = drawn game.board[x][y] = drawn
check checkBoard(game.board) == mDraw check checkBoard(game.board) == Mark.Draw
test "illegal situation: both players win board": test "illegal situation: both players win board":
game.board[1][1] = [ game.board[1][1] = [
[mPlayer1, mPlayer1, mPlayer1], [Mark.Player1, Mark.Player1, Mark.Player1],
[mPlayer2, mPlayer2, mPlayer2], [Mark.Player2, Mark.Player2, Mark.Player2],
[mFree, mFree, mFree] [Mark.Free, Mark.Free, Mark.Free]
] ]
expect(Exception): expect(Exception):
discard checkBoard(game.board) discard checkBoard(game.board)

View File

@@ -7,79 +7,79 @@ suite "Test the move procedures":
var var
player1 = Player(name: "Adam") player1 = Player(name: "Adam")
player2 = Player(name: "Eve") player2 = Player(name: "Eve")
game: GameState = newGame(player1, player2) game: GameState = newGameState(player1, player2)
test "Inital state": test "Inital state":
check game.currentPlayer == player1 check game.currentPlayer == player1
check game.players == [player1, player2] check game.players == [player1, player2]
check game.turn == 0 check game.turn == 0
check game.currentBoard.isNone check game.currentBoard.isNone
check game.result == mFree check game.result == Mark.Free
test "First move": test "First move":
game = game.makeMove((1, 1), (1, 1)) game.makeMove((1, 1), (1, 1))
check game.currentPlayer == player2 check game.currentPlayer == player2
check game.turn == 1 check game.turn == 1
check game.currentBoard == (1, 1).some() check game.currentBoard == (1, 1).some()
check game.result == mFree check game.result == Mark.Free
test "Second move": test "Second move":
game = game.makeMove((1, 1), (1, 1)) game.makeMove((1, 1), (1, 1))
game = game.makeMove((0, 0)) game.makeMove((0, 0))
check game.currentPlayer == player1 check game.currentPlayer == player1
check game.turn == 2 check game.turn == 2
check game.currentBoard == (0, 0).some() check game.currentBoard == (0, 0).some()
check game.result == mFree check game.result == Mark.Free
test "Move on wrong board": test "Move on wrong board":
game = game.makeMove((1, 1), (1, 1)) game.makeMove((1, 1), (1, 1))
expect(IllegalMoveError): expect(IllegalMoveError):
game = game.makeMove((0, 0), (2, 2)) game.makeMove((0, 0), (2, 2))
test "Move on a taken cell": test "Move on a taken cell":
game = game.makeMove((1, 1), (1, 1)) game.makeMove((1, 1), (1, 1))
expect(IllegalMoveError): expect(IllegalMoveError):
game = game.makeMove((1, 1)) game.makeMove((1, 1))
test "Move out of bounds": test "Move out of bounds":
expect(IndexError): expect(IndexError):
game = game.makeMove((3, 0), (0, 0)) game.makeMove((3, 0), (0, 0))
test "Player 1 wins the game": test "Player 1 wins the game":
let winner = [ let winner = [
[mPlayer1, mFree, mFree], [Mark.Player1, Mark.Free, Mark.Free],
[mPlayer1, mFree, mFree], [Mark.Player1, Mark.Free, Mark.Free],
[mPlayer1, mFree, mFree] [Mark.Player1, Mark.Free, Mark.Free]
] ]
game.board[0][0] = winner game.board[0][0] = winner
game.board[1][1] = winner game.board[1][1] = winner
game.board[2][2] = [ game.board[2][2] = [
[mPlayer1, mFree, mFree], [Mark.Player1, Mark.Free, Mark.Free],
[mFree, mFree, mFree], [Mark.Free, Mark.Free, Mark.Free],
[mPlayer1, mFree, mFree] [Mark.Player1, Mark.Free, Mark.Free]
] ]
check checkBoard(game.board) == mFree check checkBoard(game.board) == Mark.Free
game = game.makeMove((1, 0), (2, 2)) game.makeMove((1, 0), (2, 2))
check game.result == mPlayer1 check game.result == Mark.Player1
check game.currentPlayer == player1 check game.currentPlayer == player1
test "The game ends in a draw": test "The game ends in a draw":
let drawn = [ let drawn = [
[mPlayer2, mPlayer2, mPlayer1], [Mark.Player2, Mark.Player2, Mark.Player1],
[mPlayer1, mPlayer1, mPlayer2], [Mark.Player1, Mark.Player1, Mark.Player2],
[mPlayer2, mPlayer1, mPlayer2] [Mark.Player2, Mark.Player1, Mark.Player2]
] ]
for x in 0 .. 2: for x in 0 .. 2:
for y in 0 .. 2: for y in 0 .. 2:
game.board[x][y] = drawn game.board[x][y] = drawn
game.board[2][2] = [ game.board[2][2] = [
[mPlayer2, mPlayer2, mPlayer1], [Mark.Player2, Mark.Player2, Mark.Player1],
[mPlayer1, mPlayer1, mPlayer2], [Mark.Player1, Mark.Player1, Mark.Player2],
[mPlayer2, mFree, mPlayer2] [Mark.Player2, Mark.Free, Mark.Player2]
] ]
check checkBoard(game.board) == mFree check checkBoard(game.board) == Mark.Free
game = game.makeMove((2, 1), (2, 2)) game.makeMove((2, 1), (2, 2))
check game.result == mDraw check game.result == Mark.Draw
check game.currentPlayer == player1 check game.currentPlayer == player1