Implement unmake move
This commit is contained in:
205
src/board/history.rs
Normal file
205
src/board/history.rs
Normal file
@@ -0,0 +1,205 @@
|
||||
use crate::movegen::r#move::{Move, MoveType, Promote};
|
||||
|
||||
use super::{
|
||||
board::{Board, Color, PieceType},
|
||||
state::{Castle, State},
|
||||
};
|
||||
|
||||
pub struct History {
|
||||
pub move_parameters: Vec<MoveParameters>,
|
||||
}
|
||||
|
||||
impl History {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
move_parameters: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_move_parameters(&mut self, move_parameters: MoveParameters) {
|
||||
self.move_parameters.push(move_parameters)
|
||||
}
|
||||
|
||||
pub fn pop_move_parameters(&mut self) -> Option<MoveParameters> {
|
||||
self.move_parameters.pop()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MoveParameters {
|
||||
pub mv: Option<Move>,
|
||||
pub captured_piece: Option<PieceType>,
|
||||
pub promoted_piece: Option<PieceType>,
|
||||
pub castling_ability: Option<[Castle; 2]>,
|
||||
pub en_passant_target_square: Option<usize>,
|
||||
pub halfmove_clock: Option<u8>,
|
||||
}
|
||||
|
||||
impl MoveParameters {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
mv: None,
|
||||
captured_piece: None,
|
||||
promoted_piece: None,
|
||||
castling_ability: None,
|
||||
en_passant_target_square: None,
|
||||
halfmove_clock: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_move(&mut self, mv: Move) {
|
||||
self.mv = Some(mv)
|
||||
}
|
||||
|
||||
pub fn add_captured_piece(&mut self, board: &Board, dst: usize, color: Color) {
|
||||
self.captured_piece = board.piece_type_at(dst, color);
|
||||
}
|
||||
|
||||
pub fn add_promoted_piece(&mut self, piece_type: Promote) {
|
||||
self.promoted_piece = Some(piece_type.into_piece_type())
|
||||
}
|
||||
|
||||
pub fn add_irreversible_parameters(&mut self, state: State) {
|
||||
self.castling_ability = Some(state.castling_ability);
|
||||
self.en_passant_target_square = state.en_passant_target_square;
|
||||
self.halfmove_clock = Some(state.halfmove_clock)
|
||||
}
|
||||
|
||||
pub fn add_capture_and_promotion_piece(&mut self, board: &Board, mv: Move, color: Color) {
|
||||
match mv.move_type {
|
||||
MoveType::Capture => {
|
||||
self.add_captured_piece(&board, mv.dst, Color::opponent_color(color))
|
||||
}
|
||||
MoveType::Promotion(piece) => self.add_promoted_piece(piece),
|
||||
MoveType::PromotionCapture(piece) => {
|
||||
self.add_promoted_piece(piece);
|
||||
self.add_captured_piece(&board, mv.dst, Color::opponent_color(color));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
board::{fen::from_fen, history::MoveParameters, square::Square},
|
||||
movegen::r#move::{Move, MoveType, Promote},
|
||||
};
|
||||
|
||||
const FEN: &str = "1r2k2r/2P1p1qp/2npb3/1p3p2/p3P1pP/P2B1Q2/1P1PNPP1/R3K2R w KQk - 0 1";
|
||||
|
||||
#[test]
|
||||
fn test_unmake_quiet_and_double_push() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN)?;
|
||||
let board_before_make = game.board.clone();
|
||||
|
||||
let mv = Move::new_with_type(Square::B2, Square::B3, MoveType::Quiet);
|
||||
let mut move_parameters = MoveParameters::new();
|
||||
move_parameters.add_move(mv);
|
||||
move_parameters.add_irreversible_parameters(game.board.state);
|
||||
move_parameters.add_capture_and_promotion_piece(&game.board, mv, game.current_player());
|
||||
|
||||
game.board.make_move(&mv);
|
||||
game.board.unmake_move(move_parameters);
|
||||
|
||||
assert_eq!(board_before_make, game.board);
|
||||
|
||||
let mv = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush);
|
||||
let mut move_parameters = MoveParameters::new();
|
||||
move_parameters.add_move(mv);
|
||||
move_parameters.add_irreversible_parameters(game.board.state);
|
||||
move_parameters.add_capture_and_promotion_piece(&game.board, mv, game.current_player());
|
||||
|
||||
game.board.make_move(&mv);
|
||||
game.board.unmake_move(move_parameters);
|
||||
|
||||
assert_eq!(board_before_make, game.board);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unmake_capture_and_promotion() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN)?;
|
||||
let board_before_make = game.board.clone();
|
||||
|
||||
let mv = Move::new_with_type(Square::D3, Square::B5, MoveType::Capture);
|
||||
let mut move_parameters = MoveParameters::new();
|
||||
move_parameters.add_move(mv);
|
||||
move_parameters.add_irreversible_parameters(game.board.state);
|
||||
move_parameters.add_capture_and_promotion_piece(&game.board, mv, game.current_player());
|
||||
|
||||
game.board.make_move(&mv);
|
||||
game.board.unmake_move(move_parameters);
|
||||
|
||||
assert_eq!(board_before_make, game.board);
|
||||
|
||||
let mv = Move::new_with_type(Square::C7, Square::C8, MoveType::Promotion(Promote::Queen));
|
||||
let mut move_parameters = MoveParameters::new();
|
||||
move_parameters.add_move(mv);
|
||||
move_parameters.add_irreversible_parameters(game.board.state);
|
||||
move_parameters.add_capture_and_promotion_piece(&game.board, mv, game.current_player());
|
||||
|
||||
game.board.make_move(&mv);
|
||||
game.board.unmake_move(move_parameters);
|
||||
|
||||
assert_eq!(board_before_make, game.board);
|
||||
|
||||
let mv = Move::new_with_type(
|
||||
Square::C7,
|
||||
Square::B8,
|
||||
MoveType::PromotionCapture(Promote::Queen),
|
||||
);
|
||||
let mut move_parameters = MoveParameters::new();
|
||||
move_parameters.add_move(mv);
|
||||
move_parameters.add_irreversible_parameters(game.board.state);
|
||||
move_parameters.add_capture_and_promotion_piece(&game.board, mv, game.current_player());
|
||||
|
||||
game.board.make_move(&mv);
|
||||
game.board.unmake_move(move_parameters);
|
||||
|
||||
assert_eq!(board_before_make, game.board);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unmake_en_passant() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN)?;
|
||||
let mv = Move::new_with_type(Square::B2, Square::B4, MoveType::DoublePush);
|
||||
game.board.make_move(&mv);
|
||||
let board_before_make = game.board.clone();
|
||||
|
||||
let mv = Move::new_with_type(Square::A4, Square::B3, MoveType::EnPassant);
|
||||
let mut move_parameters = MoveParameters::new();
|
||||
move_parameters.add_move(mv);
|
||||
move_parameters.add_irreversible_parameters(game.board.state);
|
||||
move_parameters.add_capture_and_promotion_piece(&game.board, mv, game.current_player());
|
||||
|
||||
game.board.make_move(&mv);
|
||||
game.board.unmake_move(move_parameters);
|
||||
|
||||
assert_eq!(board_before_make, game.board);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unmake_castle() -> Result<(), String> {
|
||||
let mut game = from_fen(FEN)?;
|
||||
let board_before_make = game.board.clone();
|
||||
|
||||
let mv = Move::new_with_type(Square::E1, Square::C1, MoveType::Castle);
|
||||
let mut move_parameters = MoveParameters::new();
|
||||
move_parameters.add_move(mv);
|
||||
move_parameters.add_irreversible_parameters(game.board.state);
|
||||
move_parameters.add_capture_and_promotion_piece(&game.board, mv, game.current_player());
|
||||
|
||||
game.board.make_move(&mv);
|
||||
game.board.unmake_move(move_parameters);
|
||||
|
||||
assert_eq!(board_before_make, game.board);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -2,5 +2,6 @@ pub mod bitboard;
|
||||
pub mod board;
|
||||
pub mod fen;
|
||||
pub mod game;
|
||||
pub mod history;
|
||||
pub mod square;
|
||||
pub mod state;
|
||||
|
||||
@@ -3,6 +3,7 @@ use core::fmt;
|
||||
use crate::board::{
|
||||
bitboard::{have_common_bit, square_to_bitboard},
|
||||
board::{Board, Color, Piece, PieceType},
|
||||
history::MoveParameters,
|
||||
square::{coords_to_square, square_to_algebraic, Square},
|
||||
state::{Castle, State},
|
||||
};
|
||||
@@ -170,6 +171,66 @@ impl Board {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmake_move(&mut self, move_parameters: MoveParameters) {
|
||||
let color_before_move = self.state.change_side();
|
||||
self.state.revert_full_move(color_before_move);
|
||||
self.state.en_passant_target_square = move_parameters.en_passant_target_square;
|
||||
|
||||
if let Some(new_castling_ability) = move_parameters.castling_ability {
|
||||
self.state.castling_ability = new_castling_ability;
|
||||
}
|
||||
|
||||
if let Some(new_halfmove_clock) = move_parameters.halfmove_clock {
|
||||
self.state.halfmove_clock = new_halfmove_clock;
|
||||
}
|
||||
|
||||
let mv = move_parameters.mv.unwrap();
|
||||
let (own_pieces, opponent_pieces) = match color_before_move {
|
||||
Color::White => (&mut self.white_pieces, &mut self.black_pieces),
|
||||
Color::Black => (&mut self.black_pieces, &mut self.white_pieces),
|
||||
};
|
||||
|
||||
match mv.move_type {
|
||||
MoveType::Quiet | MoveType::DoublePush => {
|
||||
Self::move_piece(mv.dst, mv.src, own_pieces);
|
||||
}
|
||||
MoveType::Capture | MoveType::Promotion(_) | MoveType::PromotionCapture(_) => {
|
||||
if let (Some(captured_piece_type), Some(promoted_piece_type)) = (
|
||||
move_parameters.captured_piece,
|
||||
move_parameters.promoted_piece,
|
||||
) {
|
||||
opponent_pieces[captured_piece_type].bitboard |= square_to_bitboard(mv.dst);
|
||||
own_pieces[promoted_piece_type].bitboard &= !square_to_bitboard(mv.dst);
|
||||
own_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(mv.src);
|
||||
} else if let Some(captured_piece_type) = move_parameters.captured_piece {
|
||||
Self::move_piece(mv.dst, mv.src, own_pieces);
|
||||
opponent_pieces[captured_piece_type].bitboard |= square_to_bitboard(mv.dst);
|
||||
} else if let Some(promoted_piece_type) = move_parameters.promoted_piece {
|
||||
own_pieces[promoted_piece_type].bitboard &= !square_to_bitboard(mv.dst);
|
||||
own_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(mv.src);
|
||||
}
|
||||
}
|
||||
MoveType::EnPassant => {
|
||||
Self::move_piece(mv.dst, mv.src, own_pieces);
|
||||
let enemy_pawn_square = match color_before_move {
|
||||
Color::White => mv.dst - 8,
|
||||
Color::Black => mv.dst + 8,
|
||||
};
|
||||
opponent_pieces[PieceType::Pawn].bitboard |= square_to_bitboard(enemy_pawn_square)
|
||||
}
|
||||
MoveType::Castle => {
|
||||
Self::move_piece(mv.dst, mv.src, own_pieces);
|
||||
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,
|
||||
};
|
||||
own_pieces[PieceType::Rook].bitboard &= !square_to_bitboard(rook_dst);
|
||||
own_pieces[PieceType::Rook].bitboard |= square_to_bitboard(rook_src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_game_state(state: &mut State, mv: &Move, color: Color, pawn_move: bool) {
|
||||
state.set_en_passant_target_square(None);
|
||||
state.update_castling_state_quiet(mv.src, color);
|
||||
|
||||
Reference in New Issue
Block a user