Final bug fixes. Test suite is passing
This commit is contained in:
parent
e62c78e4cc
commit
d03b2c2fbf
|
@ -76,11 +76,12 @@ func countSquares*(self: Bitboard): int {.inline.} =
|
|||
|
||||
|
||||
func lowestSquare*(self: Bitboard): Square {.inline.} =
|
||||
## Returns the index of the lowest one bit
|
||||
## Returns the index of the lowest set bit
|
||||
## in the given bitboard as a square
|
||||
result = Square(self.countTrailingZeroBits().uint8)
|
||||
|
||||
|
||||
|
||||
func getFileMask*(file: int): Bitboard = Bitboard(0x101010101010101'u64) shl file.uint64
|
||||
func getRankMask*(rank: int): Bitboard = Bitboard(0xff) shl uint64(8 * rank)
|
||||
func toBitboard*(square: SomeInteger): Bitboard = Bitboard(1'u64) shl square.uint64
|
||||
|
|
|
@ -185,12 +185,6 @@ func countPieces*(self: Chessboard, kind: PieceKind, color: PieceColor): int {.i
|
|||
return self.position.pieces[color][kind][].countSquares()
|
||||
|
||||
|
||||
func countPieces*(self: Chessboard, piece: Piece): int {.inline.} =
|
||||
## Returns the number of pieces on the board that
|
||||
## are of the same type and color as the given piece
|
||||
return self.countPieces(piece.kind, piece.color)
|
||||
|
||||
|
||||
func getPiece*(self: Chessboard, square: Square): Piece {.inline.} =
|
||||
## Gets the piece at the given square
|
||||
return self.grid[square]
|
||||
|
@ -202,6 +196,61 @@ func getPiece*(self: Chessboard, square: string): Piece {.inline.} =
|
|||
return self.getPiece(square.toSquare())
|
||||
|
||||
|
||||
proc removePieceFromBitboard*(self: Chessboard, square: Square) =
|
||||
## Removes a piece at the given square in the chessboard from
|
||||
## its respective bitboard
|
||||
let piece = self.getPiece(square)
|
||||
self.position.pieces[piece.color][piece.kind][].clearBit(square)
|
||||
|
||||
|
||||
proc addPieceToBitboard*(self: Chessboard, square: Square, piece: Piece) =
|
||||
## Adds the given piece at the given square in the chessboard to
|
||||
## its respective bitboard
|
||||
self.position.pieces[piece.color][piece.kind][].setBit(square)
|
||||
|
||||
|
||||
proc spawnPiece*(self: Chessboard, square: Square, piece: Piece) =
|
||||
## Internal helper to "spawn" a given piece at the given
|
||||
## square
|
||||
when not defined(danger):
|
||||
doAssert self.getPiece(square).kind == Empty
|
||||
self.addPieceToBitboard(square, piece)
|
||||
self.grid[square] = piece
|
||||
|
||||
|
||||
proc removePiece*(self: Chessboard, square: Square) =
|
||||
## Removes a piece from the board, updating necessary
|
||||
## metadata
|
||||
when not defined(danger):
|
||||
let Piece = self.getPiece(square)
|
||||
doAssert piece.kind != Empty and piece.color != None, self.toFEN()
|
||||
self.removePieceFromBitboard(square)
|
||||
self.grid[square] = nullPiece()
|
||||
|
||||
|
||||
proc movePiece*(self: Chessboard, move: Move) =
|
||||
## Internal helper to move a piece from
|
||||
## its current square to a target square
|
||||
let piece = self.getPiece(move.startSquare)
|
||||
when not defined(danger):
|
||||
let targetSquare = self.getPiece(move.targetSquare)
|
||||
if targetSquare.color != None:
|
||||
raise newException(AccessViolationDefect, &"{piece} at {move.startSquare} attempted to overwrite {targetSquare} at {move.targetSquare}: {move}")
|
||||
# Update positional metadata
|
||||
self.removePiece(move.startSquare)
|
||||
self.spawnPiece(move.targetSquare, piece)
|
||||
|
||||
|
||||
proc movePiece*(self: Chessboard, startSquare, targetSquare: Square) =
|
||||
self.movePiece(createMove(startSquare, targetSquare))
|
||||
|
||||
|
||||
func countPieces*(self: Chessboard, piece: Piece): int {.inline.} =
|
||||
## Returns the number of pieces on the board that
|
||||
## are of the same type and color as the given piece
|
||||
return self.countPieces(piece.kind, piece.color)
|
||||
|
||||
|
||||
func getOccupancyFor*(self: Chessboard, color: PieceColor): Bitboard =
|
||||
## Get the occupancy bitboard for every piece of the given color
|
||||
result = Bitboard(0)
|
||||
|
|
|
@ -31,11 +31,6 @@ import misc
|
|||
export bitboards, magics, pieces, moves, position, rays, misc, board
|
||||
|
||||
|
||||
proc removePieceFromBitboard(self: Chessboard, square: Square)
|
||||
proc addPieceToBitboard(self: Chessboard, square: Square, piece: Piece)
|
||||
proc removePiece(self: Chessboard, square: Square)
|
||||
proc spawnPiece(self: Chessboard, square: Square, piece: Piece)
|
||||
|
||||
|
||||
proc generatePawnMoves(self: Chessboard, moves: var MoveList, destinationMask: Bitboard) =
|
||||
let
|
||||
|
@ -71,58 +66,62 @@ proc generatePawnMoves(self: Chessboard, moves: var MoveList, destinationMask: B
|
|||
canDoublePush = canDoublePush.forwardRelativeTo(sideToMove) and not occupancy
|
||||
canDoublePush = canDoublePush.forwardRelativeTo(sideToMove) and not occupancy and destinationMask
|
||||
|
||||
for pawn in singlePushes and not orthogonalPins:
|
||||
for pawn in singlePushes:
|
||||
let pawnBB = pawn.toBitboard()
|
||||
if promotionRank.contains(pawn):
|
||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||
moves.add(createMove(pawnBB.backwardRelativeTo(sideToMove), pawn, promotion))
|
||||
else:
|
||||
moves.add(createMove(pawnBB.backwardRelativeTo(sideToMove), pawn))
|
||||
|
||||
for pawn in singlePushes and orthogonalPins:
|
||||
let pawnBB = pawn.toBitboard()
|
||||
if promotionRank.contains(pawn):
|
||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||
moves.add(createMove(pawnBB.backwardRelativeTo(sideToMove), pawn, promotion))
|
||||
else:
|
||||
moves.add(createMove(pawnBB.backwardRelativeTo(sideToMove), pawn))
|
||||
|
||||
for pawn in canDoublePush and orthogonalPins:
|
||||
moves.add(createMove(pawn.toBitboard().doubleBackwardRelativeTo(sideToMove), pawn, DoublePush))
|
||||
|
||||
for pawn in canDoublePush and not orthogonalPins:
|
||||
|
||||
for pawn in canDoublePush:
|
||||
moves.add(createMove(pawn.toBitboard().doubleBackwardRelativeTo(sideToMove), pawn, DoublePush))
|
||||
|
||||
let canCapture = pawns and not orthogonalPins
|
||||
var
|
||||
captureLeft = canCapture.forwardLeftRelativeTo(sideToMove)
|
||||
captureRight = canCapture.forwardRightRelativeTo(sideToMove)
|
||||
# If a piece is pinned on the right, it can only capture on the right and
|
||||
# vice versa for the left
|
||||
if (let capture = diagonalPins and captureLeft; capture) != 0:
|
||||
captureRight = Bitboard(0)
|
||||
captureLeft = capture
|
||||
if (let capture = diagonalPins and captureRight; capture) != 0:
|
||||
captureLeft = Bitboard(0)
|
||||
captureRight = capture
|
||||
# We mask off the non-enemy pieces and destination mask now because we need the unobstructed movement
|
||||
# mask to check for pins correctly
|
||||
captureLeft = captureLeft and enemyPieces and destinationMask
|
||||
captureRight = captureRight and enemyPieces and destinationMask
|
||||
for pawn in captureRight:
|
||||
let
|
||||
canCapture = pawns and not orthogonalPins
|
||||
canCaptureLeftUnpinned = (canCapture and not diagonalPins).forwardLeftRelativeTo(sideToMove) and enemyPieces and destinationMask
|
||||
canCaptureRightUnpinned = (canCapture and not diagonalPins).forwardRightRelativeTo(sideToMove) and enemyPieces and destinationMask
|
||||
|
||||
for pawn in canCaptureRightUnpinned:
|
||||
let pawnBB = pawn.toBitboard()
|
||||
if promotionRank.contains(pawn):
|
||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||
moves.add(createMove(pawnBB.backwardLeftRelativeTo(sideToMove), pawn, Capture, promotion))
|
||||
else:
|
||||
moves.add(createMove(pawnBB.backwardLeftRelativeTo(sideToMove), pawn, Capture))
|
||||
for pawn in captureLeft:
|
||||
|
||||
for pawn in canCaptureLeftUnpinned:
|
||||
let pawnBB = pawn.toBitboard()
|
||||
if promotionRank.contains(pawn):
|
||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||
moves.add(createMove(pawnBB.backwardRightRelativeTo(sideToMove), pawn, Capture, promotion))
|
||||
else:
|
||||
moves.add(createMove(pawnBB.backwardRightRelativeTo(sideToMove), pawn, Capture))
|
||||
|
||||
# Special cases for pawns pinned diagonally that can capture their pinners
|
||||
|
||||
let
|
||||
canCaptureLeft = canCapture.forwardLeftRelativeTo(sideToMove) and enemyPieces and destinationMask
|
||||
canCaptureRight = canCapture.forwardRightRelativeTo(sideToMove) and enemyPieces and destinationMask
|
||||
leftPinnedCanCapture = (canCaptureLeft and diagonalPins) and not canCaptureLeftUnpinned
|
||||
rightPinnedCanCapture = ((canCaptureRight and diagonalPins) and not canCaptureRightUnpinned) and not canCaptureRightUnpinned
|
||||
|
||||
for pawn in leftPinnedCanCapture:
|
||||
let pawnBB = pawn.toBitboard()
|
||||
if promotionRank.contains(pawn):
|
||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||
moves.add(createMove(pawnBB.backwardRightRelativeTo(sideToMove), pawn, Capture, promotion))
|
||||
else:
|
||||
moves.add(createMove(pawnBB.backwardRightRelativeTo(sideToMove), pawn, Capture))
|
||||
|
||||
for pawn in rightPinnedCanCapture:
|
||||
let pawnBB = pawn.toBitboard()
|
||||
if promotionRank.contains(pawn):
|
||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||
moves.add(createMove(pawnBB.backwardLeftRelativeTo(sideToMove), pawn, Capture, promotion))
|
||||
else:
|
||||
moves.add(createMove(pawnBB.backwardLeftRelativeTo(sideToMove), pawn, Capture))
|
||||
|
||||
# En passant captures
|
||||
var epBitboard = if epTarget != nullSquare(): epTarget.toBitboard() else: Bitboard(0)
|
||||
if epBitboard != 0:
|
||||
|
@ -150,7 +149,7 @@ proc generatePawnMoves(self: Chessboard, moves: var MoveList, destinationMask: B
|
|||
if not self.isOccupancyAttacked(friendlyKing, newOccupancy):
|
||||
# En passant does not create a check on the king: all good
|
||||
moves.add(createMove(friendlyPawn, epBitboard, EnPassant))
|
||||
self.addPieceToBitboard(epPawnSquare, Piece(kind: Pawn, color: nonSideToMove))
|
||||
self.spawnPiece(epPawnSquare, epPiece)
|
||||
if epRight != 0:
|
||||
# Note that this isn't going to be the same pawn from the previous if block!
|
||||
let
|
||||
|
@ -162,7 +161,7 @@ proc generatePawnMoves(self: Chessboard, moves: var MoveList, destinationMask: B
|
|||
if not self.isOccupancyAttacked(friendlyKing, newOccupancy):
|
||||
# En passant does not create a check on the king: all good
|
||||
moves.add(createMove(friendlyPawn, epBitboard, EnPassant))
|
||||
self.addPieceToBitboard(epPawnSquare, Piece(kind: Pawn, color: nonSideToMove))
|
||||
self.spawnPiece(epPawnSquare, epPiece)
|
||||
|
||||
|
||||
proc generateRookMoves(self: Chessboard, moves: var MoveList, destinationMask: Bitboard) =
|
||||
|
@ -321,56 +320,6 @@ proc generateMoves*(self: Chessboard, moves: var MoveList) =
|
|||
# Queens are just handled rooks + bishops
|
||||
|
||||
|
||||
|
||||
proc removePieceFromBitboard(self: Chessboard, square: Square) =
|
||||
## Removes a piece at the given square in the chessboard from
|
||||
## its respective bitboard
|
||||
let piece = self.getPiece(square)
|
||||
self.position.pieces[piece.color][piece.kind][].clearBit(square)
|
||||
|
||||
|
||||
proc addPieceToBitboard(self: Chessboard, square: Square, piece: Piece) =
|
||||
## Adds the given piece at the given square in the chessboard to
|
||||
## its respective bitboard
|
||||
self.position.pieces[piece.color][piece.kind][].setBit(square)
|
||||
|
||||
|
||||
proc spawnPiece(self: Chessboard, square: Square, piece: Piece) =
|
||||
## Internal helper to "spawn" a given piece at the given
|
||||
## square
|
||||
when not defined(danger):
|
||||
doAssert self.getPiece(square).kind == Empty
|
||||
self.addPieceToBitboard(square, piece)
|
||||
self.grid[square] = piece
|
||||
|
||||
|
||||
proc removePiece(self: Chessboard, square: Square) =
|
||||
## Removes a piece from the board, updating necessary
|
||||
## metadata
|
||||
when not defined(danger):
|
||||
let Piece = self.getPiece(square)
|
||||
doAssert piece.kind != Empty and piece.color != None, self.toFEN()
|
||||
self.removePieceFromBitboard(square)
|
||||
self.grid[square] = nullPiece()
|
||||
|
||||
|
||||
proc movePiece(self: Chessboard, move: Move) =
|
||||
## Internal helper to move a piece from
|
||||
## its current square to a target square
|
||||
let piece = self.grid[move.startSquare]
|
||||
when not defined(danger):
|
||||
let targetSquare = self.getPiece(move.targetSquare)
|
||||
if targetSquare.color != None:
|
||||
raise newException(AccessViolationDefect, &"{piece} at {move.startSquare} attempted to overwrite {targetSquare} at {move.targetSquare}: {move}")
|
||||
# Update positional metadata
|
||||
self.removePiece(move.startSquare)
|
||||
self.spawnPiece(move.targetSquare, piece)
|
||||
|
||||
|
||||
proc movePiece(self: Chessboard, startSquare, targetSquare: Square) =
|
||||
self.movePiece(createMove(startSquare, targetSquare))
|
||||
|
||||
|
||||
proc doMove*(self: Chessboard, move: Move) =
|
||||
## Internal function called by makeMove after
|
||||
## performing legality checks. Can be used in
|
||||
|
@ -381,7 +330,7 @@ proc doMove*(self: Chessboard, move: Move) =
|
|||
self.positions.add(self.position)
|
||||
|
||||
# Final checks
|
||||
let piece = self.grid[move.startSquare]
|
||||
let piece = self.getPiece(move.startSquare)
|
||||
when not defined(danger):
|
||||
doAssert piece.kind != Empty and piece.color != None, &"{move} {self.toFEN()}"
|
||||
|
||||
|
@ -424,10 +373,10 @@ proc doMove*(self: Chessboard, move: Move) =
|
|||
if move.isCastling():
|
||||
# Move the rook where it belongs
|
||||
if move.targetSquare == piece.kingSideCastling():
|
||||
let rook = self.grid[piece.color.kingSideRook()]
|
||||
let rook = self.getPiece(piece.color.kingSideRook())
|
||||
self.movePiece(piece.color.kingSideRook(), rook.kingSideCastling())
|
||||
if move.targetSquare == piece.queenSideCastling():
|
||||
let rook = self.grid[piece.color.queenSideRook()]
|
||||
let rook = self.getPiece(piece.color.queenSideRook())
|
||||
self.movePiece(piece.color.queenSideRook(), rook.queenSideCastling())
|
||||
|
||||
if piece.kind == Rook:
|
||||
|
|
Loading…
Reference in New Issue