Simplify History struct into a Vec

This commit is contained in:
stefiosif
2025-02-18 20:12:29 +02:00
parent 0cd2cffd10
commit 7accc28aba
4 changed files with 100 additions and 138 deletions

View File

@@ -33,7 +33,7 @@ pub fn from_fen(fen: &str) -> Result<Game, FenError> {
Ok(Game { Ok(Game {
board, board,
history: History::new(), unmake_stack: Vec::new(),
mailbox, mailbox,
hash, hash,
tt: TranspositionTable::new(), tt: TranspositionTable::new(),
@@ -138,7 +138,6 @@ fn castling_ability(castling: &str) -> Result<[Castle; 2], FenError> {
use std::collections::HashMap; use std::collections::HashMap;
use super::bitboard::square_to_bitboard; use super::bitboard::square_to_bitboard;
use super::history::History;
use super::mailbox::Mailbox; use super::mailbox::Mailbox;
use super::zobrist::zobrist_keys; use super::zobrist::zobrist_keys;

View File

@@ -7,7 +7,6 @@ use String as FenError;
use super::{ use super::{
board::{Board, Color, PieceType}, board::{Board, Color, PieceType},
history::{History, MoveInfo},
mailbox::Mailbox, mailbox::Mailbox,
square::Square, square::Square,
state::Castle, state::Castle,
@@ -25,7 +24,7 @@ impl PartialEq for Game {
#[derive(Debug, Clone, Eq)] #[derive(Debug, Clone, Eq)]
pub struct Game { pub struct Game {
pub board: Board, pub board: Board,
pub history: History, pub unmake_stack: Vec<UnmakeState>,
pub mailbox: Mailbox, pub mailbox: Mailbox,
pub hash: ZobristHash, pub hash: ZobristHash,
pub tt: TranspositionTable, pub tt: TranspositionTable,
@@ -35,11 +34,13 @@ pub struct Game {
impl Game { impl Game {
pub fn new() -> Self { pub fn new() -> Self {
let board = Board::startpos();
Self { Self {
board: Board::startpos(), board: Board::startpos(),
history: History::new(), unmake_stack: Vec::new(),
mailbox: Mailbox::from_board(&Board::startpos()), mailbox: Mailbox::from_board(&board),
hash: zobrist_keys().calculate_hash(&Board::startpos()), hash: zobrist_keys().calculate_hash(&board),
tt: TranspositionTable::new(), tt: TranspositionTable::new(),
killer: [None; MAX_DEPTH as usize], killer: [None; MAX_DEPTH as usize],
history_heuristic: [[[0; 64]; 64]; 2], history_heuristic: [[[0; 64]; 64]; 2],
@@ -59,7 +60,7 @@ impl Game {
} }
pub fn make_move(&mut self, mv: &Move) { pub fn make_move(&mut self, mv: &Move) {
let move_info = MoveInfo::new() let unmake_info = UnmakeState::new()
.with_move(*mv) .with_move(*mv)
.with_castling_ability(self.board.state.castling_ability) .with_castling_ability(self.board.state.castling_ability)
.with_en_passant_square(self.board.state.en_passant_square) .with_en_passant_square(self.board.state.en_passant_square)
@@ -69,7 +70,7 @@ impl Game {
.with_promoted_piece(mv, self.current_player()) .with_promoted_piece(mv, self.current_player())
.with_zobrist_hash(&self.hash); .with_zobrist_hash(&self.hash);
self.history.push_move_info(move_info); self.unmake_stack.push(unmake_info);
let board = &mut self.board; let board = &mut self.board;
let hash = &mut self.hash; let hash = &mut self.hash;
@@ -178,29 +179,24 @@ impl Game {
pub fn unmake_move(&mut self) { pub fn unmake_move(&mut self) {
let board = &mut self.board; let board = &mut self.board;
let mailbox = &mut self.mailbox; let mailbox = &mut self.mailbox;
let move_info = &mut self let unmake_info = self.unmake_stack.pop().expect("Stack empty before pop.");
.history
.pop_move_info()
.expect("History stack is empty");
let color_before_move = board.state.change_side(); let color_before_move = board.state.change_side();
board.state.revert_full_move(color_before_move); 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; 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; 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; board.state.halfmove_clock = new_halfmove_clock;
} }
let mv = move_info let mv = unmake_info.mv.expect("Expected move info from stack.");
.mv
.expect("Expected move parameters from history stack");
let piece_at_dst = mailbox.piece_at(mv.dst()).expect("No piece at dst."); let piece_at_dst = mailbox.piece_at(mv.dst()).expect("No piece at dst.");
match MoveType::from_move(&mv) { match MoveType::from_move(&mv) {
MoveType::Quiet | MoveType::DoublePush => { MoveType::Quiet | MoveType::DoublePush => {
@@ -209,19 +205,19 @@ impl Game {
mailbox.set_piece_at(mv.dst(), None); mailbox.set_piece_at(mv.dst(), None);
} }
MoveType::Capture => { MoveType::Capture => {
let captured_piece = move_info let captured_piece = unmake_info
.captured_piece .captured_piece
.expect("Expected captured piece to unmake Capture"); .expect("Expected captured piece to unmake Capture");
board.move_piece(mv.dst(), mv.src(), piece_at_dst.0); board.move_piece(mv.dst(), mv.src(), piece_at_dst.0);
board.insert_opponent_piece(mv.dst(), captured_piece.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)); mailbox.set_piece_at(mv.dst(), Some(captured_piece));
} }
MoveType::PromotionKnight MoveType::PromotionKnight
| MoveType::PromotionBishop | MoveType::PromotionBishop
| MoveType::PromotionRook | MoveType::PromotionRook
| MoveType::PromotionQueen => { | MoveType::PromotionQueen => {
let promoted_piece = move_info let promoted_piece = unmake_info
.promoted_piece .promoted_piece
.expect("Expected promoted piece to unmake Promotion"); .expect("Expected promoted piece to unmake Promotion");
board.remove_own_piece(mv.dst(), promoted_piece.0); board.remove_own_piece(mv.dst(), promoted_piece.0);
@@ -234,10 +230,10 @@ impl Game {
| MoveType::PromotionCaptureBishop | MoveType::PromotionCaptureBishop
| MoveType::PromotionCaptureRook | MoveType::PromotionCaptureRook
| MoveType::PromotionCaptureQueen => { | MoveType::PromotionCaptureQueen => {
let promoted_piece = move_info let promoted_piece = unmake_info
.promoted_piece .promoted_piece
.expect("Expected promoted piece to unmake PromotionCapture"); .expect("Expected promoted piece to unmake PromotionCapture");
let captured_piece = move_info let captured_piece = unmake_info
.captured_piece .captured_piece
.expect("Expected captured piece to unmake PromotionCapture"); .expect("Expected captured piece to unmake PromotionCapture");
board.remove_own_piece(mv.dst(), promoted_piece.0); board.remove_own_piece(mv.dst(), promoted_piece.0);
@@ -277,13 +273,13 @@ impl Game {
} }
pub fn make_null_move(&mut self) { 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_castling_ability(self.board.state.castling_ability)
.with_en_passant_square(self.board.state.en_passant_square) .with_en_passant_square(self.board.state.en_passant_square)
.with_halfmove_clock(self.board.state.halfmove_clock) .with_halfmove_clock(self.board.state.halfmove_clock)
.with_zobrist_hash(&self.hash); .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.hash.update_side_to_move_key();
self.board self.board
.state .state
@@ -292,25 +288,22 @@ impl Game {
pub fn unmake_null_move(&mut self) { pub fn unmake_null_move(&mut self) {
let board = &mut self.board; let board = &mut self.board;
let move_info = &mut self let unmake_info = self.unmake_stack.pop().expect("Stack empty before pop.");
.history
.pop_move_info()
.expect("History stack is empty");
let color_before_move = board.state.change_side(); let color_before_move = board.state.change_side();
board.state.revert_full_move(color_before_move); 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; 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; 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; board.state.halfmove_clock = new_halfmove_clock;
} }
} }
@@ -320,7 +313,79 @@ impl Game {
return false; 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<Move>,
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<usize>,
pub halfmove_clock: Option<u8>,
pub zobrist_hash: Option<ZobristHash>,
}
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<usize>) -> 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
} }
} }

View File

@@ -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<MoveInfo>);
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<MoveInfo> {
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<Move>,
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<usize>,
pub halfmove_clock: Option<u8>,
pub zobrist_hash: Option<ZobristHash>,
}
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<usize>) -> 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
}
}

View File

@@ -2,7 +2,6 @@ pub mod bitboard;
pub mod board; pub mod board;
pub mod fen; pub mod fen;
pub mod game; pub mod game;
pub mod history;
pub mod mailbox; pub mod mailbox;
pub mod square; pub mod square;
pub mod state; pub mod state;