[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> // Eryn Wells <eryn@erynwells.me>
use crate::{ use crate::{
display::DiagramFormatter, piece_sets::PlacePieceError, Castle, EnPassant, Flags, MoveCounter, castle, display::DiagramFormatter, piece_sets::PlacePieceError, EnPassant, MoveCounter,
PieceSet, PieceSet,
}; };
use chessfriend_bitboard::BitBoard; use chessfriend_bitboard::BitBoard;
@ -10,10 +10,10 @@ use std::iter::Iterator;
#[derive(Clone, Debug, Eq)] #[derive(Clone, Debug, Eq)]
pub struct Board { pub struct Board {
flags: Flags,
pieces: PieceSet, pieces: PieceSet,
en_passant: Option<EnPassant>, en_passant: Option<EnPassant>,
pub move_counter: MoveCounter, pub move_counter: MoveCounter,
pub castling_rights: castle::Rights,
} }
impl Board { impl Board {
@ -91,6 +91,39 @@ impl Board {
self.piece_on_square(square) 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 /// A [`BitBoard`] representing the set of squares containing a piece. This
/// set is the inverse of [`Board::empty_squares`]. /// set is the inverse of [`Board::empty_squares`].
#[must_use] #[must_use]
@ -182,7 +215,7 @@ impl Board {
impl Default for Board { impl Default for Board {
fn default() -> Self { fn default() -> Self {
Self { Self {
flags: Flags::default(), castling_rights: castle::Rights::default(),
pieces: PieceSet::default(), pieces: PieceSet::default(),
en_passant: None, en_passant: None,
move_counter: MoveCounter::default(), move_counter: MoveCounter::default(),
@ -193,7 +226,7 @@ impl Default for Board {
impl PartialEq for Board { impl PartialEq for Board {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.pieces == other.pieces self.pieces == other.pieces
&& self.flags == other.flags && self.castling_rights == other.castling_rights
&& self.en_passant == other.en_passant && self.en_passant == other.en_passant
&& self.move_counter == other.move_counter && self.move_counter == other.move_counter
} }
@ -232,8 +265,12 @@ mod tests {
#[test] #[test]
fn king_not_on_starting_square_cannot_castle() { fn king_not_on_starting_square_cannot_castle() {
let board = test_board!(White King on E4); let board = test_board!(White King on E4);
assert!(!board.player_has_right_to_castle(Color::White, Castle::KingSide)); assert!(!board
assert!(!board.player_has_right_to_castle(Color::White, Castle::QueenSide)); .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] #[test]
@ -244,8 +281,12 @@ mod tests {
White Rook on H1 White Rook on H1
); );
assert!(board.player_has_right_to_castle(Color::White, Castle::KingSide)); assert!(board
assert!(board.player_has_right_to_castle(Color::White, Castle::QueenSide)); .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] #[test]

View file

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

View file

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