From 70ba30ba1a43af893bdcabbf298b68f72c3dcc2c Mon Sep 17 00:00:00 2001 From: stefiosif Date: Sat, 21 Sep 2024 17:51:39 +0300 Subject: [PATCH] Replace loops with mailbox lookups and move make/unmake tests to game.rs --- src/board/board.rs | 195 +++++++--------------------------- src/board/game.rs | 248 +++++++++++++++++++++++++++++++++++++------ src/board/history.rs | 85 --------------- src/board/state.rs | 20 +++- 4 files changed, 270 insertions(+), 278 deletions(-) diff --git a/src/board/board.rs b/src/board/board.rs index 187d73f..b769f13 100644 --- a/src/board/board.rs +++ b/src/board/board.rs @@ -164,66 +164,6 @@ impl Board { self.state = state; } - pub fn update_game_state( - state: &mut State, - mv: &Move, - color: Color, - pawn_move: bool, - en_passant_square: Option, - ) { - state.set_en_passant_square(en_passant_square); - state.update_castling_state_quiet(mv.src, color); - state.update_castling_state_capture(mv.dst, color.opponent()); - state.update_half_move(mv.move_type, pawn_move); - state.update_full_move(color); - state.change_side(); - } - - pub fn move_piece(&mut self, src: usize, dst: usize) { - let pieces = self.own_pieces(); - pieces - .iter_mut() - .filter(|p| have_common_bit(p.bitboard, square_to_bitboard(src))) - .for_each(|p| { - p.bitboard &= !square_to_bitboard(src); - p.bitboard |= square_to_bitboard(dst); - }); - - //TODO: - // pieces[piece_type].bitboard &= !square_to_bitboard(src); - // pieces[piece_type].bitboard |= square_to_bitboard(dst); - } - - pub fn remove_own_piece(&mut self, square: usize) { - let pieces = self.own_pieces(); - pieces - .iter_mut() - .filter(|p| have_common_bit(p.bitboard, square_to_bitboard(square))) - .for_each(|p| { - p.bitboard &= !square_to_bitboard(square); - }); - } - - pub fn remove_opponent_piece(&mut self, square: usize) { - let pieces = self.opponent_pieces(); - pieces - .iter_mut() - .filter(|p| have_common_bit(p.bitboard, square_to_bitboard(square))) - .for_each(|p| { - p.bitboard &= !square_to_bitboard(square); - }); - } - - pub fn promote_piece(&mut self, square: usize, promote: &Promote) { - let pieces = self.own_pieces(); - match promote { - Promote::Knight => pieces[PieceType::Knight].bitboard |= square_to_bitboard(square), - Promote::Bishop => pieces[PieceType::Bishop].bitboard |= square_to_bitboard(square), - Promote::Rook => pieces[PieceType::Rook].bitboard |= square_to_bitboard(square), - Promote::Queen => pieces[PieceType::Queen].bitboard |= square_to_bitboard(square), - }; - } - pub fn is_pawn_move(&mut self, square: usize) -> bool { let pieces = self.own_pieces(); have_common_bit(square_to_bitboard(square), pieces[PieceType::Pawn].bitboard) @@ -249,6 +189,42 @@ impl Board { Color::Black => (&mut self.black_pieces, &mut self.white_pieces), } } + + pub fn move_piece(&mut self, src: usize, dst: usize, piece_type: PieceType) { + let pieces = self.own_pieces(); + pieces[piece_type].bitboard &= !square_to_bitboard(src); + pieces[piece_type].bitboard |= square_to_bitboard(dst); + } + + pub fn insert_own_piece(&mut self, square: usize, piece_type: PieceType) { + let pieces = self.own_pieces(); + pieces[piece_type].bitboard |= square_to_bitboard(square); + } + + pub fn insert_opponent_piece(&mut self, square: usize, piece_type: PieceType) { + let pieces = self.opponent_pieces(); + pieces[piece_type].bitboard |= square_to_bitboard(square); + } + + pub fn remove_own_piece(&mut self, square: usize, piece_type: PieceType) { + let pieces = self.own_pieces(); + pieces[piece_type].bitboard &= !square_to_bitboard(square); + } + + pub fn remove_opponent_piece(&mut self, square: usize, piece_type: PieceType) { + let pieces = self.opponent_pieces(); + pieces[piece_type].bitboard &= !square_to_bitboard(square); + } + + pub fn promote_piece(&mut self, square: usize, promote: &Promote) { + let pieces = self.own_pieces(); + match promote { + Promote::Knight => pieces[PieceType::Knight].bitboard |= square_to_bitboard(square), + Promote::Bishop => pieces[PieceType::Bishop].bitboard |= square_to_bitboard(square), + Promote::Rook => pieces[PieceType::Rook].bitboard |= square_to_bitboard(square), + Promote::Queen => pieces[PieceType::Queen].bitboard |= square_to_bitboard(square), + }; + } } impl Default for Board { @@ -324,10 +300,7 @@ impl Color { #[cfg(test)] mod tests { - use crate::{ - board::{fen::from_fen, square::Square}, - movegen::{attack_generator::init_attacks, r#move::MoveType}, - }; + use crate::{board::fen::from_fen, movegen::attack_generator::init_attacks}; use super::*; @@ -353,98 +326,4 @@ mod tests { Ok(()) } - - const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1"; - const FEN_QUIET: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2BQ3/1P1PNPP1/R3K2R b KQk - 1 1"; - - #[test] - fn test_make_move_quiet() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let b1c3 = Move::new(Square::F3, Square::E3); - game.make_move(&b1c3); - - assert_eq!(game, from_fen(FEN_QUIET)?); - - Ok(()) - } - - const FEN_CAPTURE: &str = "1r2k2r/2P1pq1p/2npb3/1p3QpP/p3P3/P2B4/1P1PNPP1/R3K2R b KQk - 0 1"; - - #[test] - fn test_make_move_capture() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let f3f5 = Move::new_with_type(Square::F3, Square::F5, MoveType::Capture); - game.make_move(&f3f5); - - assert_eq!(game, from_fen(FEN_CAPTURE)?); - - Ok(()) - } - - const FEN_EN_PASSANT: &str = - "1r2k2r/2P1pq1p/2npb1P1/1p3p2/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; - - #[test] - fn test_make_move_en_passant() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let h5g6 = Move::new_with_type(Square::H5, Square::G6, MoveType::EnPassant); - game.make_move(&h5g6); - - assert_eq!(game, from_fen(FEN_EN_PASSANT)?); - - Ok(()) - } - - const FEN_DOUBLE_PUSH: &str = - "1r2k2r/2P1pq1p/2npb3/1p3ppP/pP2P3/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1"; - - #[test] - fn test_make_move_double_push() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let b2b4 = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush); - game.make_move(&b2b4); - assert_eq!(game, from_fen(FEN_DOUBLE_PUSH)?); - - Ok(()) - } - - const FEN_PROMOTION: &str = "1rQ1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; - - #[test] - fn test_make_move_promotion() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let c7c8 = Move::new_with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen)); - game.make_move(&c7c8); - assert_eq!(game, from_fen(FEN_PROMOTION)?); - - Ok(()) - } - - const FEN_PROMOTION_CAPTURE: &str = - "1Q2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; - - #[test] - fn test_make_move_promotion_capture() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let c7b8 = Move::new_with_type( - Square::C7, - Square::B8, - MoveType::PromotionCapture(Promote::Queen), - ); - game.make_move(&c7b8); - assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE)?); - - Ok(()) - } - - const FEN_CASTLE: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R4RK1 b k - 1 1"; - - #[test] - fn test_make_move_castle() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let e1g1 = Move::new_with_type(Square::E1, Square::G1, MoveType::Castle); - game.make_move(&e1g1); - assert_eq!(game, from_fen(FEN_CASTLE)?); - Ok(()) - } } diff --git a/src/board/game.rs b/src/board/game.rs index 375e555..b761426 100644 --- a/src/board/game.rs +++ b/src/board/game.rs @@ -5,7 +5,6 @@ use crate::{ use String as FenError; use super::{ - bitboard::square_to_bitboard, board::{Board, Color, PieceType}, history::{History, MoveParameters}, mailbox::Mailbox, @@ -55,28 +54,30 @@ impl Game { let pawn_move = board.is_pawn_move(mv.src); let mut en_passant_square = None; + let piece_at_src = mailbox.find_piece_at(mv.src).expect("Expected set piece"); + let piece_at_dst = mailbox.find_piece_at(mv.dst); match &mv.move_type { MoveType::Quiet => { - board.move_piece(mv.src, mv.dst); + board.move_piece(mv.src, mv.dst, piece_at_src); mailbox.set_piece_at(mv.dst, mailbox.find_piece_at(mv.src)); } MoveType::Capture => { - board.move_piece(mv.src, mv.dst); - board.remove_opponent_piece(mv.dst); + board.move_piece(mv.src, mv.dst, piece_at_src); + board.remove_opponent_piece(mv.dst, piece_at_dst.expect("Expected set piece")); mailbox.set_piece_at(mv.dst, mailbox.find_piece_at(mv.src)); } MoveType::EnPassant => { - board.move_piece(mv.src, mv.dst); - let piece_to_remove = match color { + board.move_piece(mv.src, mv.dst, piece_at_src); + let piece_to_remove_sq = match color { Color::White => mv.dst - 8, Color::Black => mv.dst + 8, }; - board.remove_opponent_piece(piece_to_remove); + board.remove_opponent_piece(piece_to_remove_sq, PieceType::Pawn); mailbox.set_piece_at(mv.dst, mailbox.find_piece_at(mv.src)); - mailbox.set_piece_at(piece_to_remove, None); + mailbox.set_piece_at(piece_to_remove_sq, None); } MoveType::DoublePush => { - board.move_piece(mv.src, mv.dst); + board.move_piece(mv.src, mv.dst, piece_at_src); en_passant_square = match color { Color::White => Some(mv.src + 8), Color::Black => Some(mv.src.saturating_sub(8)), @@ -84,24 +85,24 @@ impl Game { mailbox.set_piece_at(mv.dst, mailbox.find_piece_at(mv.src)); } MoveType::Promotion(promote) => { - board.remove_own_piece(mv.src); + board.remove_own_piece(mv.src, piece_at_src); board.promote_piece(mv.dst, promote); mailbox.set_piece_at(mv.dst, Some(promote.into_piece_type())); } MoveType::PromotionCapture(promote) => { - board.remove_own_piece(mv.src); - board.remove_opponent_piece(mv.dst); + board.remove_own_piece(mv.src, piece_at_src); + board.remove_opponent_piece(mv.dst, piece_at_dst.expect("Expected set piece")); board.promote_piece(mv.dst, promote); mailbox.set_piece_at(mv.dst, Some(promote.into_piece_type())); } MoveType::Castle => { - board.move_piece(mv.src, mv.dst); + board.move_piece(mv.src, mv.dst, piece_at_src); let (rook_src, rook_dst) = match mv.dst { Square::C1 | Square::C8 => (mv.dst - 2, mv.dst + 1), Square::G1 | Square::G8 => (mv.dst + 1, mv.dst - 1), _ => return, }; - board.move_piece(rook_src, rook_dst); + board.move_piece(rook_src, rook_dst, PieceType::Rook); board.state.set_castling_ability(color, Castle::None); mailbox.set_piece_at(mv.dst, mailbox.find_piece_at(mv.src)); mailbox.set_piece_at(rook_src, None); @@ -110,7 +111,9 @@ impl Game { } mailbox.set_piece_at(mv.src, None); - Board::update_game_state(&mut board.state, mv, color, pawn_move, en_passant_square); + board + .state + .update_game_state(mv, color, pawn_move, en_passant_square); } pub fn unmake_move(&mut self) { @@ -133,10 +136,10 @@ impl Game { } let mv = move_parameters.mv.unwrap(); - + let piece_at_dst = mailbox.find_piece_at(mv.dst).expect("Expected set piece"); match &mv.move_type { MoveType::Quiet | MoveType::DoublePush => { - board.move_piece(mv.dst, mv.src); + board.move_piece(mv.dst, mv.src, piece_at_dst); mailbox.set_piece_at(mv.src, mailbox.find_piece_at(mv.dst)); mailbox.set_piece_at(mv.dst, None); } @@ -144,9 +147,8 @@ impl Game { let captured_piece = move_parameters .captured_piece .expect("Expected captured piece to unmake Capture"); - board.move_piece(mv.dst, mv.src); - let opponent_pieces = board.opponent_pieces(); - opponent_pieces[captured_piece].bitboard |= square_to_bitboard(mv.dst); + board.move_piece(mv.dst, mv.src, piece_at_dst); + board.insert_opponent_piece(mv.dst, captured_piece); mailbox.set_piece_at(mv.src, move_parameters.moved_piece); mailbox.set_piece_at(mv.dst, Some(captured_piece)); } @@ -154,9 +156,9 @@ impl Game { let promoted_piece = move_parameters .promoted_piece .expect("Expected promoted piece to unmake Promotion"); - let own_pieces = board.own_pieces(); - own_pieces[promoted_piece].bitboard &= !square_to_bitboard(mv.dst); - own_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(mv.src); + board.remove_own_piece(mv.dst, promoted_piece); + board.insert_own_piece(mv.src, PieceType::Pawn); + mailbox.set_piece_at(mv.src, Some(PieceType::Pawn)); mailbox.set_piece_at(mv.dst, None); } @@ -167,34 +169,31 @@ impl Game { let captured_piece = move_parameters .captured_piece .expect("Expected captured piece to unmake PromotionCapture"); - let (own_pieces, opponent_pieces) = board.all_pieces(); - opponent_pieces[captured_piece].bitboard |= square_to_bitboard(mv.dst); - own_pieces[promoted_piece].bitboard &= !square_to_bitboard(mv.dst); - own_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(mv.src); + board.remove_own_piece(mv.dst, promoted_piece); + board.insert_opponent_piece(mv.dst, captured_piece); + board.insert_own_piece(mv.src, PieceType::Pawn); mailbox.set_piece_at(mv.src, Some(PieceType::Pawn)); mailbox.set_piece_at(mv.dst, Some(captured_piece)); } MoveType::EnPassant => { - board.move_piece(mv.dst, mv.src); let enemy_pawn_square = match color_before_move { Color::White => mv.dst - 8, Color::Black => mv.dst + 8, }; - let opponent_pieces = board.opponent_pieces(); - opponent_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(enemy_pawn_square); + board.move_piece(mv.dst, mv.src, piece_at_dst); + board.insert_opponent_piece(enemy_pawn_square, PieceType::Pawn); mailbox.set_piece_at(mv.src, Some(PieceType::Pawn)); mailbox.set_piece_at(enemy_pawn_square, Some(PieceType::Pawn)); } MoveType::Castle => { - board.move_piece(mv.dst, mv.src); let (rook_src, rook_dst) = match mv.dst { Square::C1 | Square::C8 => (mv.dst - 2, mv.dst + 1), Square::G1 | Square::G8 => (mv.dst + 1, mv.dst - 1), _ => return, }; - let own_pieces = board.own_pieces(); - own_pieces[PieceType::Rook].bitboard &= !square_to_bitboard(rook_dst); - own_pieces[PieceType::Rook].bitboard |= square_to_bitboard(rook_src); + board.move_piece(mv.dst, mv.src, piece_at_dst); + board.remove_own_piece(rook_dst, PieceType::Rook); + board.insert_own_piece(rook_src, PieceType::Rook); mailbox.set_piece_at(mv.src, mailbox.find_piece_at(mv.dst)); mailbox.set_piece_at(mv.dst, None); mailbox.set_piece_at(rook_src, Some(PieceType::Rook)); @@ -209,3 +208,184 @@ impl Default for Game { Self::new() } } + +#[cfg(test)] +mod tests { + use crate::{ + board::{fen::from_fen, square::Square}, + movegen::r#move::{MoveType, Promote}, + }; + + use super::*; + + const FEN: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R w KQk g6 0 1"; + const FEN_QUIET: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2BQ3/1P1PNPP1/R3K2R b KQk - 1 1"; + + #[test] + fn test_make_move_quiet() -> Result<(), String> { + let mut game = from_fen(FEN)?; + let b1c3 = Move::new(Square::F3, Square::E3); + game.make_move(&b1c3); + + assert_eq!(game, from_fen(FEN_QUIET)?); + + Ok(()) + } + + const FEN_CAPTURE: &str = "1r2k2r/2P1pq1p/2npb3/1p3QpP/p3P3/P2B4/1P1PNPP1/R3K2R b KQk - 0 1"; + + #[test] + fn test_make_move_capture() -> Result<(), String> { + let mut game = from_fen(FEN)?; + let f3f5 = Move::new_with_type(Square::F3, Square::F5, MoveType::Capture); + game.make_move(&f3f5); + + assert_eq!(game, from_fen(FEN_CAPTURE)?); + + Ok(()) + } + + const FEN_EN_PASSANT: &str = + "1r2k2r/2P1pq1p/2npb1P1/1p3p2/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; + + #[test] + fn test_make_move_en_passant() -> Result<(), String> { + let mut game = from_fen(FEN)?; + let h5g6 = Move::new_with_type(Square::H5, Square::G6, MoveType::EnPassant); + game.make_move(&h5g6); + + assert_eq!(game, from_fen(FEN_EN_PASSANT)?); + + Ok(()) + } + + const FEN_DOUBLE_PUSH: &str = + "1r2k2r/2P1pq1p/2npb3/1p3ppP/pP2P3/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1"; + + #[test] + fn test_make_move_double_push() -> Result<(), String> { + let mut game = from_fen(FEN)?; + let b2b4 = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush); + game.make_move(&b2b4); + assert_eq!(game, from_fen(FEN_DOUBLE_PUSH)?); + + Ok(()) + } + + const FEN_PROMOTION: &str = "1rQ1k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; + + #[test] + fn test_make_move_promotion() -> Result<(), String> { + let mut game = from_fen(FEN)?; + let c7c8 = Move::new_with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen)); + game.make_move(&c7c8); + assert_eq!(game, from_fen(FEN_PROMOTION)?); + + Ok(()) + } + + const FEN_PROMOTION_CAPTURE: &str = + "1Q2k2r/4pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R3K2R b KQk - 0 1"; + + #[test] + fn test_make_move_promotion_capture() -> Result<(), String> { + let mut game = from_fen(FEN)?; + let c7b8 = Move::new_with_type( + Square::C7, + Square::B8, + MoveType::PromotionCapture(Promote::Queen), + ); + game.make_move(&c7b8); + assert_eq!(game, from_fen(FEN_PROMOTION_CAPTURE)?); + + Ok(()) + } + + const FEN_CASTLE: &str = "1r2k2r/2P1pq1p/2npb3/1p3ppP/p3P3/P2B1Q2/1P1PNPP1/R4RK1 b k - 1 1"; + + #[test] + fn test_make_move_castle() -> Result<(), String> { + let mut game = from_fen(FEN)?; + let e1g1 = Move::new_with_type(Square::E1, Square::G1, MoveType::Castle); + game.make_move(&e1g1); + assert_eq!(game, from_fen(FEN_CASTLE)?); + Ok(()) + } + + const FEN_1: &str = "1r2k2r/2P1p1qp/2npb3/1p3p2/p3P1pP/P2B1Q2/1P1PNPP1/R3K2R w KQk - 0 1"; + const FEN_2: &str = "1r2k2r/2P1p1qp/2npb3/1p3p2/pP2P1pP/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1"; + + #[test] + fn test_unmake_quiet_and_double_push() -> Result<(), String> { + let mut game = from_fen(FEN_1)?; + let game_before_make = game.clone(); + let mv = Move::new_with_type(Square::B2, Square::B3, MoveType::Quiet); + game.make_move(&mv); + game.unmake_move(); + + assert_eq!(game_before_make, game); + + let mv = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush); + game.make_move(&mv); + game.unmake_move(); + + assert_eq!(game_before_make, game); + + Ok(()) + } + + #[test] + fn test_unmake_capture_and_promotion() -> Result<(), String> { + let mut game = from_fen(FEN_1)?; + let game_before_make = game.clone(); + let mv = Move::new_with_type(Square::D3, Square::B5, MoveType::Capture); + game.make_move(&mv); + game.unmake_move(); + + assert_eq!(game_before_make, game); + + let mv = Move::new_with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen)); + game.make_move(&mv); + game.unmake_move(); + + assert_eq!(game_before_make, game); + + let mv = Move::new_with_type( + Square::C7, + Square::B8, + MoveType::PromotionCapture(Promote::Queen), + ); + game.make_move(&mv); + game.unmake_move(); + + assert_eq!(game_before_make, game); + + Ok(()) + } + + #[test] + fn test_unmake_en_passant() -> Result<(), String> { + let mut game = from_fen(FEN_2)?; + let game_before_make = game.clone(); + let mv = Move::new_with_type(Square::A4, Square::B3, MoveType::EnPassant); + game.make_move(&mv); + game.unmake_move(); + + assert_eq!(game_before_make, game); + + Ok(()) + } + + #[test] + fn test_unmake_castle() -> Result<(), String> { + let mut game = from_fen(FEN_1)?; + let game_before_make = game.clone(); + let mv = Move::new_with_type(Square::E1, Square::C1, MoveType::Castle); + game.make_move(&mv); + game.unmake_move(); + + assert_eq!(game_before_make, game); + + Ok(()) + } +} diff --git a/src/board/history.rs b/src/board/history.rs index c2b52c9..a3d7eae 100644 --- a/src/board/history.rs +++ b/src/board/history.rs @@ -95,88 +95,3 @@ impl Default for MoveParameters { Self::new() } } - -#[cfg(test)] -mod tests { - use crate::{ - board::{fen::from_fen, square::Square}, - movegen::r#move::{Move, MoveType, Promote}, - }; - - const FEN: &str = "1r2k2r/2P1p1qp/2npb3/1p3p2/p3P1pP/P2B1Q2/1P1PNPP1/R3K2R w KQk - 0 1"; - const FEN_2: &str = "1r2k2r/2P1p1qp/2npb3/1p3p2/pP2P1pP/P2B1Q2/3PNPP1/R3K2R b KQk b3 0 1"; - - #[test] - fn test_unmake_quiet_and_double_push() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let game_before_make = game.clone(); - let mv = Move::new_with_type(Square::B2, Square::B3, MoveType::Quiet); - game.make_move(&mv); - game.unmake_move(); - - assert_eq!(game_before_make, game); - - let mv = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush); - game.make_move(&mv); - game.unmake_move(); - - assert_eq!(game_before_make, game); - - Ok(()) - } - - #[test] - fn test_unmake_capture_and_promotion() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let game_before_make = game.clone(); - let mv = Move::new_with_type(Square::D3, Square::B5, MoveType::Capture); - game.make_move(&mv); - game.unmake_move(); - - assert_eq!(game_before_make, game); - - let mv = Move::new_with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen)); - game.make_move(&mv); - game.unmake_move(); - - assert_eq!(game_before_make, game); - - let mv = Move::new_with_type( - Square::C7, - Square::B8, - MoveType::PromotionCapture(Promote::Queen), - ); - game.make_move(&mv); - game.unmake_move(); - - assert_eq!(game_before_make, game); - - Ok(()) - } - - #[test] - fn test_unmake_en_passant() -> Result<(), String> { - let mut game = from_fen(FEN_2)?; - let game_before_make = game.clone(); - let mv = Move::new_with_type(Square::A4, Square::B3, MoveType::EnPassant); - game.make_move(&mv); - game.unmake_move(); - - assert_eq!(game_before_make, game); - - Ok(()) - } - - #[test] - fn test_unmake_castle() -> Result<(), String> { - let mut game = from_fen(FEN)?; - let game_before_make = game.clone(); - let mv = Move::new_with_type(Square::E1, Square::C1, MoveType::Castle); - game.make_move(&mv); - game.unmake_move(); - - assert_eq!(game_before_make, game); - - Ok(()) - } -} diff --git a/src/board/state.rs b/src/board/state.rs index ca8e0b6..4d7e5b6 100644 --- a/src/board/state.rs +++ b/src/board/state.rs @@ -1,4 +1,7 @@ -use crate::{board::board::Color, movegen::r#move::MoveType}; +use crate::{ + board::board::Color, + movegen::r#move::{Move, MoveType}, +}; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct State { @@ -36,6 +39,21 @@ impl State { } } + pub fn update_game_state( + &mut self, + mv: &Move, + color: Color, + pawn_move: bool, + en_passant_square: Option, + ) { + self.set_en_passant_square(en_passant_square); + self.update_castling_state_quiet(mv.src, color); + self.update_castling_state_capture(mv.dst, color.opponent()); + self.update_half_move(mv.move_type, pawn_move); + self.update_full_move(color); + self.change_side(); + } + pub const fn en_passant_square(&self) -> Option { self.en_passant_square }