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 type
Location = tuple[row, col: int] Location = tuple[row, col: int]
Pieces = tuple[king: Location, queen: Location, rooks: array[2, Location], Pieces = tuple[king: Location, queen: Location, rooks: seq[Location],
bishops: array[2, Location], knights: array[2, Location], bishops: seq[Location], knights: seq[Location],
pawns: array[8, Location]] pawns: seq[Location]]
PieceColor* = enum PieceColor* = enum
None = 0, None = 0,
White, White,
Black Black
PieceKind* = enum PieceKind* = enum
Empty = 0, # No piece Empty = '\0', # No piece
Bishop = 'b', Bishop = 'b',
King = 'k' King = 'k'
Knight = 'n', Knight = 'n',
@ -39,12 +39,13 @@ type
Piece* = object Piece* = object
color*: PieceColor color*: PieceColor
kind*: PieceKind kind*: PieceKind
captured*: bool
Position* = object Position* = object
piece*: Piece piece*: Piece
location*: Location location*: Location
ChessBoard* = ref object ChessBoard* = ref object
## A chess board object ## A chess board object
grid*: Matrix[Piece] grid: Matrix[Piece]
# Currently active color # Currently active color
turn: PieceColor turn: PieceColor
# Number of half moves since # Number of half moves since
@ -72,6 +73,53 @@ for _ in countup(0, 63):
empty.add(Piece(kind: Empty, color: None)) 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 = func rankToColumn(rank: int): int =
## Converts a chess rank (1-indexed) ## Converts a chess rank (1-indexed)
## into a 0-indexed column value for our ## into a 0-indexed column value for our
@ -110,7 +158,6 @@ proc getPiece*(self: ChessBoard, square: string): Piece =
proc `$`*(self: ChessBoard): string = proc `$`*(self: ChessBoard): string =
result &= "- - - - - - - -" result &= "- - - - - - - -"
#const indeces = [7, 6, 5, 4, 3, 2, 1, 0]
for i, row in self.grid: for i, row in self.grid:
result &= "\n" result &= "\n"
for piece in row: for piece in row:
@ -129,20 +176,6 @@ proc `$`*(self: ChessBoard): string =
proc newChessboard: ChessBoard = proc newChessboard: ChessBoard =
## Returns a new, empty chessboard ## Returns a new, empty chessboard
new(result) 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 # Turns our flat sequence into an 8x8 grid
result.grid = newMatrixFromSeq[Piece](empty, (8, 8)) result.grid = newMatrixFromSeq[Piece](empty, (8, 8))
result.attacked = (@[], @[]) result.attacked = (@[], @[])
@ -187,26 +220,13 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of Black: of Black:
case piece.kind: case piece.kind:
of Pawn: of Pawn:
# Find first empty slot in the pieces array result.pieces.black.pawns.add((row, column))
for i, e in result.pieces.black.pawns:
if e == (-1, -1):
result.pieces.black.pawns[i] = (row, column)
break
of Bishop: of Bishop:
if result.pieces.black.bishops[0] == (-1, -1): result.pieces.black.bishops.add((row, column))
result.pieces.black.bishops[0] = (row, column)
else:
result.pieces.black.bishops[1] = (row, column)
of Knight: of Knight:
if result.pieces.black.knights[0] == (-1, -1): result.pieces.black.knights.add((row, column))
result.pieces.black.knights[0] = (row, column)
else:
result.pieces.black.knights[1] = (row, column)
of Rook: of Rook:
if result.pieces.black.rooks[0] == (-1, -1): result.pieces.black.rooks.add((row, column))
result.pieces.black.rooks[0] = (row, column)
else:
result.pieces.black.rooks[1] = (row, column)
of Queen: of Queen:
result.pieces.black.queen = (row, column) result.pieces.black.queen = (row, column)
of King: of King:
@ -216,25 +236,13 @@ proc newChessboardFromFEN*(state: string): ChessBoard =
of White: of White:
case piece.kind: case piece.kind:
of Pawn: of Pawn:
for i, e in result.pieces.white.pawns: result.pieces.white.pawns.add((row, column))
if e == (-1, -1):
result.pieces.white.pawns[i] = (row, column)
break
of Bishop: of Bishop:
if result.pieces.white.bishops[0] == (-1, -1): result.pieces.white.bishops.add((row, column))
result.pieces.white.bishops[0] = (row, column)
else:
result.pieces.white.bishops[1] = (row, column)
of Knight: of Knight:
if result.pieces.white.knights[0] == (-1, -1): result.pieces.white.knights.add((row, column))
result.pieces.white.knights[0] = (row, column)
else:
result.pieces.white.knights[1] = (row, column)
of Rook: of Rook:
if result.pieces.white.rooks[0] == (-1, -1): result.pieces.white.rooks.add((row, column))
result.pieces.white.rooks[0] = (row, column)
else:
result.pieces.white.rooks[1] = (row, column)
of Queen: of Queen:
result.pieces.white.queen = (row, column) result.pieces.white.queen = (row, column)
of King: of King:
@ -330,9 +338,30 @@ when isMainModule:
proc testPiece(piece: Piece, kind: PieceKind, color: PieceColor) = 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" 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" echo "Running tests"
var b = newDefaultChessboard() 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"]: for loc in ["a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2"]:
testPiece(b.getPiece(loc), Pawn, White) testPiece(b.getPiece(loc), Pawn, White)
for loc in ["a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7"]: for loc in ["a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7"]: