Refactored piece location storage and added test for it

This commit is contained in:
Mattia Giambirtone 2023-10-13 12:26:14 +02:00
parent 9af32e5e40
commit d3cc7bddde
1 changed files with 83 additions and 54 deletions

View File

@ -21,15 +21,15 @@ import std/strformat
type
Location = tuple[row, col: int]
Pieces = tuple[king: Location, queen: Location, rooks: array[2, Location],
bishops: array[2, Location], knights: array[2, Location],
pawns: array[8, Location]]
Pieces = tuple[king: Location, queen: Location, rooks: seq[Location],
bishops: seq[Location], knights: seq[Location],
pawns: seq[Location]]
PieceColor* = enum
None = 0,
White,
Black
PieceKind* = enum
Empty = 0, # No piece
Empty = '\0', # No piece
Bishop = 'b',
King = 'k'
Knight = 'n',
@ -39,12 +39,13 @@ type
Piece* = object
color*: PieceColor
kind*: PieceKind
captured*: bool
Position* = object
piece*: Piece
location*: Location
ChessBoard* = ref object
## A chess board object
grid*: Matrix[Piece]
grid: Matrix[Piece]
# Currently active color
turn: PieceColor
# Number of half moves since
@ -72,6 +73,53 @@ for _ in countup(0, 63):
empty.add(Piece(kind: Empty, color: None))
proc countPieces*(self: ChessBoard, kind: PieceKind, color: PieceColor): int =
## Counts the number of pieces with
## the given color and type
case color:
of White:
case kind:
of Pawn:
return self.pieces.white.pawns.len()
of Bishop:
return self.pieces.white.bishops.len()
of Knight:
return self.pieces.white.knights.len()
of Rook:
return self.pieces.white.rooks.len()
of Queen:
return int(self.pieces.white.queen != (-1, -1))
of King:
# There shall be only one, forever
return 1
else:
discard
of Black:
case kind:
of Pawn:
return self.pieces.black.pawns.len()
of Bishop:
return self.pieces.black.bishops.len()
of Knight:
return self.pieces.black.knights.len()
of Rook:
return self.pieces.black.rooks.len()
of Queen:
return int(self.pieces.black.queen != (-1, -1))
of King:
return 1
else:
discard
of None:
raise newException(ValueError, "invalid piece type")
proc countPieces*(self: ChessBoard, piece: Piece): int =
## Returns the number of pieces on the board that
## are of the same type and color of the given piece
return self.countPieces(piece.kind, piece.color)
func rankToColumn(rank: int): int =
## Converts a chess rank (1-indexed)
## into a 0-indexed column value for our
@ -110,7 +158,6 @@ proc getPiece*(self: ChessBoard, square: string): Piece =
proc `$`*(self: ChessBoard): string =
result &= "- - - - - - - -"
#const indeces = [7, 6, 5, 4, 3, 2, 1, 0]
for i, row in self.grid:
result &= "\n"
for piece in row:
@ -129,20 +176,6 @@ proc `$`*(self: ChessBoard): string =
proc newChessboard: ChessBoard =
## Returns a new, empty chessboard
new(result)
# Initialize all positions to a known default state.
# This is useful in newChessBoardFromFEN
result.pieces.black.king = (-1, -1)
result.pieces.white.king = (-1, -1)
result.pieces.black.queen = (-1, -1)
result.pieces.white.queen = (-1, -1)
result.pieces.black.rooks = [(-1, -1), (-1, -1)]
result.pieces.white.rooks = [(-1, -1), (-1, -1)]
result.pieces.black.bishops = [(-1, -1), (-1, -1)]
result.pieces.white.bishops = [(-1, -1), (-1, -1)]
result.pieces.black.knights = [(-1, -1), (-1, -1)]
result.pieces.white.knights = [(-1, -1), (-1, -1)]
result.pieces.black.pawns = [(-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1)]
result.pieces.white.pawns = [(-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1), (-1, -1)]
# Turns our flat sequence into an 8x8 grid
result.grid = newMatrixFromSeq[Piece](empty, (8, 8))
result.attacked = (@[], @[])
@ -187,26 +220,13 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of Black:
case piece.kind:
of Pawn:
# Find first empty slot in the pieces array
for i, e in result.pieces.black.pawns:
if e == (-1, -1):
result.pieces.black.pawns[i] = (row, column)
break
result.pieces.black.pawns.add((row, column))
of Bishop:
if result.pieces.black.bishops[0] == (-1, -1):
result.pieces.black.bishops[0] = (row, column)
else:
result.pieces.black.bishops[1] = (row, column)
result.pieces.black.bishops.add((row, column))
of Knight:
if result.pieces.black.knights[0] == (-1, -1):
result.pieces.black.knights[0] = (row, column)
else:
result.pieces.black.knights[1] = (row, column)
result.pieces.black.knights.add((row, column))
of Rook:
if result.pieces.black.rooks[0] == (-1, -1):
result.pieces.black.rooks[0] = (row, column)
else:
result.pieces.black.rooks[1] = (row, column)
result.pieces.black.rooks.add((row, column))
of Queen:
result.pieces.black.queen = (row, column)
of King:
@ -216,25 +236,13 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of White:
case piece.kind:
of Pawn:
for i, e in result.pieces.white.pawns:
if e == (-1, -1):
result.pieces.white.pawns[i] = (row, column)
break
result.pieces.white.pawns.add((row, column))
of Bishop:
if result.pieces.white.bishops[0] == (-1, -1):
result.pieces.white.bishops[0] = (row, column)
else:
result.pieces.white.bishops[1] = (row, column)
result.pieces.white.bishops.add((row, column))
of Knight:
if result.pieces.white.knights[0] == (-1, -1):
result.pieces.white.knights[0] = (row, column)
else:
result.pieces.white.knights[1] = (row, column)
result.pieces.white.knights.add((row, column))
of Rook:
if result.pieces.white.rooks[0] == (-1, -1):
result.pieces.white.rooks[0] = (row, column)
else:
result.pieces.white.rooks[1] = (row, column)
result.pieces.white.rooks.add((row, column))
of Queen:
result.pieces.white.queen = (row, column)
of King:
@ -330,9 +338,30 @@ when isMainModule:
proc testPiece(piece: Piece, kind: PieceKind, color: PieceColor) =
doAssert piece.kind == kind and piece.color == color, &"expected piece of kind {kind} and color {color}, got {piece.kind} / {piece.color} instead"
proc testPieceCount(board: ChessBoard, kind: PieceKind, color: PieceColor, count: int) =
let pieces = board.countPieces(kind, color)
doAssert pieces == count, &"expected {count} pieces of kind {kind} and color {color}, got {pieces} instead"
echo "Running tests"
var b = newDefaultChessboard()
# Ensure pawns are in the correct location
# Ensure correct number of pieces
testPieceCount(b, Pawn, White, 8)
testPieceCount(b, Pawn, Black, 8)
testPieceCount(b, Knight, White, 2)
testPieceCount(b, Knight, Black, 2)
testPieceCount(b, Bishop, White, 2)
testPieceCount(b, Bishop, Black, 2)
testPieceCount(b, Rook, White, 2)
testPieceCount(b, Rook, Black, 2)
testPieceCount(b, Queen, White, 1)
testPieceCount(b, Queen, Black, 1)
testPieceCount(b, King, White, 1)
testPieceCount(b, King, Black, 1)
# Ensure pieces are in the correct location
# Pawns
for loc in ["a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2"]:
testPiece(b.getPiece(loc), Pawn, White)
for loc in ["a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7"]: