Chess programming with bitboards part 2

Keywords: piece type, board, square, color, method, check, bitwise, bitboard, perform, fen, dictionary, determine, result, model. Powered by TextRank.

Now that we've covered the basics of bitboards, it's time to take a look at our model from a higher level. The objects we need to model the game include at least a board and pieces. While the engine that powers our moves and tracks piece positions will be the bitboards, there are other features we need, such as setting up the board using a FEN or retrieving the FEN representation. Below are some of the key functions I believe are useful:

occupancy(color: Color) -> BitBoard

This method returns a bitboard with all the bits set for the pieces of the specified color. This is particularly useful when computing moves, as you need to check for captures. The implementation is straightforward: you simply perform a bitwise OR on all the bitboards for the given side.

if color == .white {
  return whitePawnBoard | whiteRookBoard | whiteKnightBoard ...
}

You can also perform a bitwise OR on the result of both colors to get all the occupied squares on the board. From this, you can easily deduce the empty squares.

subscript(square: Square) -> (PieceType, Color)?

This method helps determine which piece, if any, is occupying a particular square on the board. I use a dictionary that holds references to each bitboard, keyed by the piece type and color. It’s crucial to use references because bitboards are integers, and if they are properties of the board class, the dictionary should point to these bitboards rather than hold separate copies.

Fetching the actual piece is then just a matter of performing a bitwise AND operation between the board for that piece and color, and the mask of the square. If the result is non-zero, then that’s the piece occupying the square.

possibleMoves(from: Square) -> BitBoard?

This method returns all the possible moves that a piece on the specified square can make, if any. The computation of moves for each piece using bitboards is a topic I'll cover in a future post.

isAttacked(square: Square, by color: Color) -> Bool

This method will be frequently used, especially since you cannot castle through a check or leave your king in check. The basic idea is to retrieve all the bitboards for a given color, iterate through the set bits (representing the pieces on the board), and, for each square, determine the type of piece. You can then request the attacking moves for that piece. To check if the attack bitboard includes the specified square, you perform a bitwise AND operation.

for board in whiteBoards {
  for square in board.squares {
    let (type, color) = self[square]
    let moves = type.attacks(from: square, color: color)
    if moves & square.mask != 0 { return true }
  }
  ...
}


Metadata

447 words

Similar posts

Powered by TF-IDF/Cosine similarity

First published on 2024-09-15

Generated on 13 Dec 2024 at 12:12 AM

Index

Mobile optimized version. Desktop version.