From 7accc28aba6212eeccbe5f3b5475e11e0b54990b Mon Sep 17 00:00:00 2001 From: stefiosif Date: Tue, 18 Feb 2025 20:12:29 +0200 Subject: [PATCH] Simplify History struct into a Vec --- src/board/fen.rs | 3 +- src/board/game.rs | 133 ++++++++++++++++++++++++++++++++----------- src/board/history.rs | 101 -------------------------------- src/board/mod.rs | 1 - 4 files changed, 100 insertions(+), 138 deletions(-) delete mode 100644 src/board/history.rs diff --git a/src/board/fen.rs b/src/board/fen.rs index 8cc54b7..d227e4b 100644 --- a/src/board/fen.rs +++ b/src/board/fen.rs @@ -33,7 +33,7 @@ pub fn from_fen(fen: &str) -> Result { Ok(Game { board, - history: History::new(), + unmake_stack: Vec::new(), mailbox, hash, tt: TranspositionTable::new(), @@ -138,7 +138,6 @@ fn castling_ability(castling: &str) -> Result<[Castle; 2], FenError> { use std::collections::HashMap; use super::bitboard::square_to_bitboard; -use super::history::History; use super::mailbox::Mailbox; use super::zobrist::zobrist_keys; diff --git a/src/board/game.rs b/src/board/game.rs index e0afa04..a57c727 100644 --- a/src/board/game.rs +++ b/src/board/game.rs @@ -7,7 +7,6 @@ use String as FenError; use super::{ board::{Board, Color, PieceType}, - history::{History, MoveInfo}, mailbox::Mailbox, square::Square, state::Castle, @@ -25,7 +24,7 @@ impl PartialEq for Game { #[derive(Debug, Clone, Eq)] pub struct Game { pub board: Board, - pub history: History, + pub unmake_stack: Vec, pub mailbox: Mailbox, pub hash: ZobristHash, pub tt: TranspositionTable, @@ -35,11 +34,13 @@ pub struct Game { impl Game { pub fn new() -> Self { + let board = Board::startpos(); + Self { board: Board::startpos(), - history: History::new(), - mailbox: Mailbox::from_board(&Board::startpos()), - hash: zobrist_keys().calculate_hash(&Board::startpos()), + unmake_stack: Vec::new(), + mailbox: Mailbox::from_board(&board), + hash: zobrist_keys().calculate_hash(&board), tt: TranspositionTable::new(), killer: [None; MAX_DEPTH as usize], history_heuristic: [[[0; 64]; 64]; 2], @@ -59,7 +60,7 @@ impl Game { } pub fn make_move(&mut self, mv: &Move) { - let move_info = MoveInfo::new() + let unmake_info = UnmakeState::new() .with_move(*mv) .with_castling_ability(self.board.state.castling_ability) .with_en_passant_square(self.board.state.en_passant_square) @@ -69,7 +70,7 @@ impl Game { .with_promoted_piece(mv, self.current_player()) .with_zobrist_hash(&self.hash); - self.history.push_move_info(move_info); + self.unmake_stack.push(unmake_info); let board = &mut self.board; let hash = &mut self.hash; @@ -178,29 +179,24 @@ impl Game { pub fn unmake_move(&mut self) { let board = &mut self.board; let mailbox = &mut self.mailbox; - let move_info = &mut self - .history - .pop_move_info() - .expect("History stack is empty"); + let unmake_info = self.unmake_stack.pop().expect("Stack empty before pop."); let color_before_move = board.state.change_side(); board.state.revert_full_move(color_before_move); - board.state.en_passant_square = move_info.en_passant_square; + board.state.en_passant_square = unmake_info.en_passant_square; - if let Some(hash) = move_info.zobrist_hash { + if let Some(hash) = unmake_info.zobrist_hash { self.hash = hash; } - if let Some(new_castling_ability) = move_info.castling_ability { + if let Some(new_castling_ability) = unmake_info.castling_ability { board.state.castling_ability = new_castling_ability; } - if let Some(new_halfmove_clock) = move_info.halfmove_clock { + if let Some(new_halfmove_clock) = unmake_info.halfmove_clock { board.state.halfmove_clock = new_halfmove_clock; } - let mv = move_info - .mv - .expect("Expected move parameters from history stack"); + let mv = unmake_info.mv.expect("Expected move info from stack."); let piece_at_dst = mailbox.piece_at(mv.dst()).expect("No piece at dst."); match MoveType::from_move(&mv) { MoveType::Quiet | MoveType::DoublePush => { @@ -209,19 +205,19 @@ impl Game { mailbox.set_piece_at(mv.dst(), None); } MoveType::Capture => { - let captured_piece = move_info + let captured_piece = unmake_info .captured_piece .expect("Expected captured piece to unmake Capture"); board.move_piece(mv.dst(), mv.src(), piece_at_dst.0); board.insert_opponent_piece(mv.dst(), captured_piece.0); - mailbox.set_piece_at(mv.src(), move_info.moving_piece); + mailbox.set_piece_at(mv.src(), unmake_info.moving_piece); mailbox.set_piece_at(mv.dst(), Some(captured_piece)); } MoveType::PromotionKnight | MoveType::PromotionBishop | MoveType::PromotionRook | MoveType::PromotionQueen => { - let promoted_piece = move_info + let promoted_piece = unmake_info .promoted_piece .expect("Expected promoted piece to unmake Promotion"); board.remove_own_piece(mv.dst(), promoted_piece.0); @@ -234,10 +230,10 @@ impl Game { | MoveType::PromotionCaptureBishop | MoveType::PromotionCaptureRook | MoveType::PromotionCaptureQueen => { - let promoted_piece = move_info + let promoted_piece = unmake_info .promoted_piece .expect("Expected promoted piece to unmake PromotionCapture"); - let captured_piece = move_info + let captured_piece = unmake_info .captured_piece .expect("Expected captured piece to unmake PromotionCapture"); board.remove_own_piece(mv.dst(), promoted_piece.0); @@ -277,13 +273,13 @@ impl Game { } pub fn make_null_move(&mut self) { - let move_info = MoveInfo::new() + let unmake_info = UnmakeState::new() .with_castling_ability(self.board.state.castling_ability) .with_en_passant_square(self.board.state.en_passant_square) .with_halfmove_clock(self.board.state.halfmove_clock) .with_zobrist_hash(&self.hash); - self.history.push_move_info(move_info); + self.unmake_stack.push(unmake_info); self.hash.update_side_to_move_key(); self.board .state @@ -292,25 +288,22 @@ impl Game { pub fn unmake_null_move(&mut self) { let board = &mut self.board; - let move_info = &mut self - .history - .pop_move_info() - .expect("History stack is empty"); + let unmake_info = self.unmake_stack.pop().expect("Stack empty before pop."); let color_before_move = board.state.change_side(); board.state.revert_full_move(color_before_move); - board.state.en_passant_square = move_info.en_passant_square; + board.state.en_passant_square = unmake_info.en_passant_square; - if let Some(hash) = move_info.zobrist_hash { + if let Some(hash) = unmake_info.zobrist_hash { self.hash = hash; } - if let Some(new_castling_ability) = move_info.castling_ability { + if let Some(new_castling_ability) = unmake_info.castling_ability { board.state.castling_ability = new_castling_ability; } - if let Some(new_halfmove_clock) = move_info.halfmove_clock { + if let Some(new_halfmove_clock) = unmake_info.halfmove_clock { board.state.halfmove_clock = new_halfmove_clock; } } @@ -320,7 +313,79 @@ impl Game { return false; } - self.history.in_repetition(self.hash) + self.unmake_stack + .iter() + .rev() + .skip(1) + .step_by(2) + .any(|mp| mp.zobrist_hash.expect("State without hash.") == self.hash) + } +} + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub struct UnmakeState { + pub mv: Option, + pub moving_piece: Option<(PieceType, Color)>, + pub captured_piece: Option<(PieceType, Color)>, + pub promoted_piece: Option<(PieceType, Color)>, + pub castling_ability: Option<[Castle; 2]>, + pub en_passant_square: Option, + pub halfmove_clock: Option, + pub zobrist_hash: Option, +} + +impl UnmakeState { + pub fn new() -> Self { + Self::default() + } + + pub const fn with_move(mut self, mv: Move) -> Self { + self.mv = Some(mv); + self + } + + pub const fn with_castling_ability(mut self, castling_ability: [Castle; 2]) -> Self { + self.castling_ability = Some(castling_ability); + self + } + + pub const fn with_en_passant_square(mut self, en_passant_square: Option) -> Self { + self.en_passant_square = en_passant_square; + self + } + + pub const fn with_halfmove_clock(mut self, halfmove_clock: u8) -> Self { + self.halfmove_clock = Some(halfmove_clock); + self + } + + pub const fn with_moving_piece(mut self, mailbox: &Mailbox, mv: &Move) -> Self { + self.moving_piece = mailbox.piece_at(mv.src()); + self + } + + pub fn with_captured_piece(mut self, mailbox: &Mailbox, mv: &Move) -> Self { + if let MoveType::Capture + | MoveType::PromotionCaptureKnight + | MoveType::PromotionCaptureBishop + | MoveType::PromotionCaptureRook + | MoveType::PromotionCaptureQueen = MoveType::from_move(mv) + { + self.captured_piece = mailbox.piece_at(mv.dst()) + } + self + } + + pub fn with_promoted_piece(mut self, mv: &Move, color: Color) -> Self { + if mv.is_promotion() { + self.promoted_piece = Some((mv.promotion_type().into_piece_type(), color)); + } + self + } + + pub fn with_zobrist_hash(mut self, zobrist_hash: &ZobristHash) -> Self { + self.zobrist_hash = Some(zobrist_hash.to_owned()); + self } } diff --git a/src/board/history.rs b/src/board/history.rs deleted file mode 100644 index fb21a77..0000000 --- a/src/board/history.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::movegen::r#move::{Move, MoveType}; - -use super::{ - board::{Color, PieceType}, - mailbox::Mailbox, - state::Castle, - zobrist::ZobristHash, -}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct History(Vec); - -impl History { - pub const fn new() -> Self { - Self(Vec::new()) - } - - pub fn push_move_info(&mut self, move_info: MoveInfo) { - self.0.push(move_info); - } - - pub fn pop_move_info(&mut self) -> Option { - self.0.pop() - } - - pub fn in_repetition(&self, hash: ZobristHash) -> bool { - self.0 - .iter() - .rev() - .skip(1) - .step_by(2) - .any(|mp| mp.zobrist_hash.expect("State without hash") == hash) - } -} - -#[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct MoveInfo { - pub mv: Option, - pub moving_piece: Option<(PieceType, Color)>, - pub captured_piece: Option<(PieceType, Color)>, - pub promoted_piece: Option<(PieceType, Color)>, - pub castling_ability: Option<[Castle; 2]>, - pub en_passant_square: Option, - pub halfmove_clock: Option, - pub zobrist_hash: Option, -} - -impl MoveInfo { - pub fn new() -> Self { - Self::default() - } - - pub const fn with_move(mut self, mv: Move) -> Self { - self.mv = Some(mv); - self - } - - pub const fn with_castling_ability(mut self, castling_ability: [Castle; 2]) -> Self { - self.castling_ability = Some(castling_ability); - self - } - - pub const fn with_en_passant_square(mut self, en_passant_square: Option) -> Self { - self.en_passant_square = en_passant_square; - self - } - - pub const fn with_halfmove_clock(mut self, halfmove_clock: u8) -> Self { - self.halfmove_clock = Some(halfmove_clock); - self - } - - pub const fn with_moving_piece(mut self, mailbox: &Mailbox, mv: &Move) -> Self { - self.moving_piece = mailbox.piece_at(mv.src()); - self - } - - pub fn with_captured_piece(mut self, mailbox: &Mailbox, mv: &Move) -> Self { - if let MoveType::Capture - | MoveType::PromotionCaptureKnight - | MoveType::PromotionCaptureBishop - | MoveType::PromotionCaptureRook - | MoveType::PromotionCaptureQueen = MoveType::from_move(mv) - { - self.captured_piece = mailbox.piece_at(mv.dst()) - } - self - } - - pub fn with_promoted_piece(mut self, mv: &Move, color: Color) -> Self { - if mv.is_promotion() { - self.promoted_piece = Some((mv.promotion_type().into_piece_type(), color)); - } - self - } - - pub fn with_zobrist_hash(mut self, zobrist_hash: &ZobristHash) -> Self { - self.zobrist_hash = Some(zobrist_hash.to_owned()); - self - } -} diff --git a/src/board/mod.rs b/src/board/mod.rs index 4d0775b..7c60557 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -2,7 +2,6 @@ pub mod bitboard; pub mod board; pub mod fen; pub mod game; -pub mod history; pub mod mailbox; pub mod square; pub mod state;