Check procs for sub boards.

This commit is contained in:
2019-10-26 00:15:30 +02:00
parent dcddb96f93
commit b0bdb16b4a
4 changed files with 147 additions and 25 deletions

View File

@@ -1,21 +1,10 @@
import strformat
import sets
type
Board*[T] =
array[3, array[3, T]]
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
BoardResult* = enum
rPlayer1, # The first player has won the board
rPlayer2, # The second player has won the board
rDraw, # There is no winner. The board has ended in a draw
rOpen # The game on this board is still ongoing
include libmttt/types
include libmttt/checks
proc newBoard[T](initial: T): Board[T] =
## Create a new game board filled with the initial value
result = [
[initial, initial, initial],
[initial, initial, initial],
@@ -23,20 +12,38 @@ proc newBoard[T](initial: T): Board[T] =
]
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 createBoard*(): Board[Board[Mark]] =
newMetaBoard(mFree)
proc newGame*(player1, player2: Player): GameState =
## Create a new game board
GameState(
players: [player1, player2],
currentPlayer: player1,
result: rOpen,
board: newMetaBoard(mFree))
proc checkBoard*(board: Board[Mark]): BoardResult =
for x in 0 ..< board.len:
for y in 0 ..< board.len:
echo fmt"Cell (x: {x}, y: {y}) = {board[x][y]}"
## 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[BoardResult]
states.init()
for b in [board, transposed]:
states.incl(checkRows(b))
return selectResult(states)
proc checkBoard*(board: Board[Board[Mark]]): BoardResult =
## Perform a check on a metaboard to see the overall game result
rOpen

42
src/libmttt/checks.nim Normal file
View File

@@ -0,0 +1,42 @@
import sets
import types
proc selectResult(states: HashSet[BoardResult]): BoardResult =
## Analyse a set of results and select the correct one
if states.contains(rPlayer1) and
states.contains(rPlayer2):
raise newException(Exception, "Both players cannot win at the same time")
if states.contains(rPlayer1):
return rPlayer1
if states.contains(rPlayer2):
return rPlayer2
if states.contains(rOpen):
return rOpen
return rDraw
proc checkRow(row: array[3, Mark]): BoardResult =
var tokens = toHashSet(row)
# A player has won
if tokens.len == 1 and not tokens.contains(mFree):
let rowResult = tokens.pop()
if rowResult == mPlayer1:
return rPlayer1
else:
return rPlayer2
# The row is full
if tokens.len == 2 and not tokens.contains(mFree):
return rDraw
# There are still cells free in this row
else:
return rOpen
proc checkRows(board: Board[Mark]): BoardResult =
## Iterate over all rows of this board and return the result.
var states: HashSet[BoardResult]
states.init()
for row in board:
states.incl(checkRow(row))
return states.selectResult()

39
src/libmttt/types.nim Normal file
View File

@@ -0,0 +1,39 @@
import strformat
type
## A game board is a simple 2 dimensional array.
## Markers of any type can be placed inside the cells
Board*[T] = array[3, array[3, T]]
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
BoardResult* = enum
rPlayer1, ## The first player has won the board
rPlayer2, ## The second player has won the board
rDraw, ## There is no winner. The board has ended in a draw
rOpen ## The game on this board is still ongoing
GameState* = ref object
## Contains all state for a pint in the game
board*: Board[Board[Mark]]
players*: array[2, Player]
currentPlayer*: Player
result*: BoardResult
turn*: int
proc `$`*(player: 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}
"""

View File

@@ -9,7 +9,41 @@ import unittest
import libmttt
test "debugging":
var metaBoard = createBoard()
metaBoard[0][0][1][1] = mPlayer1
echo metaBoard[0][0].checkBoard()
suite "Test the board result checker":
setup:
var
player1 = Player(name: "Max")
player2 = Player(name: "Adam")
state: GameState = newGame(player1, player2)
test "row checking":
state.board[0][0] = [
[mFree, mFree, mFree],
[mPlayer1, mPlayer1, mPlayer1],
[mFree, mFree, mFree]
]
check checkBoard(state.board[0][0]) == rPlayer1
test "column checking":
state.board[0][0] = [
[mPlayer2, mFree, mPlayer1],
[mPlayer2, mPlayer1, mFree],
[mPlayer2, mPlayer1, mFree]
]
check checkBoard(state.board[0][0]) == rPlayer2
test "check for draw":
state.board[0][0] = [
[mPlayer1, mPlayer2, mPlayer1],
[mPlayer2, mPlayer1, mPlayer1],
[mPlayer2, mPlayer1, mPlayer2]
]
check checkBoard(state.board[0][0]) == rDraw
test "check for open board":
state.board[0][0] = [
[mPlayer1, mPlayer2, mFree],
[mPlayer1, mFree, mPlayer1],
[mFree, mPlayer1, mPlayer2]
]
check checkBoard(state.board[0][0]) == rOpen