From 404212363e4a5c1ffbc813ab85059423953bf943 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Tue, 3 Jun 2025 20:25:53 -0700 Subject: [PATCH] [board, moves] Make Board::castling_rights and Board::en_passant_target private Make the struct fields private and export getters and various setters for manipulating the data. Update all the references to these fields to use the getters and setters instead. --- board/src/board.rs | 60 ++++++++++++++++++++++++++++++++++-- board/src/castle.rs | 9 +++--- board/src/fen.rs | 16 +++++----- board/src/macros.rs | 2 +- board/src/movement.rs | 2 +- board/src/sight.rs | 2 +- moves/src/generators/pawn.rs | 2 +- moves/src/make_move.rs | 22 ++++++------- moves/src/record.rs | 4 +-- moves/src/unmake_move.rs | 28 ++++++++--------- 10 files changed, 96 insertions(+), 51 deletions(-) diff --git a/board/src/board.rs b/board/src/board.rs index 7433f08..9c465e0 100644 --- a/board/src/board.rs +++ b/board/src/board.rs @@ -7,7 +7,7 @@ use crate::{ PieceSet, }; use chessfriend_bitboard::BitBoard; -use chessfriend_core::{Color, Piece, Shape, Square}; +use chessfriend_core::{Color, Piece, Shape, Square, Wing}; pub type HalfMoveClock = u32; pub type FullMoveClock = u32; @@ -16,8 +16,8 @@ pub type FullMoveClock = u32; pub struct Board { active_color: Color, pieces: PieceSet, - pub castling_rights: castle::Rights, - pub en_passant_target: Option, + castling_rights: castle::Rights, + en_passant_target: Option, pub half_move_clock: HalfMoveClock, pub full_move_number: FullMoveClock, } @@ -71,6 +71,60 @@ impl Board { self.active_color = color; } +impl Board { + #[must_use] + pub fn castling_rights(&self) -> castle::Rights { + self.castling_rights + } + + pub fn set_castling_rights(&mut self, rights: castle::Rights) { + self.castling_rights = rights; + } + + #[must_use] + pub fn active_color_has_castling_right(&self, wing: Wing) -> bool { + self.color_has_castling_right(self.active_color, wing) + } + + #[must_use] + pub fn color_has_castling_right(&self, color: Color, wing: Wing) -> bool { + self.castling_rights.color_has_right(color, wing) + } + + pub fn grant_castling_right(&mut self, color: Color, wing: Wing) { + self.castling_rights.grant(color, wing); + } + + pub fn revoke_all_castling_rights(&mut self) { + self.castling_rights.revoke_all(); + } + + pub fn revoke_castling_right(&mut self, color: Color, wing: Wing) { + self.castling_rights.revoke(color, wing); + } +} + +impl Board { + /// Returns a copy of the current en passant square, if one exists. + #[must_use] + pub fn en_passant_target(&self) -> Option { + self.en_passant_target + } + + pub fn set_en_passant_target(&mut self, square: Square) { + self.set_en_passant_target_option(Some(square)); + } + + pub fn set_en_passant_target_option(&mut self, square: Option) { + self.en_passant_target = square; + } + + pub fn clear_en_passant_target(&mut self) { + self.en_passant_target = None; + } +} + +impl Board { #[must_use] pub fn get_piece(&self, square: Square) -> Option { self.pieces.get(square) diff --git a/board/src/castle.rs b/board/src/castle.rs index 02228d1..c5c3423 100644 --- a/board/src/castle.rs +++ b/board/src/castle.rs @@ -46,7 +46,7 @@ impl Board { let color = self.unwrap_color(color); - if !self.castling_rights.color_has_right(color, wing) { + if !self.color_has_castling_right(color, wing) { return Err(CastleEvaluationError::NoRights { color, wing }); } @@ -98,15 +98,14 @@ mod tests { #[test] fn king_on_starting_square_can_castle() { - let pos = test_board!( + let board = test_board!( White King on E1, White Rook on A1, White Rook on H1 ); - let rights = pos.castling_rights; - assert!(rights.color_has_right(Color::White, Wing::KingSide)); - assert!(rights.color_has_right(Color::White, Wing::QueenSide)); + assert!(board.color_has_castling_right(Color::White, Wing::KingSide)); + assert!(board.color_has_castling_right(Color::White, Wing::QueenSide)); } #[test] diff --git a/board/src/fen.rs b/board/src/fen.rs index 9a70391..470675b 100644 --- a/board/src/fen.rs +++ b/board/src/fen.rs @@ -123,7 +123,7 @@ impl ToFenStr for Board { (Color::Black, Wing::QueenSide), ] .map(|(color, castle)| { - if !self.castling_rights.color_has_right(color, castle) { + if !self.color_has_castling_right(color, castle) { return ""; } @@ -146,7 +146,7 @@ impl ToFenStr for Board { write!( fen_string, " {}", - self.en_passant_target + self.en_passant_target() .map_or("-".to_string(), |square| square.to_string()) ) .map_err(ToFenStrError::FmtError)?; @@ -230,14 +230,14 @@ impl FromFenStr for Board { .next() .ok_or(FromFenStrError::MissingField(Field::CastlingRights))?; if castling_rights == "-" { - board.castling_rights.revoke_all(); + board.revoke_all_castling_rights(); } else { for ch in castling_rights.chars() { match ch { - 'K' => board.castling_rights.grant(Color::White, Wing::KingSide), - 'Q' => board.castling_rights.grant(Color::White, Wing::QueenSide), - 'k' => board.castling_rights.grant(Color::Black, Wing::KingSide), - 'q' => board.castling_rights.grant(Color::Black, Wing::QueenSide), + 'K' => board.grant_castling_right(Color::White, Wing::KingSide), + 'Q' => board.grant_castling_right(Color::White, Wing::QueenSide), + 'k' => board.grant_castling_right(Color::Black, Wing::KingSide), + 'q' => board.grant_castling_right(Color::Black, Wing::QueenSide), _ => return Err(FromFenStrError::InvalidValue), }; } @@ -249,7 +249,7 @@ impl FromFenStr for Board { if en_passant_square != "-" { let square = Square::from_algebraic_str(en_passant_square) .map_err(FromFenStrError::ParseSquareError)?; - board.en_passant_target = Some(square); + board.set_en_passant_target(square); } let half_move_clock = fields diff --git a/board/src/macros.rs b/board/src/macros.rs index e6a5c3a..b3baf85 100644 --- a/board/src/macros.rs +++ b/board/src/macros.rs @@ -14,7 +14,7 @@ macro_rules! test_board { $crate::PlacePieceStrategy::default()); )* board.set_active_color(chessfriend_core::Color::$to_move); - board.en_passant_target = Some(chessfriend_core::Square::$en_passant); + board.set_en_passant_target(chessfriend_core::Square::$en_passant); println!("{}", board.display()); diff --git a/board/src/movement.rs b/board/src/movement.rs index f386579..4fa4381 100644 --- a/board/src/movement.rs +++ b/board/src/movement.rs @@ -29,7 +29,7 @@ impl Movement for Piece { match self.shape { Shape::Pawn => { - let en_passant_square: BitBoard = board.en_passant_target.into(); + let en_passant_square: BitBoard = board.en_passant_target().into(); // Pawns can only move to squares they can see to capture. let sight = self.sight(square, board) & (opposing_occupancy | en_passant_square); let pushes = pawn_pushes(square.into(), self.color, board.occupancy()); diff --git a/board/src/sight.rs b/board/src/sight.rs index bb6f8e1..9a84f7e 100644 --- a/board/src/sight.rs +++ b/board/src/sight.rs @@ -79,7 +79,7 @@ impl Sight for Piece { match self.shape { Shape::Pawn => { - let en_passant_square: BitBoard = board.en_passant_target.into(); + let en_passant_square: BitBoard = board.en_passant_target().into(); match self.color { Color::White => white_pawn_sight(&info, en_passant_square), Color::Black => black_pawn_sight(&info, en_passant_square), diff --git a/moves/src/generators/pawn.rs b/moves/src/generators/pawn.rs index f4974de..5e8b76b 100644 --- a/moves/src/generators/pawn.rs +++ b/moves/src/generators/pawn.rs @@ -48,7 +48,7 @@ impl PawnMoveGenerator { let (single_pushes, double_pushes) = Self::pushes(pawns, color, empty); let (left_captures, right_captures) = Self::captures(pawns, color, enemies); - let en_passant: BitBoard = board.en_passant_target.into(); + let en_passant: BitBoard = board.en_passant_target().into(); Self { color, diff --git a/moves/src/make_move.rs b/moves/src/make_move.rs index d92cb43..dfd9b41 100644 --- a/moves/src/make_move.rs +++ b/moves/src/make_move.rs @@ -169,11 +169,11 @@ impl MakeMoveInternal for T { // board state before the change is preserved. let record = MoveRecord::new(board, ply, None); - board.en_passant_target = match target.rank() { - Rank::FOUR => Some(Square::from_file_rank(target.file(), Rank::THREE)), - Rank::FIVE => Some(Square::from_file_rank(target.file(), Rank::SIX)), + board.set_en_passant_target(match target.rank() { + Rank::FOUR => Square::from_file_rank(target.file(), Rank::THREE), + Rank::FIVE => Square::from_file_rank(target.file(), Rank::SIX), _ => unreachable!(), - }; + }); self.advance_clocks(HalfMoveClock::Advance); @@ -192,7 +192,7 @@ impl MakeMoveInternal for T { if ply.is_en_passant() { let en_passant_square = board - .en_passant_target + .en_passant_target() .ok_or(MakeMoveError::NoCaptureSquare)?; if target_square != en_passant_square { return Err(MakeMoveError::InvalidEnPassantCapture(target_square)); @@ -240,7 +240,7 @@ impl MakeMoveInternal for T { // original board state is preserved. let record = MoveRecord::new(board, ply, None); - board.castling_rights.revoke(active_color, wing); + board.revoke_castling_right(active_color, wing); self.advance_clocks(HalfMoveClock::Advance); @@ -446,7 +446,7 @@ mod tests { assert_eq!(board.get_piece(Square::E2), None); assert_eq!(board.get_piece(Square::E4), Some(piece!(White Pawn))); assert_eq!( - board.en_passant_target, + board.en_passant_target(), Some(Square::E3), "en passant square not set" ); @@ -518,9 +518,7 @@ mod tests { assert_eq!(board.get_piece(Square::H1), None); assert_eq!(board.get_piece(Square::G1), Some(piece!(White King))); assert_eq!(board.get_piece(Square::F1), Some(piece!(White Rook))); - assert!(!board - .castling_rights - .color_has_right(Color::White, Wing::KingSide)); + assert!(!board.color_has_castling_right(Color::White, Wing::KingSide)); Ok(()) } @@ -540,9 +538,7 @@ mod tests { assert_eq!(board.get_piece(Square::A1), None); assert_eq!(board.get_piece(Square::C1), Some(piece!(White King))); assert_eq!(board.get_piece(Square::D1), Some(piece!(White Rook))); - assert!(!board - .castling_rights - .color_has_right(Color::White, Wing::QueenSide)); + assert!(!board.color_has_castling_right(Color::White, Wing::QueenSide)); Ok(()) } diff --git a/moves/src/record.rs b/moves/src/record.rs index 139f2d4..47a2c3f 100644 --- a/moves/src/record.rs +++ b/moves/src/record.rs @@ -34,8 +34,8 @@ impl MoveRecord { Self { color: board.active_color(), ply, - en_passant_target: board.en_passant_target, - castling_rights: board.castling_rights, + en_passant_target: board.en_passant_target(), + castling_rights: board.castling_rights(), half_move_clock: board.half_move_clock, captured_piece: capture, } diff --git a/moves/src/unmake_move.rs b/moves/src/unmake_move.rs index b3df2ca..7fd3fec 100644 --- a/moves/src/unmake_move.rs +++ b/moves/src/unmake_move.rs @@ -70,8 +70,8 @@ impl UnmakeMove for T { let board = self.board_mut(); board.set_active_color(record.color); - board.en_passant_target = record.en_passant_target; - board.castling_rights = record.castling_rights; + board.set_en_passant_target_option(record.en_passant_target); + board.set_castling_rights(record.castling_rights); board.half_move_clock = record.half_move_clock; Ok(()) @@ -260,7 +260,7 @@ mod tests { // Verify move was made assert_eq!(board.get_piece(Square::E2), None); assert_eq!(board.get_piece(Square::E4), Some(piece!(White Pawn))); - assert_eq!(board.en_passant_target, Some(Square::E3)); + assert_eq!(board.en_passant_target(), Some(Square::E3)); assert_eq!(board.active_color(), Color::Black); board.unmake_move(&record)?; @@ -268,7 +268,7 @@ mod tests { // Verify original state restored assert_eq!(board.get_piece(Square::E2), Some(piece!(White Pawn))); assert_eq!(board.get_piece(Square::E4), None); - assert_eq!(board.en_passant_target, None); + assert_eq!(board.en_passant_target(), None); assert_eq!(board.active_color(), Color::White); Ok(()) @@ -396,7 +396,7 @@ mod tests { White Rook on H1, ]; - let original_castling_rights = board.castling_rights; + let original_castling_rights = board.castling_rights(); let ply = Move::castle(Wing::KingSide); let record = board.make_move(ply, ValidateMove::Yes)?; @@ -406,9 +406,7 @@ mod tests { assert_eq!(board.get_piece(Square::H1), None); assert_eq!(board.get_piece(Square::G1), Some(piece!(White King))); assert_eq!(board.get_piece(Square::F1), Some(piece!(White Rook))); - assert!(!board - .castling_rights - .color_has_right(Color::White, Wing::KingSide)); + assert!(!board.color_has_castling_right(Color::White, Wing::KingSide)); board.unmake_move(&record)?; @@ -417,7 +415,7 @@ mod tests { assert_eq!(board.get_piece(Square::H1), Some(piece!(White Rook))); assert_eq!(board.get_piece(Square::G1), None); assert_eq!(board.get_piece(Square::F1), None); - assert_eq!(board.castling_rights, original_castling_rights); + assert_eq!(board.castling_rights(), original_castling_rights); assert_eq!(board.active_color(), Color::White); Ok(()) @@ -430,7 +428,7 @@ mod tests { White Rook on A1, ]; - let original_castling_rights = board.castling_rights; + let original_castling_rights = board.castling_rights(); let ply = Move::castle(Wing::QueenSide); let record = board.make_move(ply, ValidateMove::Yes)?; @@ -440,9 +438,7 @@ mod tests { assert_eq!(board.get_piece(Square::A1), None); assert_eq!(board.get_piece(Square::C1), Some(piece!(White King))); assert_eq!(board.get_piece(Square::D1), Some(piece!(White Rook))); - assert!(!board - .castling_rights - .color_has_right(Color::White, Wing::QueenSide)); + assert!(!board.color_has_castling_right(Color::White, Wing::QueenSide)); board.unmake_move(&record)?; @@ -451,7 +447,7 @@ mod tests { assert_eq!(board.get_piece(Square::A1), Some(piece!(White Rook))); assert_eq!(board.get_piece(Square::C1), None); assert_eq!(board.get_piece(Square::D1), None); - assert_eq!(board.castling_rights, original_castling_rights); + assert_eq!(board.castling_rights(), original_castling_rights); assert_eq!(board.active_color(), Color::White); Ok(()) @@ -465,7 +461,7 @@ mod tests { ]; board.set_active_color(Color::Black); - let original_castling_rights = board.castling_rights; + let original_castling_rights = board.castling_rights(); let ply = Move::castle(Wing::KingSide); let record = board.make_move(ply, ValidateMove::Yes)?; @@ -483,7 +479,7 @@ mod tests { assert_eq!(board.get_piece(Square::H8), Some(piece!(Black Rook))); assert_eq!(board.get_piece(Square::G8), None); assert_eq!(board.get_piece(Square::F8), None); - assert_eq!(board.castling_rights, original_castling_rights); + assert_eq!(board.castling_rights(), original_castling_rights); assert_eq!(board.active_color(), Color::Black); Ok(())