From e1ccdc728eb701664e01c18725b3a1ef8f6b7248 Mon Sep 17 00:00:00 2001 From: nocturn9x Date: Tue, 16 Apr 2024 08:50:42 +0200 Subject: [PATCH] Drop old attack tracking system in preparation for bitboards --- src/Chess/board.nim | 131 ++++++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 64 deletions(-) diff --git a/src/Chess/board.nim b/src/Chess/board.nim index 52dae45..f93ca9f 100644 --- a/src/Chess/board.nim +++ b/src/Chess/board.nim @@ -104,12 +104,7 @@ type fullMoveCount: int8 # En passant target square (see https://en.wikipedia.org/wiki/En_passant) enPassantSquare*: Square - # Squares of all pieces - pieces: tuple[white, black: Pieces] - # Squares attacked by both sides - attacked: tuple[white, black: Attacked] - # Pieces pinned by both sides (only absolute pins) - pinned: tuple[white, black: Attacked] + # Active color turn: PieceColor @@ -144,18 +139,18 @@ func `-`*(a, b: Square): Square{.inline.} = (a.rank - b.rank, a.file - b.file) func isValid*(a: Square): bool {.inline.} = a.rank in 0..7 and a.file in 0..7 func isLightSquare(a: Square): bool {.inline.} = (a.rank + a.file and 2) == 0 proc generateMoves(self: ChessBoard, square: Square): seq[Move] -proc getAttackers*(self: ChessBoard, square: Square, color: PieceColor): seq[Square] -proc getAttackFor*(self: ChessBoard, source, target: Square): tuple[source, target, direction: Square] -proc isAttacked*(self: ChessBoard, square: Square, color: PieceColor = None): bool +# proc getAttackers*(self: ChessBoard, square: Square, color: PieceColor): seq[Square] +# proc getAttackFor*(self: ChessBoard, source, target: Square): tuple[source, target, direction: Square] +# proc isAttacked*(self: ChessBoard, square: Square, color: PieceColor = None): bool proc isLegal(self: ChessBoard, move: Move): bool {.inline.} proc doMove(self: ChessBoard, move: Move) proc pretty*(self: ChessBoard): string proc spawnPiece(self: ChessBoard, square: Square, piece: Piece) -proc updateAttackedSquares(self: ChessBoard) -proc updateSlidingAttacks(self: ChessBoard) -proc getPinnedDirections(self: ChessBoard, square: Square): seq[Square] -proc getAttacks*(self: ChessBoard, square: Square): Attacked -proc getSlidingAttacks(self: ChessBoard, square: Square): tuple[attacks: Attacked, pins: Attacked] +# proc updateAttackedSquares(self: ChessBoard) +# proc updateSlidingAttacks(self: ChessBoard) +# proc getPinnedDirections(self: ChessBoard, square: Square): seq[Square] +# proc getAttacks*(self: ChessBoard, square: Square): Attacked +# proc getSlidingAttacks(self: ChessBoard, square: Square): tuple[attacks: Attacked, pins: Attacked] proc inCheck*(self: ChessBoard, color: PieceColor = None): bool proc toFEN*(self: ChessBoard): string proc undoLastMove*(self: ChessBoard) @@ -325,22 +320,8 @@ proc newChessboard: ChessBoard = new(result) for i in 0..63: result.grid[i] = emptyPiece() - result.position = Position(attacked: (@[], @[]), - enPassantSquare: emptySquare(), - turn: White, - fullMoveCount: 1, - pieces: (white: (king: emptySquare(), - queens: @[], - rooks: @[], - bishops: @[], - knights: @[], - pawns: @[]), - black: (king: emptySquare(), - queens: @[], - rooks: @[], - bishops: @[], - knights: @[], - pawns: @[]))) + result.position = Position(enPassantSquare: emptySquare(), turn: White) + # Handy wrappers and utilities for handling low-level stuff func coordToIndex(row, col: SomeInteger): int {.inline.} = (row * 8) + col @@ -358,6 +339,14 @@ func `shl`(a: Bitboard, x: Positive): Bitboard = Bitboard(a.uint64 shl x) func `shr`(a: Bitboard, x: Positive): Bitboard = Bitboard(a.uint64 shr x) func `and`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 and b.uint64) func `shr`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 and b.uint64) +func `+`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 + b.uint64) +func `-`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 - b.uint64) +func `div`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 div b.uint64) +func `*`(a, b: Bitboard): Bitboard = Bitboard(a.uint64 * b.uint64) +func `+`(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 + b) +func `-`(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 - b.uint64) +func `div`(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 div b) +func `*`(a: Bitboard, b: SomeUnsignedInt): Bitboard = Bitboard(a.uint64 * b) func `==`(a, b: Bitboard): bool {.inline.} = a.uint64 == b.uint64 func `==`(a: Bitboard, b: SomeInteger): bool {.inline.} = a.uint64 == b.uint64 @@ -367,6 +356,8 @@ func getRankMask(rank: Positive): Bitboard = Bitboard(uint64.high()) shl Positiv func squareToBitboard(square: SomeInteger): Bitboard = Bitboard(1'u64) shl square.uint64 func squareToBitboard(square: Square): Bitboard = squareToBitboard(coordToIndex(square)) +proc bitboardToSquare(b: Bitboard): Square = Square(b.uint64.countTrailingZeroBits().indexToCoord()) + func toBin(x: Bitboard, b: Positive = 64): string = toBin(BiggestInt(x), b) func toBin(x: uint64, b: Positive = 64): string = toBin(Bitboard(x), b) @@ -375,9 +366,9 @@ iterator items(self: Bitboard): Square = ## Iterates ove the given bitboard ## and returns all the squares that ## are set - var bits = self.uint64 + var bits = self while bits != 0: - yield Square(bits.countTrailingZeroBits().indexToCoord()) + yield bits.bitboardToSquare() bits = bits and bits - 1 @@ -668,10 +659,10 @@ proc newChessboardFromFEN*(fen: string): ChessBoard = else: raise newException(ValueError, "invalid FEN: too many fields in FEN string") inc(index) - result.updateAttackedSquares() - if result.inCheck(result.getActiveColor().opposite): + # result.updateAttackedSquares() + #[if result.inCheck(result.getActiveColor().opposite): # Opponent king cannot be captured on the next move - raise newException(ValueError, "invalid position: opponent king can be captured") + raise newException(ValueError, "invalid position: opponent king can be captured")]# if result.position.bitboards.white.king == Bitboard(0) or result.position.bitboards.black.king == Bitboard(0): # Both kings must be on the board raise newException(ValueError, "invalid position: exactly one king of each color must be present") @@ -854,13 +845,17 @@ func getKing(self: ChessBoard, color: PieceColor = None): Square {.inline.} = color = self.getActiveColor() case color: of White: - return self.position.pieces.white.king + return self.position.bitboards.white.king.uint64.countTrailingZeroBits().indexToCoord() of Black: - return self.position.pieces.black.king + return self.position.bitboards.black.king.uint64.countTrailingZeroBits().indexToCoord() else: discard +# TODO +proc inCheck*(self: ChessBoard, color: PieceColor = None): bool = false +proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king: bool] {.inline.} = (false, false) +#[ proc inCheck*(self: ChessBoard, color: PieceColor = None): bool = ## Returns whether the given color's ## king is in check. If the color is @@ -869,7 +864,7 @@ proc inCheck*(self: ChessBoard, color: PieceColor = None): bool = var color = color if color == None: color = self.getActiveColor() - case color: + #[case color: of White: result = self.isAttacked(self.position.pieces.white.king, Black) of Black: @@ -877,6 +872,7 @@ proc inCheck*(self: ChessBoard, color: PieceColor = None): bool = else: # Unreachable discard + ]# proc inDoubleCheck*(self: ChessBoard, color: PieceColor = None): bool = @@ -935,11 +931,11 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king: # is temporarily prohibited on that side case color: of White: - square = self.position.pieces.white.king + square = self.position.bitboards.white.king.bitboardToSquare() queenSide = color.leftSide() kingSide = color.rightSide() of Black: - square = self.position.pieces.black.king + square = self.position.bitboards.black.king.bitboardToSquare() queenSide = color.rightSide() kingSide = color.leftSide() of None: @@ -1049,12 +1045,13 @@ proc getCheckResolutions(self: ChessBoard, color: PieceColor): seq[Square] = if not square.isValid(): break result.add(square) - +]# proc generatePawnMoves(self: ChessBoard, square: Square): seq[Move] = ## Generates the possible moves for the pawn in the given ## square - var + + #[var piece = self.grid[square.rank, square.file] directions: seq[Square] = @[] assert piece.kind == Pawn, &"generatePawnMoves called on a {piece.kind}" @@ -1136,10 +1133,12 @@ proc generatePawnMoves(self: ChessBoard, square: Square): seq[Move] = result.add(Move(startSquare: square, targetSquare: target, flags: promotionType.uint16 or flags)) continue result.add(Move(startSquare: square, targetSquare: target, flags: flags)) + ]# proc generateSlidingMoves(self: ChessBoard, square: Square): seq[Move] = ## Generates moves for the sliding piece in the given square + #[ let piece = self.grid[square.rank, square.file] assert piece.kind in [Bishop, Rook, Queen], &"generateSlidingMoves called on a {piece.kind}" var directions: seq[Square] = @[] @@ -1198,10 +1197,12 @@ proc generateSlidingMoves(self: ChessBoard, square: Square): seq[Move] = break # Target square is empty, keep going result.add(Move(startSquare: square, targetSquare: square)) + ]# proc generateKingMoves(self: ChessBoard, square: Square): seq[Move] = ## Generates moves for the king in the given square + #[ var piece = self.grid[square.rank, square.file] assert piece.kind == King, &"generateKingMoves called on a {piece.kind}" @@ -1243,10 +1244,12 @@ proc generateKingMoves(self: ChessBoard, square: Square): seq[Move] = # Target square is empty or contains an enemy piece: # All good for us! result.add(Move(startSquare: square, targetSquare: square, flags: flag.uint16)) + ]# proc generateKnightMoves(self: ChessBoard, square: Square): seq[Move] = ## Generates moves for the knight in the given square + #[ var piece = self.grid[square.rank, square.file] assert piece.kind == Knight, &"generateKnightMoves called on a {piece.kind}" @@ -1283,6 +1286,7 @@ proc generateKnightMoves(self: ChessBoard, square: Square): seq[Move] = else: # Target square is empty result.add(Move(startSquare: square, targetSquare: square)) + ]# proc checkInsufficientMaterialPieceCount(self: ChessBoard, color: PieceColor): bool = @@ -1327,12 +1331,12 @@ proc checkInsufficientMaterial(self: ChessBoard): bool = var darkSquare = 0 lightSquare = 0 - for bishop in self.position.pieces.black.bishops: + for bishop in self.position.bitboards.black.bishops: if bishop.isLightSquare(): lightSquare += 1 else: darkSquare += 1 - for bishop in self.position.pieces.white.bishops: + for bishop in self.position.bitboards.white.bishops: if bishop.isLightSquare(): lightSquare += 1 else: @@ -1375,8 +1379,8 @@ proc generateAllMoves*(self: ChessBoard): seq[Move] = if self.grid[i, j].color == self.getActiveColor(): for move in self.generateMoves((int8(i), int8(j))): result.add(move) - +#[ proc isAttacked*(self: ChessBoard, square: Square, color: PieceColor = None): bool = ## Returns whether the given square is attacked ## by the given color @@ -1662,7 +1666,7 @@ proc updateAttackedSquares(self: ChessBoard) = self.updateKnightAttacks() # Kings self.updateKingAttacks() - +]# proc removePieceFromBitboard(self: ChessBoard, square: Square) = ## Removes a piece at the given square in the chessboard from @@ -1751,8 +1755,8 @@ proc removePiece(self: ChessBoard, square: Square, attack: bool = true) = var piece = self.grid[square] self.grid[square] = emptyPiece() self.removePieceFromBitboard(square) - if attack: - self.updateAttackedSquares() + #[if attack: + self.updateAttackedSquares()]# proc updateMoveBitboards(self: ChessBoard, move: Move) = @@ -1826,8 +1830,8 @@ proc movePiece(self: ChessBoard, move: Move, attack: bool = true) = self.grid[move.startSquare] = emptyPiece() # Actually move the piece on the board self.grid[move.targetSquare] = piece - if attack: - self.updateAttackedSquares() + #[if attack: + self.updateAttackedSquares()]# proc movePiece(self: ChessBoard, startSquare, targetSquare: Square, attack: bool = true) = @@ -1925,7 +1929,6 @@ proc doMove(self: ChessBoard, move: Move) = fullMoveCount: fullMoveCount, turn: self.getActiveColor().opposite, castlingAvailable: castlingAvailable, - pieces: self.position.pieces, enPassantSquare: enPassantTarget, bitboards: self.position.bitboards ) @@ -1975,7 +1978,7 @@ proc doMove(self: ChessBoard, move: Move) = else: # Unreachable discard - self.updateAttackedSquares() + #self.updateAttackedSquares() proc spawnPiece(self: ChessBoard, square: Square, piece: Piece) = @@ -1993,28 +1996,28 @@ proc updateBoard*(self: ChessBoard) = ## in the chessboard for i in 0..63: self.grid[i] = emptyPiece() - for sq in self.position.pieces.white.pawns: + for sq in self.position.bitboards.white.pawns: self.grid[sq] = Piece(color: White, kind: Pawn) - for sq in self.position.pieces.black.pawns: + for sq in self.position.bitboards.black.pawns: self.grid[sq] = Piece(color: Black, kind: Pawn) - for sq in self.position.pieces.white.bishops: + for sq in self.position.bitboards.white.bishops: self.grid[sq] = Piece(color: White, kind: Bishop) - for sq in self.position.pieces.black.bishops: + for sq in self.position.bitboards.black.bishops: self.grid[sq] = Piece(color: Black, kind: Bishop) - for sq in self.position.pieces.white.knights: + for sq in self.position.bitboards.white.knights: self.grid[sq] = Piece(color: White, kind: Knight) - for sq in self.position.pieces.black.knights: + for sq in self.position.bitboards.black.knights: self.grid[sq] = Piece(color: Black, kind: Knight) - for sq in self.position.pieces.white.rooks: + for sq in self.position.bitboards.white.rooks: self.grid[sq] = Piece(color: White, kind: Rook) - for sq in self.position.pieces.black.rooks: + for sq in self.position.bitboards.black.rooks: self.grid[sq] = Piece(color: Black, kind: Rook) - for sq in self.position.pieces.white.queens: + for sq in self.position.bitboards.white.queens: self.grid[sq] = Piece(color: White, kind: Queen) - for sq in self.position.pieces.black.queens: + for sq in self.position.bitboards.black.queens: self.grid[sq] = Piece(color: Black, kind: Queen) - self.grid[self.position.pieces.white.king] = Piece(color: White, kind: King) - self.grid[self.position.pieces.black.king] = Piece(color: Black, kind: King) + self.grid[self.position.bitboards.white.king.bitboardToSquare()] = Piece(color: White, kind: King) + self.grid[self.position.bitboards.black.king.bitboardToSquare()] = Piece(color: Black, kind: King) proc undoLastMove*(self: ChessBoard) =