From 0b100d5f147da130c0759c15db6722b9d91d91e5 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 2 May 2025 15:03:48 -0700 Subject: [PATCH] [board] Remove Flags struct, replace it with Castle and supporting structs Encapsulate castling rights within a small module. Castle provides the API to the rest of the board package. Rights encodes the castling rights for each player. Parameters defines castling parameters for each player. --- board/src/castle.rs | 119 +++------------------------------ board/src/castle/parameters.rs | 110 ++++++++++++++++++++++++++++++ board/src/castle/rights.rs | 92 +++++++++++++++++++++++++ board/src/flags.rs | 90 ------------------------- board/src/lib.rs | 2 - 5 files changed, 210 insertions(+), 203 deletions(-) create mode 100644 board/src/castle/parameters.rs create mode 100644 board/src/castle/rights.rs delete mode 100644 board/src/flags.rs diff --git a/board/src/castle.rs b/board/src/castle.rs index 3534d8d..7f477df 100644 --- a/board/src/castle.rs +++ b/board/src/castle.rs @@ -1,7 +1,12 @@ // Eryn Wells -use chessfriend_bitboard::BitBoard; -use chessfriend_core::{Color, Square}; +mod parameters; +mod rights; + +pub use rights::Rights; + +use chessfriend_core::Color; +use parameters::Parameters; #[repr(u8)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -10,118 +15,10 @@ pub enum Castle { QueenSide = 1, } -pub struct Parameters { - /// Origin squares of the king and rook. - origin: Squares, - - /// Target or destination squares for the king and rook. - target: Squares, - - /// The set of squares that must be clear of any pieces in order to perform - /// this castle. - clear: BitBoard, - - /// The set of squares that must not be attacked (i.e. visible to opposing - /// pieces) in order to perform this castle. - check: BitBoard, -} - -impl Parameters { - pub fn king_origin_square(&self) -> Square { - self.origin.king - } - - pub fn rook_origin_square(&self) -> Square { - self.origin.rook - } - - pub fn king_target_square(&self) -> Square { - self.target.king - } - - pub fn rook_target_square(&self) -> Square { - self.target.rook - } - - /// A [`BitBoard`] of the squares that must be clear of any piece in order - /// to perform this castle move. - pub fn clear_squares(&self) -> &BitBoard { - &self.clear - } - - /// A [`BitBoard`] of the squares that must not be visible to opposing - /// pieces in order to perform this castle move. - pub fn check_squares(&self) -> &BitBoard { - &self.check - } -} - -#[derive(Debug)] -struct Squares { - king: Square, - rook: Square, -} - impl Castle { pub const ALL: [Castle; 2] = [Castle::KingSide, Castle::QueenSide]; - /// Parameters for each castling move, organized by color and board-side. - const PARAMETERS: [[Parameters; 2]; Color::NUM] = [ - [ - Parameters { - origin: Squares { - king: Square::E1, - rook: Square::H1, - }, - target: Squares { - king: Square::G1, - rook: Square::F1, - }, - clear: BitBoard::new(0b0110_0000), - check: BitBoard::new(0b0111_0000), - }, - Parameters { - origin: Squares { - king: Square::E1, - rook: Square::A1, - }, - target: Squares { - king: Square::C1, - rook: Square::D1, - }, - clear: BitBoard::new(0b0000_1110), - check: BitBoard::new(0b0001_1100), - }, - ], - [ - Parameters { - origin: Squares { - king: Square::E8, - rook: Square::H8, - }, - target: Squares { - king: Square::G8, - rook: Square::F8, - }, - clear: BitBoard::new(0b0110_0000 << (8 * 7)), - check: BitBoard::new(0b0111_0000 << (8 * 7)), - }, - Parameters { - origin: Squares { - king: Square::E8, - rook: Square::A8, - }, - target: Squares { - king: Square::C8, - rook: Square::D8, - }, - clear: BitBoard::new(0b0000_1110 << (8 * 7)), - check: BitBoard::new(0b0001_1100 << (8 * 7)), - }, - ], - ]; - pub fn parameters(self, color: Color) -> &'static Parameters { - &Castle::PARAMETERS[color as usize][self as usize] + &Parameters::BY_COLOR[color as usize][self as usize] } } diff --git a/board/src/castle/parameters.rs b/board/src/castle/parameters.rs new file mode 100644 index 0000000..cfe5901 --- /dev/null +++ b/board/src/castle/parameters.rs @@ -0,0 +1,110 @@ +use chessfriend_bitboard::BitBoard; +use chessfriend_core::{Color, Square}; + +pub struct Parameters { + /// Origin squares of the king and rook. + origin: Squares, + + /// Target or destination squares for the king and rook. + target: Squares, + + /// The set of squares that must be clear of any pieces in order to perform + /// this castle. + clear: BitBoard, + + /// The set of squares that must not be attacked (i.e. visible to opposing + /// pieces) in order to perform this castle. + check: BitBoard, +} + +#[derive(Debug)] +pub(super) struct Squares { + pub king: Square, + pub rook: Square, +} + +impl Parameters { + /// Parameters for each castling move, organized by color and board-side. + pub(super) const BY_COLOR: [[Self; 2]; Color::NUM] = [ + [ + Parameters { + origin: Squares { + king: Square::E1, + rook: Square::H1, + }, + target: Squares { + king: Square::G1, + rook: Square::F1, + }, + clear: BitBoard::new(0b0110_0000), + check: BitBoard::new(0b0111_0000), + }, + Parameters { + origin: Squares { + king: Square::E1, + rook: Square::A1, + }, + target: Squares { + king: Square::C1, + rook: Square::D1, + }, + clear: BitBoard::new(0b0000_1110), + check: BitBoard::new(0b0001_1100), + }, + ], + [ + Parameters { + origin: Squares { + king: Square::E8, + rook: Square::H8, + }, + target: Squares { + king: Square::G8, + rook: Square::F8, + }, + clear: BitBoard::new(0b0110_0000 << (8 * 7)), + check: BitBoard::new(0b0111_0000 << (8 * 7)), + }, + Parameters { + origin: Squares { + king: Square::E8, + rook: Square::A8, + }, + target: Squares { + king: Square::C8, + rook: Square::D8, + }, + clear: BitBoard::new(0b0000_1110 << (8 * 7)), + check: BitBoard::new(0b0001_1100 << (8 * 7)), + }, + ], + ]; + + pub fn king_origin_square(&self) -> Square { + self.origin.king + } + + pub fn rook_origin_square(&self) -> Square { + self.origin.rook + } + + pub fn king_target_square(&self) -> Square { + self.target.king + } + + pub fn rook_target_square(&self) -> Square { + self.target.rook + } + + /// A [`BitBoard`] of the squares that must be clear of any piece in order + /// to perform this castle move. + pub fn clear_squares(&self) -> &BitBoard { + &self.clear + } + + /// A [`BitBoard`] of the squares that must not be visible to opposing + /// pieces in order to perform this castle move. + pub fn check_squares(&self) -> &BitBoard { + &self.check + } +} diff --git a/board/src/castle/rights.rs b/board/src/castle/rights.rs new file mode 100644 index 0000000..f6016e8 --- /dev/null +++ b/board/src/castle/rights.rs @@ -0,0 +1,92 @@ +use super::Castle; +use chessfriend_core::Color; +use std::fmt; + +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub struct Rights(u8); + +impl Rights { + /// Returns `true` if the player has the right to castle on the given side + /// of the board. + /// + /// A player retains the right to castle on a particular side of the board + /// as long as they have not moved their king, or the rook on that side of + /// the board. + pub fn player_has_right_to_castle(self, color: Color, castle: Castle) -> bool { + (self.0 & (1 << Self::_player_has_right_to_castle_flag_offset(color, castle))) != 0 + } + + pub fn set_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) { + self.0 |= 1 << Self::_player_has_right_to_castle_flag_offset(color, castle); + } + + pub fn clear_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) { + self.0 &= !(1 << Self::_player_has_right_to_castle_flag_offset(color, castle)); + } + + pub fn clear_all(&mut self) { + self.0 &= 0b1111_1100; + } + + fn _player_has_right_to_castle_flag_offset(color: Color, castle: Castle) -> usize { + ((color as usize) << 1) & castle as usize + } +} + +impl fmt::Debug for Rights { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Flags({:08b})", self.0) + } +} + +impl Default for Rights { + fn default() -> Self { + Self(0b0000_1111) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn castling_rights() { + assert_eq!( + Rights::_player_has_right_to_castle_flag_offset(Color::White, Castle::KingSide), + 0 + ); + assert_eq!( + Rights::_player_has_right_to_castle_flag_offset(Color::White, Castle::QueenSide), + 1 + ); + assert_eq!( + Rights::_player_has_right_to_castle_flag_offset(Color::Black, Castle::KingSide), + 2 + ); + assert_eq!( + Rights::_player_has_right_to_castle_flag_offset(Color::Black, Castle::QueenSide), + 3 + ); + } + + #[test] + fn default_rights() { + let mut rights = Rights::default(); + assert!(rights.player_has_right_to_castle(Color::White, Castle::KingSide)); + assert!(rights.player_has_right_to_castle(Color::White, Castle::QueenSide)); + assert!(rights.player_has_right_to_castle(Color::Black, Castle::KingSide)); + assert!(rights.player_has_right_to_castle(Color::Black, Castle::QueenSide)); + + rights.clear_player_has_right_to_castle_flag(Color::White, Castle::QueenSide); + assert!(rights.player_has_right_to_castle(Color::White, Castle::KingSide)); + assert!(!rights.player_has_right_to_castle(Color::White, Castle::QueenSide)); + assert!(rights.player_has_right_to_castle(Color::Black, Castle::KingSide)); + assert!(rights.player_has_right_to_castle(Color::Black, Castle::QueenSide)); + + rights.set_player_has_right_to_castle_flag(Color::White, Castle::QueenSide); + assert!(rights.player_has_right_to_castle(Color::White, Castle::KingSide)); + assert!(rights.player_has_right_to_castle(Color::White, Castle::QueenSide)); + assert!(rights.player_has_right_to_castle(Color::Black, Castle::KingSide)); + assert!(rights.player_has_right_to_castle(Color::Black, Castle::QueenSide)); + } +} diff --git a/board/src/flags.rs b/board/src/flags.rs deleted file mode 100644 index 548c00f..0000000 --- a/board/src/flags.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Eryn Wells - -use crate::Castle; -use chessfriend_core::Color; -use std::fmt; - -#[derive(Clone, Copy, Eq, Hash, PartialEq)] -pub struct Flags(u8); - -impl Flags { - #[inline] - fn player_has_right_to_castle_flag_offset(color: Color, castle: Castle) -> usize { - ((color as usize) << 1) + castle as usize - } - - #[allow(dead_code)] - pub(super) fn player_has_right_to_castle(self, color: Color, castle: Castle) -> bool { - (self.0 & (1 << Self::player_has_right_to_castle_flag_offset(color, castle))) != 0 - } - - pub(super) fn set_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) { - self.0 |= 1 << Self::player_has_right_to_castle_flag_offset(color, castle); - } - - pub(super) fn clear_player_has_right_to_castle_flag(&mut self, color: Color, castle: Castle) { - self.0 &= !(1 << Self::player_has_right_to_castle_flag_offset(color, castle)); - } - - pub(super) fn clear_all_castling_rights(&mut self) { - self.0 &= 0b1111_1100; - } -} - -impl fmt::Debug for Flags { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Flags({:08b})", self.0) - } -} - -impl Default for Flags { - fn default() -> Self { - Flags(0b0000_1111) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn castle_flags() { - assert_eq!( - Flags::player_has_right_to_castle_flag_offset(Color::White, Castle::KingSide), - 0 - ); - assert_eq!( - Flags::player_has_right_to_castle_flag_offset(Color::White, Castle::QueenSide), - 1 - ); - assert_eq!( - Flags::player_has_right_to_castle_flag_offset(Color::Black, Castle::KingSide), - 2 - ); - assert_eq!( - Flags::player_has_right_to_castle_flag_offset(Color::Black, Castle::QueenSide), - 3 - ); - } - - #[test] - fn defaults() { - let mut flags: Flags = Default::default(); - assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide)); - assert!(flags.player_has_right_to_castle(Color::White, Castle::QueenSide)); - assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide)); - assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide)); - - flags.clear_player_has_right_to_castle_flag(Color::White, Castle::QueenSide); - assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide)); - assert!(!flags.player_has_right_to_castle(Color::White, Castle::QueenSide)); - assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide)); - assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide)); - - flags.set_player_has_right_to_castle_flag(Color::White, Castle::QueenSide); - assert!(flags.player_has_right_to_castle(Color::White, Castle::KingSide)); - assert!(flags.player_has_right_to_castle(Color::White, Castle::QueenSide)); - assert!(flags.player_has_right_to_castle(Color::Black, Castle::KingSide)); - assert!(flags.player_has_right_to_castle(Color::Black, Castle::QueenSide)); - } -} diff --git a/board/src/lib.rs b/board/src/lib.rs index 013128c..35c7803 100644 --- a/board/src/lib.rs +++ b/board/src/lib.rs @@ -4,7 +4,6 @@ pub mod castle; pub mod display; pub mod en_passant; pub mod fen; -pub mod flags; pub mod macros; pub mod move_counter; @@ -17,6 +16,5 @@ pub use builder::Builder; use castle::Castle; use en_passant::EnPassant; -use flags::Flags; use move_counter::MoveCounter; use piece_sets::PieceSet;