From d3cc7bdddec12e010d6ba3accac318b47242d901 Mon Sep 17 00:00:00 2001 From: Mattia Giambirtone Date: Fri, 13 Oct 2023 12:26:14 +0200 Subject: [PATCH] Refactored piece location storage and added test for it --- src/Chess/board.nim | 137 +++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 54 deletions(-) diff --git a/src/Chess/board.nim b/src/Chess/board.nim index aedc1a7..b496bc4 100644 --- a/src/Chess/board.nim +++ b/src/Chess/board.nim @@ -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"]: