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(())