Check procs for sub boards.
This commit is contained in:
@@ -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
42
src/libmttt/checks.nim
Normal 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
39
src/libmttt/types.nim
Normal 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}
|
||||
"""
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user