[board] Replace Flags with castle::Rights

This commit is contained in:
Eryn Wells 2025-05-02 15:41:45 -07:00
parent cd60a453aa
commit bb8d5a6aa3
3 changed files with 62 additions and 19 deletions

View file

@ -1,7 +1,7 @@
// Eryn Wells <eryn@erynwells.me>
use crate::{
display::DiagramFormatter, piece_sets::PlacePieceError, Castle, EnPassant, Flags, MoveCounter,
castle, display::DiagramFormatter, piece_sets::PlacePieceError, EnPassant, MoveCounter,
PieceSet,
};
use chessfriend_bitboard::BitBoard;
@ -10,10 +10,10 @@ use std::iter::Iterator;
#[derive(Clone, Debug, Eq)]
pub struct Board {
flags: Flags,
pieces: PieceSet,
en_passant: Option<EnPassant>,
pub move_counter: MoveCounter,
pub castling_rights: castle::Rights,
}
impl Board {
@ -91,6 +91,39 @@ impl Board {
self.piece_on_square(square)
}
/// Returns `true` if the player is able to castle on the given side of the board.
///
/// The following requirements must be met:
///
/// 1. The player must still have the right to castle on that side of the
/// board. The king and rook involved in the castle must not have moved.
/// 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 fn player_can_castle(&self, player: Color, castle: Castle) -> bool {
if !self
.castling_rights
.player_has_right_to_castle(player, castle.into())
{
return false;
}
let castling_parameters = castle.parameters(player);
let all_pieces = self.all_pieces_bitboard();
if !(all_pieces & castling_parameters.clear_squares()).is_empty() {
return false;
}
let danger_squares = self.king_danger(player);
if !(danger_squares & castling_parameters.check_squares()).is_empty() {
return false;
}
true
}
/// A [`BitBoard`] representing the set of squares containing a piece. This
/// set is the inverse of [`Board::empty_squares`].
#[must_use]
@ -182,7 +215,7 @@ impl Board {
impl Default for Board {
fn default() -> Self {
Self {
flags: Flags::default(),
castling_rights: castle::Rights::default(),
pieces: PieceSet::default(),
en_passant: None,
move_counter: MoveCounter::default(),
@ -193,7 +226,7 @@ impl Default for Board {
impl PartialEq for Board {
fn eq(&self, other: &Self) -> bool {
self.pieces == other.pieces
&& self.flags == other.flags
&& self.castling_rights == other.castling_rights
&& self.en_passant == other.en_passant
&& self.move_counter == other.move_counter
}
@ -232,8 +265,12 @@ mod tests {
#[test]
fn king_not_on_starting_square_cannot_castle() {
let board = test_board!(White King on E4);
assert!(!board.player_has_right_to_castle(Color::White, Castle::KingSide));
assert!(!board.player_has_right_to_castle(Color::White, Castle::QueenSide));
assert!(!board
.castling_rights
.player_has_right_to_castle(Color::White, Castle::KingSide));
assert!(!board
.castling_rights
.player_has_right_to_castle(Color::White, Castle::QueenSide));
}
#[test]
@ -244,8 +281,12 @@ mod tests {
White Rook on H1
);
assert!(board.player_has_right_to_castle(Color::White, Castle::KingSide));
assert!(board.player_has_right_to_castle(Color::White, Castle::QueenSide));
assert!(board
.castling_rights
.player_has_right_to_castle(Color::White, Castle::KingSide));
assert!(board
.castling_rights
.player_has_right_to_castle(Color::White, Castle::QueenSide));
}
#[test]

View file

@ -1,11 +1,11 @@
// Eryn Wells <eryn@erynwells.me>
use crate::{piece_sets::Mailbox, Board, Castle, EnPassant, Flags, MoveCounter, PieceSet};
use crate::{castle, piece_sets::Mailbox, Board, Castle, EnPassant, MoveCounter, PieceSet};
use chessfriend_core::{piece, Color, PlacedPiece, Rank, Shape, Square};
#[derive(Clone)]
pub struct Builder {
flags: Flags,
castling_rights: castle::Rights,
pieces: Mailbox,
kings: [Option<Square>; Color::NUM],
en_passant: Option<EnPassant>,
@ -26,8 +26,8 @@ impl Builder {
let black_king = board.king_square(Color::Black);
Self {
flags: *board.flags(),
pieces,
castling_rights: board.castling_rights,
kings: [Some(white_king), Some(black_king)],
en_passant: board.en_passant(),
move_counter: board.move_counter,
@ -74,13 +74,13 @@ impl Builder {
}
pub fn player_can_castle(&mut self, color: Color, castle: Castle) -> &mut Self {
self.flags
self.castling_rights
.set_player_has_right_to_castle_flag(color, castle);
self
}
pub fn no_castling_rights(&mut self) -> &mut Self {
self.flags.clear_all_castling_rights();
self.castling_rights.clear_all();
self
}
@ -91,7 +91,7 @@ impl Builder {
.filter(Self::is_piece_placement_valid)
.collect();
let mut flags = self.flags;
let mut castling_rights = self.castling_rights;
for color in Color::ALL {
for castle in Castle::ALL {
@ -105,16 +105,16 @@ impl Builder {
self.kings[color as usize] == Some(parameters.king_origin_square());
if !king_is_on_starting_square || !has_rook_on_starting_square {
flags.clear_player_has_right_to_castle_flag(color, castle);
castling_rights.clear_player_has_right_to_castle_flag(color, castle);
}
}
}
Board {
flags,
pieces,
self.en_passant,
move_counter: self.move_counter,
castling_rights,
}
}
}
@ -143,7 +143,7 @@ impl Default for Builder {
]);
Self {
flags: Flags::default(),
castling_rights: castle::Rights::default(),
pieces,
kings: [Some(white_king_square), Some(black_king_square)],
en_passant: None,

View file

@ -103,8 +103,10 @@ impl ToFenStr for Board {
(Color::Black, Castle::QueenSide),
]
.map(|(color, castle)| {
let can_castle = self.player_has_right_to_castle(color, castle);
if !can_castle {
if !self
.castling_rights
.player_has_right_to_castle(color, castle)
{
return "";
}