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.} =
|
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
|
## in the given bitboard as a square
|
||||||
result = Square(self.countTrailingZeroBits().uint8)
|
result = Square(self.countTrailingZeroBits().uint8)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func getFileMask*(file: int): Bitboard = Bitboard(0x101010101010101'u64) shl file.uint64
|
func getFileMask*(file: int): Bitboard = Bitboard(0x101010101010101'u64) shl file.uint64
|
||||||
func getRankMask*(rank: int): Bitboard = Bitboard(0xff) shl uint64(8 * rank)
|
func getRankMask*(rank: int): Bitboard = Bitboard(0xff) shl uint64(8 * rank)
|
||||||
func toBitboard*(square: SomeInteger): Bitboard = Bitboard(1'u64) shl square.uint64
|
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()
|
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.} =
|
func getPiece*(self: Chessboard, square: Square): Piece {.inline.} =
|
||||||
## Gets the piece at the given square
|
## Gets the piece at the given square
|
||||||
return self.grid[square]
|
return self.grid[square]
|
||||||
|
@ -202,6 +196,61 @@ func getPiece*(self: Chessboard, square: string): Piece {.inline.} =
|
||||||
return self.getPiece(square.toSquare())
|
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 =
|
func getOccupancyFor*(self: Chessboard, color: PieceColor): Bitboard =
|
||||||
## Get the occupancy bitboard for every piece of the given color
|
## Get the occupancy bitboard for every piece of the given color
|
||||||
result = Bitboard(0)
|
result = Bitboard(0)
|
||||||
|
|
|
@ -31,11 +31,6 @@ import misc
|
||||||
export bitboards, magics, pieces, moves, position, rays, misc, board
|
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) =
|
proc generatePawnMoves(self: Chessboard, moves: var MoveList, destinationMask: Bitboard) =
|
||||||
let
|
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
|
||||||
canDoublePush = canDoublePush.forwardRelativeTo(sideToMove) and not occupancy and destinationMask
|
canDoublePush = canDoublePush.forwardRelativeTo(sideToMove) and not occupancy and destinationMask
|
||||||
|
|
||||||
for pawn in singlePushes and not orthogonalPins:
|
for pawn in singlePushes:
|
||||||
let pawnBB = pawn.toBitboard()
|
let pawnBB = pawn.toBitboard()
|
||||||
if promotionRank.contains(pawn):
|
if promotionRank.contains(pawn):
|
||||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||||
moves.add(createMove(pawnBB.backwardRelativeTo(sideToMove), pawn, promotion))
|
moves.add(createMove(pawnBB.backwardRelativeTo(sideToMove), pawn, promotion))
|
||||||
else:
|
else:
|
||||||
moves.add(createMove(pawnBB.backwardRelativeTo(sideToMove), pawn))
|
moves.add(createMove(pawnBB.backwardRelativeTo(sideToMove), pawn))
|
||||||
|
|
||||||
for pawn in singlePushes and orthogonalPins:
|
for pawn in canDoublePush:
|
||||||
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:
|
|
||||||
moves.add(createMove(pawn.toBitboard().doubleBackwardRelativeTo(sideToMove), pawn, DoublePush))
|
moves.add(createMove(pawn.toBitboard().doubleBackwardRelativeTo(sideToMove), pawn, DoublePush))
|
||||||
|
|
||||||
let canCapture = pawns and not orthogonalPins
|
let
|
||||||
var
|
canCapture = pawns and not orthogonalPins
|
||||||
captureLeft = canCapture.forwardLeftRelativeTo(sideToMove)
|
canCaptureLeftUnpinned = (canCapture and not diagonalPins).forwardLeftRelativeTo(sideToMove) and enemyPieces and destinationMask
|
||||||
captureRight = canCapture.forwardRightRelativeTo(sideToMove)
|
canCaptureRightUnpinned = (canCapture and not diagonalPins).forwardRightRelativeTo(sideToMove) and enemyPieces and destinationMask
|
||||||
# If a piece is pinned on the right, it can only capture on the right and
|
|
||||||
# vice versa for the left
|
for pawn in canCaptureRightUnpinned:
|
||||||
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 pawnBB = pawn.toBitboard()
|
let pawnBB = pawn.toBitboard()
|
||||||
if promotionRank.contains(pawn):
|
if promotionRank.contains(pawn):
|
||||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||||
moves.add(createMove(pawnBB.backwardLeftRelativeTo(sideToMove), pawn, Capture, promotion))
|
moves.add(createMove(pawnBB.backwardLeftRelativeTo(sideToMove), pawn, Capture, promotion))
|
||||||
else:
|
else:
|
||||||
moves.add(createMove(pawnBB.backwardLeftRelativeTo(sideToMove), pawn, Capture))
|
moves.add(createMove(pawnBB.backwardLeftRelativeTo(sideToMove), pawn, Capture))
|
||||||
for pawn in captureLeft:
|
|
||||||
|
for pawn in canCaptureLeftUnpinned:
|
||||||
let pawnBB = pawn.toBitboard()
|
let pawnBB = pawn.toBitboard()
|
||||||
if promotionRank.contains(pawn):
|
if promotionRank.contains(pawn):
|
||||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||||
moves.add(createMove(pawnBB.backwardRightRelativeTo(sideToMove), pawn, Capture, promotion))
|
moves.add(createMove(pawnBB.backwardRightRelativeTo(sideToMove), pawn, Capture, promotion))
|
||||||
else:
|
else:
|
||||||
moves.add(createMove(pawnBB.backwardRightRelativeTo(sideToMove), pawn, Capture))
|
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
|
# En passant captures
|
||||||
var epBitboard = if epTarget != nullSquare(): epTarget.toBitboard() else: Bitboard(0)
|
var epBitboard = if epTarget != nullSquare(): epTarget.toBitboard() else: Bitboard(0)
|
||||||
if epBitboard != 0:
|
if epBitboard != 0:
|
||||||
|
@ -150,7 +149,7 @@ proc generatePawnMoves(self: Chessboard, moves: var MoveList, destinationMask: B
|
||||||
if not self.isOccupancyAttacked(friendlyKing, newOccupancy):
|
if not self.isOccupancyAttacked(friendlyKing, newOccupancy):
|
||||||
# En passant does not create a check on the king: all good
|
# En passant does not create a check on the king: all good
|
||||||
moves.add(createMove(friendlyPawn, epBitboard, EnPassant))
|
moves.add(createMove(friendlyPawn, epBitboard, EnPassant))
|
||||||
self.addPieceToBitboard(epPawnSquare, Piece(kind: Pawn, color: nonSideToMove))
|
self.spawnPiece(epPawnSquare, epPiece)
|
||||||
if epRight != 0:
|
if epRight != 0:
|
||||||
# Note that this isn't going to be the same pawn from the previous if block!
|
# Note that this isn't going to be the same pawn from the previous if block!
|
||||||
let
|
let
|
||||||
|
@ -162,7 +161,7 @@ proc generatePawnMoves(self: Chessboard, moves: var MoveList, destinationMask: B
|
||||||
if not self.isOccupancyAttacked(friendlyKing, newOccupancy):
|
if not self.isOccupancyAttacked(friendlyKing, newOccupancy):
|
||||||
# En passant does not create a check on the king: all good
|
# En passant does not create a check on the king: all good
|
||||||
moves.add(createMove(friendlyPawn, epBitboard, EnPassant))
|
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) =
|
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
|
# 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) =
|
proc doMove*(self: Chessboard, move: Move) =
|
||||||
## Internal function called by makeMove after
|
## Internal function called by makeMove after
|
||||||
## performing legality checks. Can be used in
|
## performing legality checks. Can be used in
|
||||||
|
@ -381,7 +330,7 @@ proc doMove*(self: Chessboard, move: Move) =
|
||||||
self.positions.add(self.position)
|
self.positions.add(self.position)
|
||||||
|
|
||||||
# Final checks
|
# Final checks
|
||||||
let piece = self.grid[move.startSquare]
|
let piece = self.getPiece(move.startSquare)
|
||||||
when not defined(danger):
|
when not defined(danger):
|
||||||
doAssert piece.kind != Empty and piece.color != None, &"{move} {self.toFEN()}"
|
doAssert piece.kind != Empty and piece.color != None, &"{move} {self.toFEN()}"
|
||||||
|
|
||||||
|
@ -424,10 +373,10 @@ proc doMove*(self: Chessboard, move: Move) =
|
||||||
if move.isCastling():
|
if move.isCastling():
|
||||||
# Move the rook where it belongs
|
# Move the rook where it belongs
|
||||||
if move.targetSquare == piece.kingSideCastling():
|
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())
|
self.movePiece(piece.color.kingSideRook(), rook.kingSideCastling())
|
||||||
if move.targetSquare == piece.queenSideCastling():
|
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())
|
self.movePiece(piece.color.queenSideRook(), rook.queenSideCastling())
|
||||||
|
|
||||||
if piece.kind == Rook:
|
if piece.kind == Rook:
|
||||||
|
|
Loading…
Reference in New Issue