Add king move generation
This commit is contained in:
parent
86265c68f0
commit
3bb2cc7c66
|
@ -154,9 +154,11 @@ func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Directi
|
||||||
of BackwardRight:
|
of BackwardRight:
|
||||||
return bitboard shl 9
|
return bitboard shl 9
|
||||||
of BackwardLeft:
|
of BackwardLeft:
|
||||||
return bitboard shr 7
|
return bitboard shl 7
|
||||||
else:
|
of Left:
|
||||||
discard
|
return bitboard shr 1
|
||||||
|
of Right:
|
||||||
|
return bitboard shl 1
|
||||||
of Black:
|
of Black:
|
||||||
# The directions for black are just the opposite of those for white,
|
# The directions for black are just the opposite of those for white,
|
||||||
# so we avoid duplicating any code
|
# so we avoid duplicating any code
|
||||||
|
@ -172,14 +174,18 @@ func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Directi
|
||||||
of BackwardRight:
|
of BackwardRight:
|
||||||
return bitboard shr 9
|
return bitboard shr 9
|
||||||
of BackwardLeft:
|
of BackwardLeft:
|
||||||
return bitboard shl 7
|
return bitboard shr 7
|
||||||
else:
|
of Left:
|
||||||
discard
|
return bitboard shl 1
|
||||||
|
of Right:
|
||||||
|
return bitboard shr 1
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
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 getDirectionMask*(square: Square, color: PieceColor, direction: Direction): Bitboard =
|
func getDirectionMask*(square: Square, color: PieceColor, direction: Direction): Bitboard =
|
||||||
## Get a bitmask for the given direction for a piece
|
## Get a bitmask for the given direction for a piece
|
||||||
|
@ -193,6 +199,9 @@ 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 rightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Right) and not getFileMask(7)
|
||||||
|
|
||||||
|
|
||||||
# We mask off the first and last ranks for
|
# We mask off the first and last ranks for
|
||||||
# left and right movements respectively to
|
# left and right movements respectively to
|
||||||
|
@ -205,9 +214,12 @@ func forwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||||
getDirectionMask(self, side, ForwardLeft) and not getFileMask(0)
|
getDirectionMask(self, side, ForwardLeft) and not getFileMask(0)
|
||||||
|
|
||||||
|
|
||||||
func bottomRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
func backwardRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||||
getDirectionMask(self, side, BackwardRight) and not getFileMask(7)
|
getDirectionMask(self, side, BackwardRight)
|
||||||
|
|
||||||
|
|
||||||
func bottomLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
func backwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||||
getDirectionMask(self, side, BackwardLeft) and not getFileMask(0)
|
getDirectionMask(self, side, BackwardLeft)
|
||||||
|
|
||||||
|
|
||||||
|
# func forwardRelativeTo*(self: Bitboard, side: PieceColor): Bitboard = getDirectionMask(self, side, Forward)
|
||||||
|
|
|
@ -260,7 +260,7 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
|
||||||
of Queen:
|
of Queen:
|
||||||
result.position.pieces.black.queens.uint64.setBit(bitIndex)
|
result.position.pieces.black.queens.uint64.setBit(bitIndex)
|
||||||
of King:
|
of King:
|
||||||
if result.position.pieces.black.king != Bitboard(0'u64):
|
if result.position.pieces.black.king != 0:
|
||||||
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
|
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
|
||||||
result.position.pieces.black.king.uint64.setBit(bitIndex)
|
result.position.pieces.black.king.uint64.setBit(bitIndex)
|
||||||
else:
|
else:
|
||||||
|
@ -360,7 +360,7 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
|
||||||
#[if result.inCheck(result.getSideToMove().opposite):
|
#[if result.inCheck(result.getSideToMove().opposite):
|
||||||
# Opponent king cannot be captured on the next move
|
# 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.pieces.white.king == Bitboard(0) or result.position.pieces.black.king == Bitboard(0):
|
if result.position.pieces.white.king == 0 or result.position.pieces.black.king == 0:
|
||||||
# Both kings must be on the board
|
# Both kings must be on the board
|
||||||
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
|
raise newException(ValueError, "invalid position: exactly one king of each color must be present")
|
||||||
|
|
||||||
|
@ -492,17 +492,17 @@ func getFlags*(move: Move): seq[MoveFlag] =
|
||||||
result.add(Default)
|
result.add(Default)
|
||||||
|
|
||||||
|
|
||||||
func getKing(self: ChessBoard, color: PieceColor = None): Square {.inline.} =
|
func getKingSquare(self: ChessBoard, color: PieceColor = None): Square {.inline.} =
|
||||||
## Returns the square of the king for the given
|
## Returns the square of the king for the given
|
||||||
## color (if it is None, the active color is used)
|
## side (if it is None, the side to move is used)
|
||||||
var color = color
|
var color = color
|
||||||
if color == None:
|
if color == None:
|
||||||
color = self.getSideToMove()
|
color = self.getSideToMove()
|
||||||
case color:
|
case color:
|
||||||
of White:
|
of White:
|
||||||
return Square(self.position.pieces.white.king.uint64.countTrailingZeroBits())
|
return self.position.pieces.white.king.toSquare()
|
||||||
of Black:
|
of Black:
|
||||||
return Square(self.position.pieces.black.king.uint64.countTrailingZeroBits())
|
return self.position.pieces.black.king.toSquare()
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
@ -527,6 +527,28 @@ proc getOccupancy(self: ChessBoard): Bitboard =
|
||||||
result = self.getOccupancyFor(Black) or self.getOccupancyFor(White)
|
result = self.getOccupancyFor(Black) or self.getOccupancyFor(White)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
proc getPawnAttacks(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
||||||
|
## Returns the attack bitboard for the given square from
|
||||||
|
## the pawns of the given side
|
||||||
|
let
|
||||||
|
sq = square.toBitboard()
|
||||||
|
pawns = self.getBitboard(Pawn, attacker)
|
||||||
|
bottomLeft = sq.backwardLeftRelativeTo(attacker)
|
||||||
|
bottomRight = sq.backwardRightRelativeTo(attacker)
|
||||||
|
return pawns and (bottomLeft or bottomRight)
|
||||||
|
|
||||||
|
|
||||||
|
proc getAttacksTo(self: ChessBoard, square: Square, attacker: PieceColor): Bitboard =
|
||||||
|
## Computes the attack bitboard for the given square from
|
||||||
|
## the given side
|
||||||
|
result = Bitboard(0)
|
||||||
|
let
|
||||||
|
squareBitboard = square.toBitboard()
|
||||||
|
result = result or self.getPawnAttacks(square, attacker)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
## and non-promotion pawn moves
|
## and non-promotion pawn moves
|
||||||
|
@ -536,13 +558,13 @@ proc generatePawnMovements(self: ChessBoard, moves: var MoveList) =
|
||||||
# We can only move to squares that are *not* occupied by another piece.
|
# We can only move to squares that are *not* occupied by another piece.
|
||||||
# We also cannot move to the last rank, as that will result in a promotion
|
# We also cannot move to the last rank, as that will result in a promotion
|
||||||
# and is handled elsewhere
|
# and is handled elsewhere
|
||||||
occupancy = not (self.getOccupancy() or sideToMove.getLastRank())
|
allowedSquares = not (self.getOccupancy() or sideToMove.getLastRank())
|
||||||
# Single push
|
# Single push
|
||||||
for square in pawns.forwardRelativeTo(sideToMove) and occupancy:
|
for square in pawns.forwardRelativeTo(sideToMove) and allowedSquares:
|
||||||
moves.add(createMove(square.toBitboard().backwardRelativeTo(sideToMove), square))
|
moves.add(createMove(square.toBitboard().backwardRelativeTo(sideToMove), square))
|
||||||
# Double push
|
# Double push
|
||||||
let rank = if sideToMove == White: getRankMask(6) else: getRankMask(1) # Only pawns on their starting rank can double push
|
let rank = if sideToMove == White: getRankMask(6) else: getRankMask(1) # Only pawns on their starting rank can double push
|
||||||
for square in (pawns and rank).doubleForwardRelativeTo(sideToMove) and occupancy:
|
for square in (pawns and rank).doubleForwardRelativeTo(sideToMove) and allowedSquares:
|
||||||
moves.add(createMove(square.toBitboard().doubleBackwardRelativeTo(sideToMove), square, DoublePush))
|
moves.add(createMove(square.toBitboard().doubleBackwardRelativeTo(sideToMove), square, DoublePush))
|
||||||
|
|
||||||
|
|
||||||
|
@ -553,17 +575,18 @@ proc generatePawnCaptures(self: ChessBoard, moves: var MoveList) =
|
||||||
sideToMove = self.getSideToMove()
|
sideToMove = self.getSideToMove()
|
||||||
nonSideToMove = sideToMove.opposite()
|
nonSideToMove = sideToMove.opposite()
|
||||||
pawns = self.getBitboard(Pawn, sideToMove)
|
pawns = self.getBitboard(Pawn, sideToMove)
|
||||||
# We can only capture enemy pieces
|
# We can only capture enemy pieces (except the king)
|
||||||
enemyPieces = self.getOccupancyFor(nonSideToMove)
|
enemyPieces = self.getOccupancyFor(nonSideToMove) and not self.getBitboard(King, nonSideToMove)
|
||||||
rightMovement = pawns.forwardRightRelativeTo(sideToMove)
|
rightMovement = pawns.forwardRightRelativeTo(sideToMove)
|
||||||
leftMovement = pawns.forwardLeftRelativeTo(sideToMove)
|
leftMovement = pawns.forwardLeftRelativeTo(sideToMove)
|
||||||
epTarget = self.getEnPassantTarget()
|
epTarget = self.getEnPassantTarget()
|
||||||
let epBitboard = if (epTarget != nullSquare()): epTarget.toBitboard() else: Bitboard(0)
|
let epBitboard = if (epTarget != nullSquare()): epTarget.toBitboard() else: Bitboard(0)
|
||||||
# Top right attacks
|
# Top right attacks
|
||||||
for square in rightMovement and enemyPieces:
|
for square in rightMovement and enemyPieces:
|
||||||
moves.add(createMove(square.toBitboard().bottomLeftRelativeTo(sideToMove), square, Capture))
|
moves.add(createMove(square.toBitboard().backwardLeftRelativeTo(sideToMove), square, Capture))
|
||||||
|
# Top left attacks
|
||||||
for square in leftMovement and enemyPieces:
|
for square in leftMovement and enemyPieces:
|
||||||
moves.add(createMove(square.toBitboard().bottomRightRelativeTo(sideToMove), square, Capture))
|
moves.add(createMove(square.toBitboard().backwardRightRelativeTo(sideToMove), square, Capture))
|
||||||
# Special case for en passant
|
# Special case for en passant
|
||||||
let
|
let
|
||||||
epLeft = epBitboard and leftMovement
|
epLeft = epBitboard and leftMovement
|
||||||
|
@ -580,31 +603,47 @@ proc generatePawnPromotions(self: ChessBoard, moves: var MoveList) =
|
||||||
let
|
let
|
||||||
sideToMove = self.getSideToMove()
|
sideToMove = self.getSideToMove()
|
||||||
pawns = self.getBitboard(Pawn, sideToMove)
|
pawns = self.getBitboard(Pawn, sideToMove)
|
||||||
occupancy = not self.getOccupancy()
|
occupancy = self.getOccupancy()
|
||||||
for square in pawns.forwardRelativeTo(sideToMove) and occupancy and sideToMove.getLastRank():
|
for square in pawns.forwardRelativeTo(sideToMove) and not occupancy and sideToMove.getLastRank():
|
||||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||||
moves.add(createMove(square.toBitboard().backwardRelativeTo(sideToMove), square, promotion))
|
moves.add(createMove(square.toBitboard().backwardRelativeTo(sideToMove), square, promotion))
|
||||||
|
|
||||||
|
|
||||||
proc generatePawnMoves(self: ChessBoard, moves: var MoveList) =
|
proc generatePawnMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
## Generates the possible moves for the pawn in the given
|
## Generates all the legal pawn moves for the side to move
|
||||||
## square
|
|
||||||
|
|
||||||
self.generatePawnMovements(moves)
|
self.generatePawnMovements(moves)
|
||||||
self.generatePawnCaptures(moves)
|
self.generatePawnCaptures(moves)
|
||||||
self.generatePawnPromotions(moves)
|
self.generatePawnPromotions(moves)
|
||||||
|
|
||||||
|
|
||||||
proc generateSlidingMoves(self: ChessBoard, square: Square): seq[Move] =
|
proc generateSlidingMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
## Generates moves for the sliding piece in the given square
|
## Generates all legal sliding moves for the side to move
|
||||||
|
|
||||||
|
|
||||||
|
proc generateKingMoves(self: ChessBoard, moves: var MoveList) =
|
||||||
|
## Generates all legal king moves for the side to move
|
||||||
|
let
|
||||||
|
sideToMove = self.getSideToMove()
|
||||||
|
king = self.getBitboard(King, sideToMove)
|
||||||
|
allowedSquares = not self.getOccupancy()
|
||||||
|
nonSideToMove = sideToMove.opposite()
|
||||||
|
enemyPieces = self.getOccupancyFor(nonSideToMove) and not self.getBitboard(King, 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
|
||||||
|
for square in movements and allowedSquares:
|
||||||
|
moves.add(createMove(king, square))
|
||||||
|
# Captures
|
||||||
|
for square in movements and enemyPieces:
|
||||||
|
moves.add(createMove(king, square, Capture))
|
||||||
|
|
||||||
|
|
||||||
proc generateKingMoves(self: ChessBoard, square: Square): seq[Move] =
|
proc generateKnightMoves(self: ChessBoard, moves: var MoveList)=
|
||||||
## Generates moves for the king in the given square
|
## Generates all the legal knight moves for the side to move
|
||||||
|
|
||||||
|
|
||||||
proc generateKnightMoves(self: ChessBoard, square: Square): seq[Move] =
|
|
||||||
## Generates moves for the knight in the given square
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -678,6 +717,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)
|
||||||
# TODO: all pieces
|
# TODO: all pieces
|
||||||
|
|
||||||
|
|
||||||
|
@ -1716,17 +1756,12 @@ when isMainModule:
|
||||||
testPieceBitboard(blackKing, blackKingSquares)
|
testPieceBitboard(blackKing, blackKingSquares)
|
||||||
|
|
||||||
b = newChessboardFromFEN("B7/P1P1P1P1/1P1P1P1P/7k/8/8/8/3K4 w - - 0 1")
|
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.generatePawnMoves(m)
|
b.generateMoves(m)
|
||||||
echo &"Pawn moves for {b.getSideToMove()} at {b.toFEN()}: "
|
echo &"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()
|
||||||
#[b.doMove(createMove("d1", "c1"))
|
echo b.pretty()
|
||||||
m.clear()
|
|
||||||
b.generatePawnMoves(m)
|
|
||||||
echo &"Pawn moves for {b.getSideToMove()} at {b.toFEN()}: "
|
|
||||||
for move in m:
|
|
||||||
echo " - ", move.startSquare, move.targetSquare, " ", move.getFlags()
|
|
||||||
echo b.pretty()]#
|
|
||||||
# setControlCHook(proc () {.noconv.} = quit(0))
|
# setControlCHook(proc () {.noconv.} = quit(0))
|
||||||
# quit(main())
|
# quit(main())
|
||||||
|
|
Loading…
Reference in New Issue