Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b9c29d01d | |||
| 64f8780e96 | |||
| 6c983e0795 | |||
| bf2b59f720 | |||
| be953892dc |
@@ -1,6 +1,6 @@
|
||||
# Package
|
||||
|
||||
version = "1.0.0"
|
||||
version = "3.0.0"
|
||||
author = "luxick"
|
||||
description = "Game library for meta tic tac toe"
|
||||
license = "GPL-2.0"
|
||||
|
||||
153
src/libmttt.nim
153
src/libmttt.nim
@@ -1,25 +1,25 @@
|
||||
import sets, strformat, options, sequtils
|
||||
import sets, options, sequtils
|
||||
|
||||
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
|
||||
Board*[T] = array[3, array[3, T]]
|
||||
|
||||
## A Position in a board
|
||||
## A Position on a board
|
||||
Coordinate* = tuple[x: int, y:int]
|
||||
|
||||
Player* = ref object
|
||||
mark*: Mark
|
||||
name*: string
|
||||
|
||||
Mark* = enum
|
||||
mPlayer1, ## The mark of the fist player
|
||||
mPlayer2, ## The mark of the second player
|
||||
mFree, ## This mark signals that a cell is empty
|
||||
mDraw ## Special mark to indicate the game has ended in a draw
|
||||
Mark* {.pure.} = enum
|
||||
Player1, ## The mark of the fist player
|
||||
Player2, ## The mark of the second player
|
||||
Free, ## This mark signals that a cell is empty
|
||||
Draw ## Special mark to indicate the game has ended in a draw
|
||||
|
||||
GameState* = ref object
|
||||
## Contains all state for a pint in the game
|
||||
## Contains all state for a game
|
||||
board*: Board[Board[Mark]]
|
||||
players*: array[2, Player]
|
||||
currentPlayer*: Player
|
||||
@@ -28,20 +28,11 @@ type
|
||||
turn*: int
|
||||
|
||||
IllegalMoveError* = object of Exception
|
||||
IIlegalStateError* = object of Exception
|
||||
|
||||
proc `$`*(player: Player): string =
|
||||
$player.name
|
||||
|
||||
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}
|
||||
"""
|
||||
###################################################
|
||||
# Constructors
|
||||
###################################################
|
||||
|
||||
proc newBoard[T](initial: T): Board[T] =
|
||||
## Create a new game board filled with the initial value
|
||||
@@ -51,23 +42,15 @@ proc newBoard[T](initial: T): Board[T] =
|
||||
[initial, initial, initial]
|
||||
]
|
||||
|
||||
proc newMetaBoard[T](val: T): Board[Board[T]] =
|
||||
## Create the meta board, composed out of 9 normal boards.
|
||||
[
|
||||
[newBoard(val), newBoard(val), newBoard(val)],
|
||||
[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
|
||||
proc newGameState*(player1: var Player, player2: var Player): GameState =
|
||||
## Initializes a new game state with the passed players
|
||||
player1.mark = Mark.Player1
|
||||
player2.mark = Mark.Player2
|
||||
GameState(
|
||||
players: [player1, player2],
|
||||
currentPlayer: player1,
|
||||
result: mFree,
|
||||
board: newMetaBoard(mFree))
|
||||
result: Mark.Free,
|
||||
board: newBoard(newBoard(Mark.Free))) # The meta board is a board of boards
|
||||
|
||||
###################################################
|
||||
# Board Checking
|
||||
@@ -75,28 +58,42 @@ proc newGame*(player1: var Player, player2: var Player): GameState =
|
||||
|
||||
proc selectResult(states: HashSet[Mark]): Mark =
|
||||
## Analyse a set of results and select the correct one
|
||||
if states.contains(mPlayer1) and
|
||||
states.contains(mPlayer2):
|
||||
raise newException(Exception, "Both players cannot win at the same time")
|
||||
if states.contains(Mark.Player1) and
|
||||
states.contains(Mark.Player2):
|
||||
raise newException(IIlegalStateError, "Both players cannot win at the same time")
|
||||
|
||||
if states.contains(mPlayer1):
|
||||
return mPlayer1
|
||||
if states.contains(mPlayer2):
|
||||
return mPlayer2
|
||||
if states.contains(mFree):
|
||||
return mFree
|
||||
return mDraw
|
||||
if states.contains(Mark.Player1):
|
||||
return Mark.Player1
|
||||
if states.contains(Mark.Player2):
|
||||
return Mark.Player2
|
||||
if states.contains(Mark.Free):
|
||||
return Mark.Free
|
||||
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 =
|
||||
## Evaluates a single row of a board
|
||||
var tokens = toHashSet(row)
|
||||
# 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()
|
||||
# The row is full
|
||||
if tokens.len == 2 and not tokens.contains(mFree):
|
||||
return mDraw
|
||||
if tokens.len == 2 and not tokens.contains(Mark.Free):
|
||||
return Mark.Draw
|
||||
# There are still cells free in this row
|
||||
return mFree
|
||||
return Mark.Free
|
||||
|
||||
proc checkRows(board: Board[Mark]): Mark =
|
||||
## 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 =
|
||||
var states: HashSet[Mark]
|
||||
states.init()
|
||||
var topToBottom: array[3, Mark]
|
||||
var bottomToTop: array[3, Mark]
|
||||
for x in 0 .. 2:
|
||||
topToBottom[x] = board[x][x]
|
||||
bottomToTop[x] = board[2-x][x]
|
||||
states.incl(checkRow(topToBottom))
|
||||
states.incl(checkRow(bottomToTop))
|
||||
# Construct a temporary row from the diagonal indices, so checkRow cann be used
|
||||
var row: array[3, Mark]
|
||||
# Flip the board so the other diagonal is checked too.
|
||||
for b in [board, board.flipped]:
|
||||
for x in 0 .. 2:
|
||||
row[x] = b[x][x]
|
||||
states.incl(checkRow(row))
|
||||
return states.selectResult()
|
||||
|
||||
proc checkBoard*(board: Board[Mark]): Mark =
|
||||
## 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]
|
||||
states.init()
|
||||
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))
|
||||
|
||||
return selectResult(states)
|
||||
|
||||
proc checkBoard*(board: Board[Board[Mark]]): Mark =
|
||||
## Perform a check on a metaboard to see the overall game result
|
||||
|
||||
# This temporary board will hold the intermediate results from each sub board
|
||||
var subResults = newBoard(mFree)
|
||||
var states: HashSet[Mark]
|
||||
states.init()
|
||||
var subResults = newBoard(Mark.Free)
|
||||
for x in 0 .. 2:
|
||||
for y in 0 .. 2:
|
||||
subResults[x][y] = checkBoard(board[x][y])
|
||||
@@ -152,16 +140,18 @@ proc checkBoard*(board: Board[Board[Mark]]): Mark =
|
||||
# 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:
|
||||
raise newException(IndexError, "Move target not in bounds of the board")
|
||||
if state.currentBoard.isNone:
|
||||
raise newException(ValueError, "No board value passed")
|
||||
raise newException(IllegalMoveError, "No board value passed")
|
||||
|
||||
let board = state.currentBoard.get()
|
||||
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")
|
||||
|
||||
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)
|
||||
|
||||
# Exit early. The game has ended.
|
||||
if state.result != mFree:
|
||||
return state
|
||||
if state.result != Mark.Free:
|
||||
state.currentBoard = none(Coordinate)
|
||||
return
|
||||
|
||||
let nextBoard = checkBoard(state.board[cell.x][cell.y])
|
||||
if nextBoard == mFree:
|
||||
if nextBoard == Mark.Free:
|
||||
state.currentBoard = cell.some()
|
||||
else:
|
||||
state.currentBoard = none(Coordinate)
|
||||
|
||||
state.currentPlayer = state.players.filter(proc (p: Player): bool =
|
||||
p.mark != state.currentPlayer.mark)[0]
|
||||
|
||||
return state
|
||||
|
||||
proc makeMove*(state: GameState, cell: Coordinate, boardChoice: Coordinate): GameState =
|
||||
if checkBoard(state.board[boardChoice.x][boardChoice.y]) != mFree:
|
||||
proc makeMove*(state: GameState, cell: Coordinate, boardChoice: Coordinate): void =
|
||||
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")
|
||||
if state.currentBoard.isSome:
|
||||
raise newException(IllegalMoveError, "Player does not have free choice for board")
|
||||
state.currentBoard = boardChoice.some()
|
||||
return state.makeMove(cell)
|
||||
state.makeMove(cell)
|
||||
@@ -14,146 +14,146 @@ suite "Test the board result checker":
|
||||
var
|
||||
player1 = Player(name: "Adam")
|
||||
player2 = Player(name: "Eve")
|
||||
game: GameState = newGame(player1, player2)
|
||||
game: GameState = newGameState(player1, player2)
|
||||
|
||||
test "winning row":
|
||||
game.board[0][0] = [
|
||||
[mFree, mFree, mFree],
|
||||
[mPlayer1, mPlayer1, mPlayer1],
|
||||
[mFree, mFree, mFree]
|
||||
[Mark.Free, Mark.Free, Mark.Free],
|
||||
[Mark.Player1, Mark.Player1, Mark.Player1],
|
||||
[Mark.Free, Mark.Free, Mark.Free]
|
||||
]
|
||||
check checkBoard(game.board[0][0]) == mPlayer1
|
||||
check checkBoard(game.board[0][0]) == Mark.Player1
|
||||
|
||||
test "winning column":
|
||||
game.board[0][0] = [
|
||||
[mPlayer2, mFree, mPlayer1],
|
||||
[mPlayer2, mPlayer1, mFree],
|
||||
[mPlayer2, mPlayer1, mFree]
|
||||
[Mark.Player2, Mark.Free, Mark.Player1],
|
||||
[Mark.Player2, Mark.Player1, Mark.Free],
|
||||
[Mark.Player2, Mark.Player1, Mark.Free]
|
||||
]
|
||||
check checkBoard(game.board[0][0]) == mPlayer2
|
||||
check checkBoard(game.board[0][0]) == Mark.Player2
|
||||
|
||||
test "winning diagonals":
|
||||
game.board[0][0] = [
|
||||
[mFree, mFree, mPlayer2],
|
||||
[mFree, mPlayer2, mFree],
|
||||
[mPlayer2, mFree, mFree]
|
||||
[Mark.Free, Mark.Free, Mark.Player2],
|
||||
[Mark.Free, Mark.Player2, Mark.Free],
|
||||
[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] = [
|
||||
[mPlayer1, mPlayer2, mPlayer2],
|
||||
[mPlayer2, mPlayer1, mPlayer1],
|
||||
[mPlayer2, mFree, mPlayer1]
|
||||
[Mark.Player1, Mark.Player2, Mark.Player2],
|
||||
[Mark.Player2, Mark.Player1, Mark.Player1],
|
||||
[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] = [
|
||||
[mPlayer1, mPlayer2, mPlayer1],
|
||||
[mPlayer2, mPlayer1, mPlayer1],
|
||||
[mPlayer2, mPlayer1, mPlayer2]
|
||||
[Mark.Player1, Mark.Player2, Mark.Player1],
|
||||
[Mark.Player2, Mark.Player1, Mark.Player1],
|
||||
[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":
|
||||
game.board[0][0] = [
|
||||
[mPlayer1, mPlayer2, mFree],
|
||||
[mPlayer1, mFree, mPlayer1],
|
||||
[mFree, mPlayer1, mPlayer2]
|
||||
[Mark.Player1, Mark.Player2, Mark.Free],
|
||||
[Mark.Player1, Mark.Free, Mark.Player1],
|
||||
[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":
|
||||
check checkBoard(game.board) == mFree
|
||||
check checkBoard(game.board) == Mark.Free
|
||||
|
||||
test "winning metaboard row":
|
||||
game.board[1][0] = [
|
||||
[mPlayer1, mPlayer2, mFree],
|
||||
[mPlayer1, mPlayer1, mPlayer1],
|
||||
[mFree, mPlayer1, mPlayer2]
|
||||
[Mark.Player1, Mark.Player2, Mark.Free],
|
||||
[Mark.Player1, Mark.Player1, Mark.Player1],
|
||||
[Mark.Free, Mark.Player1, Mark.Player2]
|
||||
]
|
||||
game.board[1][1] = [
|
||||
[mFree, mFree, mPlayer1],
|
||||
[mFree, mPlayer1, mFree],
|
||||
[mPlayer1, mFree, mFree]
|
||||
[Mark.Free, Mark.Free, Mark.Player1],
|
||||
[Mark.Free, Mark.Player1, Mark.Free],
|
||||
[Mark.Player1, Mark.Free, Mark.Free]
|
||||
]
|
||||
game.board[1][2] = [
|
||||
[mFree, mFree, mPlayer1],
|
||||
[mFree, mFree, mPlayer1],
|
||||
[mFree, mFree, mPlayer1]
|
||||
[Mark.Free, Mark.Free, Mark.Player1],
|
||||
[Mark.Free, Mark.Free, Mark.Player1],
|
||||
[Mark.Free, Mark.Free, Mark.Player1]
|
||||
]
|
||||
check checkBoard(game.board) == mPlayer1
|
||||
check checkBoard(game.board) == Mark.Player1
|
||||
|
||||
test "winning metaboard column":
|
||||
game.board[0][1] = [
|
||||
[mPlayer1, mPlayer2, mFree],
|
||||
[mPlayer1, mPlayer1, mPlayer1],
|
||||
[mFree, mPlayer1, mPlayer2]
|
||||
[Mark.Player1, Mark.Player2, Mark.Free],
|
||||
[Mark.Player1, Mark.Player1, Mark.Player1],
|
||||
[Mark.Free, Mark.Player1, Mark.Player2]
|
||||
]
|
||||
game.board[1][1] = [
|
||||
[mFree, mFree, mFree],
|
||||
[mPlayer1, mPlayer1, mPlayer1],
|
||||
[mFree, mFree, mFree]
|
||||
[Mark.Free, Mark.Free, Mark.Free],
|
||||
[Mark.Player1, Mark.Player1, Mark.Player1],
|
||||
[Mark.Free, Mark.Free, Mark.Free]
|
||||
]
|
||||
game.board[2][1] = [
|
||||
[mPlayer1, mFree, mFree],
|
||||
[mPlayer1, mFree, mFree],
|
||||
[mPlayer1, mFree, mFree]
|
||||
[Mark.Player1, Mark.Free, Mark.Free],
|
||||
[Mark.Player1, Mark.Free, Mark.Free],
|
||||
[Mark.Player1, Mark.Free, Mark.Free]
|
||||
]
|
||||
check checkBoard(game.board) == mPlayer1
|
||||
check checkBoard(game.board) == Mark.Player1
|
||||
|
||||
test "winning metaboard diagonal":
|
||||
game.board[0][0] = [
|
||||
[mPlayer2, mPlayer2, mFree],
|
||||
[mPlayer2, mPlayer2, mPlayer2],
|
||||
[mFree, mPlayer2, mPlayer2]
|
||||
[Mark.Player2, Mark.Player2, Mark.Free],
|
||||
[Mark.Player2, Mark.Player2, Mark.Player2],
|
||||
[Mark.Free, Mark.Player2, Mark.Player2]
|
||||
]
|
||||
game.board[1][1] = [
|
||||
[mFree, mFree, mFree],
|
||||
[mPlayer2, mPlayer2, mPlayer2],
|
||||
[mFree, mFree, mFree]
|
||||
[Mark.Free, Mark.Free, Mark.Free],
|
||||
[Mark.Player2, Mark.Player2, Mark.Player2],
|
||||
[Mark.Free, Mark.Free, Mark.Free]
|
||||
]
|
||||
game.board[2][2] = [
|
||||
[mPlayer2, mFree, mFree],
|
||||
[mPlayer2, mFree, mFree],
|
||||
[mPlayer2, mFree, mFree]
|
||||
[Mark.Player2, Mark.Free, Mark.Free],
|
||||
[Mark.Player2, Mark.Free, Mark.Free],
|
||||
[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":
|
||||
let winner = [
|
||||
[mFree, mFree, mFree],
|
||||
[mPlayer2, mPlayer2, mPlayer2],
|
||||
[mFree, mFree, mFree]
|
||||
[Mark.Free, Mark.Free, Mark.Free],
|
||||
[Mark.Player2, Mark.Player2, Mark.Player2],
|
||||
[Mark.Free, Mark.Free, Mark.Free]
|
||||
]
|
||||
let drawn = [
|
||||
[mPlayer2, mPlayer2, mPlayer1],
|
||||
[mPlayer1, mPlayer1, mPlayer2],
|
||||
[mPlayer2, mPlayer1, mPlayer2]
|
||||
[Mark.Player2, Mark.Player2, Mark.Player1],
|
||||
[Mark.Player1, Mark.Player1, Mark.Player2],
|
||||
[Mark.Player2, Mark.Player1, Mark.Player2]
|
||||
]
|
||||
game.board[0][0] = winner
|
||||
game.board[1][1] = winner
|
||||
game.board[2][2] = winner
|
||||
game.board[1][0] = drawn
|
||||
game.board[2][0] = drawn
|
||||
check checkBoard(game.board) == mPlayer2
|
||||
check checkBoard(game.board) == Mark.Player2
|
||||
|
||||
test "metaboard is drawn":
|
||||
let drawn = [
|
||||
[mPlayer2, mPlayer2, mPlayer1],
|
||||
[mPlayer1, mPlayer1, mPlayer2],
|
||||
[mPlayer2, mPlayer1, mPlayer2]
|
||||
[Mark.Player2, Mark.Player2, Mark.Player1],
|
||||
[Mark.Player1, Mark.Player1, Mark.Player2],
|
||||
[Mark.Player2, Mark.Player1, Mark.Player2]
|
||||
]
|
||||
for x in 0 .. 2:
|
||||
for y in 0 .. 2:
|
||||
game.board[x][y] = drawn
|
||||
check checkBoard(game.board) == mDraw
|
||||
check checkBoard(game.board) == Mark.Draw
|
||||
|
||||
test "illegal situation: both players win board":
|
||||
game.board[1][1] = [
|
||||
[mPlayer1, mPlayer1, mPlayer1],
|
||||
[mPlayer2, mPlayer2, mPlayer2],
|
||||
[mFree, mFree, mFree]
|
||||
[Mark.Player1, Mark.Player1, Mark.Player1],
|
||||
[Mark.Player2, Mark.Player2, Mark.Player2],
|
||||
[Mark.Free, Mark.Free, Mark.Free]
|
||||
]
|
||||
expect(Exception):
|
||||
discard checkBoard(game.board)
|
||||
@@ -7,79 +7,79 @@ suite "Test the move procedures":
|
||||
var
|
||||
player1 = Player(name: "Adam")
|
||||
player2 = Player(name: "Eve")
|
||||
game: GameState = newGame(player1, player2)
|
||||
game: GameState = newGameState(player1, player2)
|
||||
|
||||
test "Inital state":
|
||||
check game.currentPlayer == player1
|
||||
check game.players == [player1, player2]
|
||||
check game.turn == 0
|
||||
check game.currentBoard.isNone
|
||||
check game.result == mFree
|
||||
check game.result == Mark.Free
|
||||
|
||||
test "First move":
|
||||
game = game.makeMove((1, 1), (1, 1))
|
||||
game.makeMove((1, 1), (1, 1))
|
||||
check game.currentPlayer == player2
|
||||
check game.turn == 1
|
||||
check game.currentBoard == (1, 1).some()
|
||||
check game.result == mFree
|
||||
check game.result == Mark.Free
|
||||
|
||||
test "Second move":
|
||||
game = game.makeMove((1, 1), (1, 1))
|
||||
game = game.makeMove((0, 0))
|
||||
game.makeMove((1, 1), (1, 1))
|
||||
game.makeMove((0, 0))
|
||||
check game.currentPlayer == player1
|
||||
check game.turn == 2
|
||||
check game.currentBoard == (0, 0).some()
|
||||
check game.result == mFree
|
||||
check game.result == Mark.Free
|
||||
|
||||
test "Move on wrong board":
|
||||
game = game.makeMove((1, 1), (1, 1))
|
||||
game.makeMove((1, 1), (1, 1))
|
||||
expect(IllegalMoveError):
|
||||
game = game.makeMove((0, 0), (2, 2))
|
||||
game.makeMove((0, 0), (2, 2))
|
||||
|
||||
test "Move on a taken cell":
|
||||
game = game.makeMove((1, 1), (1, 1))
|
||||
game.makeMove((1, 1), (1, 1))
|
||||
expect(IllegalMoveError):
|
||||
game = game.makeMove((1, 1))
|
||||
game.makeMove((1, 1))
|
||||
|
||||
test "Move out of bounds":
|
||||
expect(IndexError):
|
||||
game = game.makeMove((3, 0), (0, 0))
|
||||
game.makeMove((3, 0), (0, 0))
|
||||
|
||||
test "Player 1 wins the game":
|
||||
let winner = [
|
||||
[mPlayer1, mFree, mFree],
|
||||
[mPlayer1, mFree, mFree],
|
||||
[mPlayer1, mFree, mFree]
|
||||
[Mark.Player1, Mark.Free, Mark.Free],
|
||||
[Mark.Player1, Mark.Free, Mark.Free],
|
||||
[Mark.Player1, Mark.Free, Mark.Free]
|
||||
]
|
||||
game.board[0][0] = winner
|
||||
game.board[1][1] = winner
|
||||
|
||||
game.board[2][2] = [
|
||||
[mPlayer1, mFree, mFree],
|
||||
[mFree, mFree, mFree],
|
||||
[mPlayer1, mFree, mFree]
|
||||
[Mark.Player1, Mark.Free, Mark.Free],
|
||||
[Mark.Free, Mark.Free, Mark.Free],
|
||||
[Mark.Player1, Mark.Free, Mark.Free]
|
||||
]
|
||||
check checkBoard(game.board) == mFree
|
||||
game = game.makeMove((1, 0), (2, 2))
|
||||
check game.result == mPlayer1
|
||||
check checkBoard(game.board) == Mark.Free
|
||||
game.makeMove((1, 0), (2, 2))
|
||||
check game.result == Mark.Player1
|
||||
check game.currentPlayer == player1
|
||||
|
||||
test "The game ends in a draw":
|
||||
let drawn = [
|
||||
[mPlayer2, mPlayer2, mPlayer1],
|
||||
[mPlayer1, mPlayer1, mPlayer2],
|
||||
[mPlayer2, mPlayer1, mPlayer2]
|
||||
[Mark.Player2, Mark.Player2, Mark.Player1],
|
||||
[Mark.Player1, Mark.Player1, Mark.Player2],
|
||||
[Mark.Player2, Mark.Player1, Mark.Player2]
|
||||
]
|
||||
for x in 0 .. 2:
|
||||
for y in 0 .. 2:
|
||||
game.board[x][y] = drawn
|
||||
|
||||
game.board[2][2] = [
|
||||
[mPlayer2, mPlayer2, mPlayer1],
|
||||
[mPlayer1, mPlayer1, mPlayer2],
|
||||
[mPlayer2, mFree, mPlayer2]
|
||||
[Mark.Player2, Mark.Player2, Mark.Player1],
|
||||
[Mark.Player1, Mark.Player1, Mark.Player2],
|
||||
[Mark.Player2, Mark.Free, Mark.Player2]
|
||||
]
|
||||
check checkBoard(game.board) == mFree
|
||||
game = game.makeMove((2, 1), (2, 2))
|
||||
check game.result == mDraw
|
||||
check checkBoard(game.board) == Mark.Free
|
||||
game.makeMove((2, 1), (2, 2))
|
||||
check game.result == Mark.Draw
|
||||
check game.currentPlayer == player1
|
||||
Reference in New Issue
Block a user