[board] Implement all the bit twiddling to track whether castling is allowed for each player and side of the board

This commit is contained in:
Eryn Wells 2024-01-08 14:41:54 -08:00
parent 8cc7e64ba6
commit 31e5771d30
3 changed files with 100 additions and 7 deletions

View file

@ -10,10 +10,27 @@ use crate::{
use std::fmt;
use std::fmt::Write;
/// A lateral side of the board relative to the player. Kingside is the side the
/// player's king starts on. Queenside is the side of the board the player's
/// queen starts on.
pub(crate) enum BoardSide {
King,
Queen,
}
#[derive(Clone, Eq, Hash, PartialEq)]
pub struct Position {
color_to_move: Color,
/// Position flags indicating various bits of game state. The flags are as
/// follows:
///
/// 0. white can castle king-side
/// 1. white can castle queen-side
/// 2. black can castle king-side
/// 3. black can castle queen-side
flags: u8,
/// Composite bitboards for all the pieces of a particular color.
pieces_per_color: [BitBoard; 2],
@ -25,7 +42,8 @@ impl Position {
pub fn empty() -> Position {
Position {
color_to_move: Color::White,
pieces_per_color: [BitBoard::empty(), BitBoard::empty()],
flags: 0b00001111,
pieces_per_color: [BitBoard::empty(); 2],
pieces_per_type: [
[
BitBoard::empty(),
@ -69,6 +87,7 @@ impl Position {
Position {
color_to_move: Color::White,
flags: 0b00001111,
pieces_per_color: [
white_pieces.iter().fold(BitBoard::empty(), |a, b| a | *b),
black_pieces.iter().fold(BitBoard::empty(), |a, b| a | *b),
@ -77,6 +96,36 @@ impl Position {
}
}
/// Returns true if the player has the right to castle on the given side of
/// the board.
///
/// The right to castle on a particular side of the board is retained as
/// long as the player has not moved their king, or the rook on that side of
/// the board.
///
/// The following requirements must also be met:
///
/// 1. The spaces between the king and rook must be clear
/// 2. The king must not be in check
/// 3. In the course of castling on that side, the king must not pass
/// through a square that an enemy piece can see
pub(crate) fn player_has_right_to_castle(&self, color: Color, side: BoardSide) -> bool {
(self.flags & (1 << Self::player_has_right_to_castle_flag_offset(color, side))) != 0
}
fn set_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) {
self.flags |= 1 << Self::player_has_right_to_castle_flag_offset(color, side);
}
fn clear_player_has_right_to_castle_flag(&mut self, color: Color, side: BoardSide) {
self.flags &= !(1 << Self::player_has_right_to_castle_flag_offset(color, side));
}
#[inline]
fn player_has_right_to_castle_flag_offset(color: Color, side: BoardSide) -> u8 {
((color as u8) << 1) + side as u8
}
pub fn place_piece(&mut self, piece: Piece, square: Square) -> Result<(), PiecePlacementError> {
let type_bb = self.bitboard_for_piece_mut(piece);
@ -200,4 +249,36 @@ mod tests {
.place_piece(piece, square)
.expect_err("Placed white queen on e4 a second time?!");
}
#[test]
fn castle_flags() {
assert_eq!(
Position::player_has_right_to_castle_flag_offset(Color::White, BoardSide::King),
0
);
assert_eq!(
Position::player_has_right_to_castle_flag_offset(Color::White, BoardSide::Queen),
1
);
assert_eq!(
Position::player_has_right_to_castle_flag_offset(Color::Black, BoardSide::King),
2
);
assert_eq!(
Position::player_has_right_to_castle_flag_offset(Color::Black, BoardSide::Queen),
3
);
let mut pos = Position::starting();
assert!(pos.player_has_right_to_castle(Color::White, BoardSide::King));
assert!(pos.player_has_right_to_castle(Color::White, BoardSide::Queen));
assert!(pos.player_has_right_to_castle(Color::Black, BoardSide::King));
assert!(pos.player_has_right_to_castle(Color::Black, BoardSide::Queen));
pos.clear_player_has_right_to_castle_flag(Color::White, BoardSide::Queen);
assert!(pos.player_has_right_to_castle(Color::White, BoardSide::King));
assert!(!pos.player_has_right_to_castle(Color::White, BoardSide::Queen));
assert!(pos.player_has_right_to_castle(Color::Black, BoardSide::King));
assert!(pos.player_has_right_to_castle(Color::Black, BoardSide::Queen));
}
}