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:
|
||||
return bitboard shl 9
|
||||
of BackwardLeft:
|
||||
return bitboard shr 7
|
||||
else:
|
||||
discard
|
||||
return bitboard shl 7
|
||||
of Left:
|
||||
return bitboard shr 1
|
||||
of Right:
|
||||
return bitboard shl 1
|
||||
of Black:
|
||||
# The directions for black are just the opposite of those for white,
|
||||
# so we avoid duplicating any code
|
||||
|
@ -172,14 +174,18 @@ func getDirectionMask*(bitboard: Bitboard, color: PieceColor, direction: Directi
|
|||
of BackwardRight:
|
||||
return bitboard shr 9
|
||||
of BackwardLeft:
|
||||
return bitboard shl 7
|
||||
else:
|
||||
discard
|
||||
return bitboard shr 7
|
||||
of Left:
|
||||
return bitboard shl 1
|
||||
of Right:
|
||||
return bitboard shr 1
|
||||
else:
|
||||
discard
|
||||
|
||||
|
||||
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 =
|
||||
## 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 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
|
||||
# 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)
|
||||
|
||||
|
||||
func bottomRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||
getDirectionMask(self, side, BackwardRight) and not getFileMask(7)
|
||||
func backwardRightRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||
getDirectionMask(self, side, BackwardRight)
|
||||
|
||||
|
||||
func bottomLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||
getDirectionMask(self, side, BackwardLeft) and not getFileMask(0)
|
||||
func backwardLeftRelativeTo*(self: Bitboard, side: PieceColor): Bitboard =
|
||||
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:
|
||||
result.position.pieces.black.queens.uint64.setBit(bitIndex)
|
||||
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")
|
||||
result.position.pieces.black.king.uint64.setBit(bitIndex)
|
||||
else:
|
||||
|
@ -360,7 +360,7 @@ proc newChessboardFromFEN*(fen: string): ChessBoard =
|
|||
#[if result.inCheck(result.getSideToMove().opposite):
|
||||
# Opponent king cannot be captured on the next move
|
||||
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
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
## color (if it is None, the active color is used)
|
||||
## side (if it is None, the side to move is used)
|
||||
var color = color
|
||||
if color == None:
|
||||
color = self.getSideToMove()
|
||||
case color:
|
||||
of White:
|
||||
return Square(self.position.pieces.white.king.uint64.countTrailingZeroBits())
|
||||
return self.position.pieces.white.king.toSquare()
|
||||
of Black:
|
||||
return Square(self.position.pieces.black.king.uint64.countTrailingZeroBits())
|
||||
return self.position.pieces.black.king.toSquare()
|
||||
else:
|
||||
discard
|
||||
|
||||
|
@ -527,6 +527,28 @@ proc getOccupancy(self: ChessBoard): Bitboard =
|
|||
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) =
|
||||
## Helper of generatePawnMoves for generating all non-capture
|
||||
## 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 also cannot move to the last rank, as that will result in a promotion
|
||||
# and is handled elsewhere
|
||||
occupancy = not (self.getOccupancy() or sideToMove.getLastRank())
|
||||
allowedSquares = not (self.getOccupancy() or sideToMove.getLastRank())
|
||||
# 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))
|
||||
# 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))
|
||||
|
||||
|
||||
|
@ -553,17 +575,18 @@ proc generatePawnCaptures(self: ChessBoard, moves: var MoveList) =
|
|||
sideToMove = self.getSideToMove()
|
||||
nonSideToMove = sideToMove.opposite()
|
||||
pawns = self.getBitboard(Pawn, sideToMove)
|
||||
# We can only capture enemy pieces
|
||||
enemyPieces = self.getOccupancyFor(nonSideToMove)
|
||||
# We can only capture enemy pieces (except the king)
|
||||
enemyPieces = self.getOccupancyFor(nonSideToMove) and not self.getBitboard(King, nonSideToMove)
|
||||
rightMovement = pawns.forwardRightRelativeTo(sideToMove)
|
||||
leftMovement = pawns.forwardLeftRelativeTo(sideToMove)
|
||||
epTarget = self.getEnPassantTarget()
|
||||
let epBitboard = if (epTarget != nullSquare()): epTarget.toBitboard() else: Bitboard(0)
|
||||
# Top right attacks
|
||||
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:
|
||||
moves.add(createMove(square.toBitboard().bottomRightRelativeTo(sideToMove), square, Capture))
|
||||
moves.add(createMove(square.toBitboard().backwardRightRelativeTo(sideToMove), square, Capture))
|
||||
# Special case for en passant
|
||||
let
|
||||
epLeft = epBitboard and leftMovement
|
||||
|
@ -580,31 +603,47 @@ proc generatePawnPromotions(self: ChessBoard, moves: var MoveList) =
|
|||
let
|
||||
sideToMove = self.getSideToMove()
|
||||
pawns = self.getBitboard(Pawn, sideToMove)
|
||||
occupancy = not self.getOccupancy()
|
||||
for square in pawns.forwardRelativeTo(sideToMove) and occupancy and sideToMove.getLastRank():
|
||||
occupancy = self.getOccupancy()
|
||||
for square in pawns.forwardRelativeTo(sideToMove) and not occupancy and sideToMove.getLastRank():
|
||||
for promotion in [PromoteToBishop, PromoteToKnight, PromoteToQueen, PromoteToRook]:
|
||||
moves.add(createMove(square.toBitboard().backwardRelativeTo(sideToMove), square, promotion))
|
||||
|
||||
|
||||
proc generatePawnMoves(self: ChessBoard, moves: var MoveList) =
|
||||
## Generates the possible moves for the pawn in the given
|
||||
## square
|
||||
|
||||
## Generates all the legal pawn moves for the side to move
|
||||
self.generatePawnMovements(moves)
|
||||
self.generatePawnCaptures(moves)
|
||||
self.generatePawnPromotions(moves)
|
||||
|
||||
|
||||
proc generateSlidingMoves(self: ChessBoard, square: Square): seq[Move] =
|
||||
## Generates moves for the sliding piece in the given square
|
||||
proc generateSlidingMoves(self: ChessBoard, moves: var MoveList) =
|
||||
## 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] =
|
||||
## Generates moves for the king in the given square
|
||||
|
||||
|
||||
proc generateKnightMoves(self: ChessBoard, square: Square): seq[Move] =
|
||||
## Generates moves for the knight in the given square
|
||||
proc generateKnightMoves(self: ChessBoard, moves: var MoveList)=
|
||||
## Generates all the legal knight moves for the side to move
|
||||
|
||||
|
||||
|
||||
|
@ -678,6 +717,7 @@ proc generateMoves*(self: ChessBoard, moves: var MoveList) =
|
|||
]#
|
||||
# TODO: Check for repetitions (requires zobrist hashing + table)
|
||||
self.generatePawnMoves(moves)
|
||||
self.generateKingMoves(moves)
|
||||
# TODO: all pieces
|
||||
|
||||
|
||||
|
@ -1716,17 +1756,12 @@ when isMainModule:
|
|||
testPieceBitboard(blackKing, blackKingSquares)
|
||||
|
||||
b = newChessboardFromFEN("B7/P1P1P1P1/1P1P1P1P/7k/8/8/8/3K4 w - - 0 1")
|
||||
b.doMove(createMove("d1", "e1"))
|
||||
var m = MoveList()
|
||||
b.generatePawnMoves(m)
|
||||
echo &"Pawn moves for {b.getSideToMove()} at {b.toFEN()}: "
|
||||
b.generateMoves(m)
|
||||
echo &"Legal moves for {b.getSideToMove()} at {b.toFEN()}: "
|
||||
for move in m:
|
||||
echo " - ", move.startSquare, move.targetSquare, " ", move.getFlags()
|
||||
#[b.doMove(createMove("d1", "c1"))
|
||||
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()]#
|
||||
echo b.pretty()
|
||||
# setControlCHook(proc () {.noconv.} = quit(0))
|
||||
# quit(main())
|
||||
|
|
Loading…
Reference in New Issue