Added knight movegen. Updated attack tracking. Fix bugs
This commit is contained in:
parent
3bb2cc7c66
commit
a07e9cc475
|
@ -112,30 +112,6 @@ func pretty*(self: Bitboard): string =
|
||||||
func `$`*(self: Bitboard): string = self.pretty()
|
func `$`*(self: Bitboard): string = self.pretty()
|
||||||
|
|
||||||
|
|
||||||
func computeDiagonalBitboards: array[14, Bitboard] {.compileTime.} =
|
|
||||||
## Precomputes all the bitboards for diagonals
|
|
||||||
result[0] = Bitboard(0x8040201008040201'u64)
|
|
||||||
var
|
|
||||||
col = 1
|
|
||||||
i = 0
|
|
||||||
# Left to right
|
|
||||||
while col < 8:
|
|
||||||
result[col] = Bitboard(0x8040201008040201'u64) shl (8 * col)
|
|
||||||
inc(col)
|
|
||||||
inc(i)
|
|
||||||
result[i] = Bitboard(0x102040810204080'u64)
|
|
||||||
inc(i)
|
|
||||||
col = 1
|
|
||||||
# Right to left
|
|
||||||
while col < 7:
|
|
||||||
result[i] = Bitboard(0x102040810204080'u64) shr (8 * col)
|
|
||||||
inc(i)
|
|
||||||
inc(col)
|
|
||||||
|
|
||||||
|
|
||||||
const diagonalBitboards = computeDiagonalBitboards()
|
|
||||||
|
|
||||||
|
|
||||||
func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Direction): Bitboard =
|
func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Direction): Bitboard =
|
||||||
## Get a bitmask relative to the given bitboard
|
## Get a bitmask relative to the given bitboard
|
||||||
## for the given direction for a piece of the
|
## for the given direction for a piece of the
|
||||||
|
@ -185,6 +161,8 @@ func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Directi
|
||||||
|
|
||||||
func getLastRank*(color: PieceColor): Bitboard = (if color == White: getRankMask(0) else: getRankMask(7))
|
func getLastRank*(color: PieceColor): Bitboard = (if color == White: getRankMask(0) else: getRankMask(7))
|
||||||
func getFirstRank*(color: PieceColor): Bitboard = (if color == White: getRankMask(7) else: getRankMask(0))
|
func getFirstRank*(color: PieceColor): Bitboard = (if color == White: getRankMask(7) else: getRankMask(0))
|
||||||
|
func getLeftmostFile*(color: PieceColor): Bitboard = (if color == White: getFileMask(0) else: getFileMask(7))
|
||||||
|
func getRightmostFile*(color: PieceColor): Bitboard = (if color == White: getFileMask(7) else: getFileMask(0))
|
||||||
|
|
||||||
|
|
||||||
func getDirectionMask*(square: Square, color: PieceColor, direction: Direction): Bitboard =
|
func getDirectionMask*(square: Square, color: PieceColor, direction: Direction): Bitboard =
|
||||||
|
@ -199,27 +177,104 @@ func doubleForwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self
|
||||||
func backwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Backward)
|
func backwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Backward)
|
||||||
func doubleBackwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.backwardRelativeTo(side).backwardRelativeTo(side)
|
func doubleBackwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.backwardRelativeTo(side).backwardRelativeTo(side)
|
||||||
|
|
||||||
func leftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Left) and not getFileMask(0)
|
func leftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Left) and not getRightmostFile(side)
|
||||||
func rightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Right) and not getFileMask(7)
|
func rightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Right) and not getLeftmostFile(side)
|
||||||
|
|
||||||
|
|
||||||
# We mask off the first and last ranks for
|
# We mask off the opposide files to make sure there are
|
||||||
# left and right movements respectively to
|
# no weird wraparounds when moving diagonally
|
||||||
# avoid weird wraparounds
|
|
||||||
func forwardRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
func forwardRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||||
getDirectionMask(self, side, ForwardRight) and not getFileMask(7)
|
getDirectionMask(self, side, ForwardRight) and not getLeftmostFile(side)
|
||||||
|
|
||||||
|
|
||||||
func forwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
func forwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||||
getDirectionMask(self, side, ForwardLeft) and not getFileMask(0)
|
getDirectionMask(self, side, ForwardLeft) and not getRightmostFile(side)
|
||||||
|
|
||||||
|
|
||||||
func backwardRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
func backwardRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||||
getDirectionMask(self, side, BackwardRight)
|
getDirectionMask(self, side, BackwardRight) and not getLeftmostFile(side)
|
||||||
|
|
||||||
|
|
||||||
func backwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
func backwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||||
getDirectionMask(self, side, BackwardLeft)
|
getDirectionMask(self, side, BackwardLeft) and not getRightmostFile(side)
|
||||||
|
|
||||||
|
|
||||||
# func forwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Forward)
|
func longKnightUpLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.doubleForwardRelativeTo(side).leftRelativeTo(side)
|
||||||
|
func longKnightUpRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.doubleForwardRelativeTo(side).rightRelativeTo(side)
|
||||||
|
func longKnightDownLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.doubleBackwardRelativeTo(side).leftRelativeTo(side)
|
||||||
|
func longKnightDownRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.doubleBackwardRelativeTo(side).rightRelativeTo(side)
|
||||||
|
|
||||||
|
func shortKnightUpLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.forwardRelativeTo(side).leftRelativeTo(side).leftRelativeTo(side)
|
||||||
|
func shortKnightUpRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.forwardRelativeTo(side).rightRelativeTo(side).rightRelativeTo(side)
|
||||||
|
func shortKnightDownLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.backwardRelativeTo(side).leftRelativeTo(side).leftRelativeTo(side)
|
||||||
|
func shortKnightDownRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = self.backwardRelativeTo(side).rightRelativeTo(side).rightRelativeTo(side)
|
||||||
|
|
||||||
|
# We precompute as much stuff as possible: lookup tables are fast!
|
||||||
|
|
||||||
|
func computeDiagonalBitboards: array[14, Bitboard] {.compileTime.} =
|
||||||
|
## Precomputes all the bitboards for diagonals
|
||||||
|
result[0] = Bitboard(0x8040201008040201'u64)
|
||||||
|
var
|
||||||
|
col = 1
|
||||||
|
i = 0
|
||||||
|
# Left to right
|
||||||
|
while col < 8:
|
||||||
|
result[col] = Bitboard(0x8040201008040201'u64) shl (8 * col)
|
||||||
|
inc(col)
|
||||||
|
inc(i)
|
||||||
|
result[i] = Bitboard(0x102040810204080'u64)
|
||||||
|
inc(i)
|
||||||
|
col = 1
|
||||||
|
# Right to left
|
||||||
|
while col < 7:
|
||||||
|
result[i] = Bitboard(0x102040810204080'u64) shr (8 * col)
|
||||||
|
inc(i)
|
||||||
|
inc(col)
|
||||||
|
|
||||||
|
|
||||||
|
func computeKingBitboards: array[64, Bitboard] =
|
||||||
|
## Precomputes all the movement bitboards for the king
|
||||||
|
for i in 0'u64..63:
|
||||||
|
let king = i.toBitboard()
|
||||||
|
# It doesn't really matter which side we generate
|
||||||
|
# the move for, they're identical for both
|
||||||
|
var movements = king.forwardRelativeTo(White)
|
||||||
|
movements = movements or king.forwardLeftRelativeTo(White)
|
||||||
|
movements = movements or king.leftRelativeTo(White)
|
||||||
|
movements = movements or king.rightRelativeTo(White)
|
||||||
|
movements = movements or king.backwardRelativeTo(White)
|
||||||
|
movements = movements or king.forwardLeftRelativeTo(White)
|
||||||
|
movements = movements or king.backwardRightRelativeTo(White)
|
||||||
|
movements = movements or king.backwardLeftRelativeTo(White)
|
||||||
|
# We don't *need* to mask the king off: the engine already masks off
|
||||||
|
# the board's occupancy when generating moves, but it may be useful for
|
||||||
|
# other parts of the movegen for this stuff not to say "the king can just
|
||||||
|
# stay still", so we do it anyway
|
||||||
|
movements = movements and not king
|
||||||
|
result[i] = movements
|
||||||
|
|
||||||
|
|
||||||
|
func computeKnightBitboards: array[64, Bitboard] =
|
||||||
|
## Precomputes all the movement bitboards for knights
|
||||||
|
for i in 0'u64..63:
|
||||||
|
let knight = i.toBitboard()
|
||||||
|
# It doesn't really matter which side we generate
|
||||||
|
# the move for, they're identical for both
|
||||||
|
var movements = knight.longKnightDownLeftRelativeTo(White)
|
||||||
|
movements = movements or knight.longKnightDownRightRelativeTo(White)
|
||||||
|
movements = movements or knight.longKnightUpLeftRelativeTo(White)
|
||||||
|
movements = movements or knight.longKnightUpRightRelativeTo(White)
|
||||||
|
movements = movements or knight.shortKnightDownLeftRelativeTo(White)
|
||||||
|
movements = movements or knight.shortKnightDownRightRelativeTo(White)
|
||||||
|
movements = movements or knight.shortKnightUpLeftRelativeTo(White)
|
||||||
|
movements = movements or knight.shortKnightUpRightRelativeTo(White)
|
||||||
|
result[i] = movements
|
||||||
|
|
||||||
|
|
||||||
|
# Precomputing stuff is *very* helpful for chess, it turns out
|
||||||
|
const DIAGONAL_BITBOARDS* = computeDiagonalBitboards()
|
||||||
|
# For some reason nim freaks out if I try to call computeKingBitboards()
|
||||||
|
# at compile-time. ¯\_(ツ)_/¯
|
||||||
|
let
|
||||||
|
KING_BITBOARDS* = computeKingBitboards()
|
||||||
|
KNIGHT_BITBOARDS* = computeKnightBitboards()
|
||||||
|
|
|
@ -539,6 +539,29 @@ proc getPawnAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bit
|
||||||
return pawns and (bottomLeft or bottomRight)
|
return pawns and (bottomLeft or bottomRight)
|
||||||
|
|
||||||
|
|
||||||
|
proc getKingAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
||||||
|
## Returns the attack bitboard for the given square from
|
||||||
|
## the king of the given side
|
||||||
|
result = Bitboard(0)
|
||||||
|
let
|
||||||
|
sq = square.toBitboard()
|
||||||
|
king = self.getBitboard(King, attacker)
|
||||||
|
if (sq and KING_BITBOARDS[square.uint64]) != 0:
|
||||||
|
result = result or sq
|
||||||
|
|
||||||
|
|
||||||
|
proc getKnightAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
||||||
|
## Returns the attack bitboard for the given square from
|
||||||
|
## the knights of the given side
|
||||||
|
let
|
||||||
|
sq = square.toBitboard()
|
||||||
|
knights = self.getBitboard(Knight, attacker)
|
||||||
|
result = Bitboard(0)
|
||||||
|
for knight in knights:
|
||||||
|
if (sq and KNIGHT_BITBOARDS[knight.uint64]) != 0:
|
||||||
|
result = result or knight.toBitboard()
|
||||||
|
|
||||||
|
|
||||||
proc getAttacksTo(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
proc getAttacksTo(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
||||||
## Computes the attack bitboard for the given square from
|
## Computes the attack bitboard for the given square from
|
||||||
## the given side
|
## the given side
|
||||||
|
@ -546,8 +569,17 @@ proc getAttacksTo(self: ChessBoard, square: Square, attacker: PieceColor): Bitbo
|
||||||
let
|
let
|
||||||
squareBitboard = square.toBitboard()
|
squareBitboard = square.toBitboard()
|
||||||
result = result or self.getPawnAttacks(square, attacker)
|
result = result or self.getPawnAttacks(square, attacker)
|
||||||
|
result = result or self.getKingAttacks(square, attacker)
|
||||||
|
result = result or self.getKnightAttacks(square, attacker)
|
||||||
|
|
||||||
|
|
||||||
|
proc getCapturablePieces(self: ChessBoard, side: PieceColor): Bitboard {.inline.} =
|
||||||
|
## Returns the set of pieces of the given color that can
|
||||||
|
## be captured
|
||||||
|
|
||||||
|
# Just a handy helper to filter out the king and avoid code duplication
|
||||||
|
return self.getOccupancyFor(side) and not self.getBitboard(King, side)
|
||||||
|
|
||||||
|
|
||||||
proc generatePawnMovements(self: ChessBoard, moves: var MoveList) =
|
proc generatePawnMovements(self: ChessBoard, moves: var MoveList) =
|
||||||
## Helper of generatePawnMoves for generating all non-capture
|
## Helper of generatePawnMoves for generating all non-capture
|
||||||
|
@ -576,7 +608,7 @@ proc generatePawnCaptures(self: ChessBoard, moves: var MoveList) =
|
||||||
nonSideToMove = sideToMove.opposite()
|
nonSideToMove = sideToMove.opposite()
|
||||||
pawns = self.getBitboard(Pawn, sideToMove)
|
pawns = self.getBitboard(Pawn, sideToMove)
|
||||||
# We can only capture enemy pieces (except the king)
|
# We can only capture enemy pieces (except the king)
|
||||||
enemyPieces = self.getOccupancyFor(nonSideToMove) and not self.getBitboard(King, nonSideToMove)
|
enemyPieces = self.getCapturablePieces(nonSideToMove)
|
||||||
rightMovement = pawns.forwardRightRelativeTo(sideToMove)
|
rightMovement = pawns.forwardRightRelativeTo(sideToMove)
|
||||||
leftMovement = pawns.forwardLeftRelativeTo(sideToMove)
|
leftMovement = pawns.forwardLeftRelativeTo(sideToMove)
|
||||||
epTarget = self.getEnPassantTarget()
|
epTarget = self.getEnPassantTarget()
|
||||||
|
@ -625,26 +657,33 @@ proc generateKingMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
let
|
let
|
||||||
sideToMove = self.getSideToMove()
|
sideToMove = self.getSideToMove()
|
||||||
king = self.getBitboard(King, sideToMove)
|
king = self.getBitboard(King, sideToMove)
|
||||||
|
moveIdx = king.toSquare().uint64
|
||||||
allowedSquares = not self.getOccupancy()
|
allowedSquares = not self.getOccupancy()
|
||||||
nonSideToMove = sideToMove.opposite()
|
nonSideToMove = sideToMove.opposite()
|
||||||
enemyPieces = self.getOccupancyFor(nonSideToMove) and not self.getBitboard(King, nonSideToMove)
|
enemyPieces = self.getCapturablePieces(nonSideToMove)
|
||||||
var movements = king.forwardRelativeTo(sideToMove)
|
|
||||||
movements = movements or king.rightRelativeTo(sideToMove)
|
|
||||||
movements = movements or king.backwardRelativeTo(sideToMove)
|
|
||||||
movements = movements or king.leftRelativeTo(sideToMove)
|
|
||||||
movements = movements or king.forwardRightRelativeTo(sideToMove)
|
|
||||||
movements = movements or king.forwardLeftRelativeTo(sideToMove)
|
|
||||||
# Regular moves
|
# Regular moves
|
||||||
for square in movements and allowedSquares:
|
for square in KING_BITBOARDS[moveIdx] and allowedSquares:
|
||||||
moves.add(createMove(king, square))
|
moves.add(createMove(king, square))
|
||||||
# Captures
|
# Captures
|
||||||
for square in movements and enemyPieces:
|
for square in KING_BITBOARDS[moveIdx] and enemyPieces:
|
||||||
moves.add(createMove(king, square, Capture))
|
moves.add(createMove(king, square, Capture))
|
||||||
|
|
||||||
|
|
||||||
proc generateKnightMoves(self: ChessBoard, moves: var MoveList)=
|
proc generateKnightMoves(self: ChessBoard, moves: var MoveList)=
|
||||||
## Generates all the legal knight moves for the side to move
|
## Generates all the legal knight moves for the side to move
|
||||||
|
let
|
||||||
|
sideToMove = self.getSideToMove()
|
||||||
|
knights = self.getBitboard(Knight, sideToMove)
|
||||||
|
allowedSquares = not self.getOccupancy()
|
||||||
|
nonSideToMove = sideToMove.opposite()
|
||||||
|
enemyPieces = self.getCapturablePieces(nonSideToMove)
|
||||||
|
for square in knights:
|
||||||
|
# Regular moves
|
||||||
|
for target in KNIGHT_BITBOARDS[square.uint64] and allowedSquares:
|
||||||
|
moves.add(createMove(square, target))
|
||||||
|
# Captures
|
||||||
|
for target in KNIGHT_BITBOARDS[square.uint64] and enemyPieces:
|
||||||
|
moves.add(createMove(square, target, Capture))
|
||||||
|
|
||||||
|
|
||||||
proc checkInsufficientMaterialPieceCount(self: ChessBoard, color: PieceColor): bool =
|
proc checkInsufficientMaterialPieceCount(self: ChessBoard, color: PieceColor): bool =
|
||||||
|
@ -718,6 +757,7 @@ proc generateMoves*(self: ChessBoard, moves: var MoveList) =
|
||||||
# TODO: Check for repetitions (requires zobrist hashing + table)
|
# TODO: Check for repetitions (requires zobrist hashing + table)
|
||||||
self.generatePawnMoves(moves)
|
self.generatePawnMoves(moves)
|
||||||
self.generateKingMoves(moves)
|
self.generateKingMoves(moves)
|
||||||
|
self.generateKnightMoves(moves)
|
||||||
# TODO: all pieces
|
# TODO: all pieces
|
||||||
|
|
||||||
|
|
||||||
|
@ -1755,13 +1795,12 @@ when isMainModule:
|
||||||
testPieceBitboard(blackQueens, blackQueenSquares)
|
testPieceBitboard(blackQueens, blackQueenSquares)
|
||||||
testPieceBitboard(blackKing, blackKingSquares)
|
testPieceBitboard(blackKing, blackKingSquares)
|
||||||
|
|
||||||
b = newChessboardFromFEN("B7/P1P1P1P1/1P1P1P1P/7k/8/8/8/3K4 w - - 0 1")
|
|
||||||
b.doMove(createMove("d1", "e1"))
|
|
||||||
var m = MoveList()
|
var m = MoveList()
|
||||||
b.generateMoves(m)
|
b.generateMoves(m)
|
||||||
echo &"Legal moves for {b.getSideToMove()} at {b.toFEN()}: "
|
echo &"There are {len(m)} legal moves for {b.getSideToMove()} at {b.toFEN()}: "
|
||||||
for move in m:
|
for move in m:
|
||||||
echo " - ", move.startSquare, move.targetSquare, " ", move.getFlags()
|
echo " - ", move.startSquare, move.targetSquare, " ", move.getFlags()
|
||||||
echo b.pretty()
|
echo b.pretty()
|
||||||
|
echo b.getAttacksTo("f3".toSquare(), White)
|
||||||
# setControlCHook(proc () {.noconv.} = quit(0))
|
# setControlCHook(proc () {.noconv.} = quit(0))
|
||||||
# quit(main())
|
# quit(main())
|
||||||
|
|
Loading…
Reference in New Issue