Do TT cutoffs before LMP, use TT move as starting best_move and readability changes

This commit is contained in:
stefiosif
2025-02-07 19:41:54 +02:00
parent b98100aa71
commit bdd065efff
4 changed files with 53 additions and 55 deletions

View File

@@ -207,7 +207,7 @@ fn uci_option(option_iter: &mut SplitWhitespace, game: &mut Game) -> anyhow::Res
let size = value let size = value
.parse::<usize>() .parse::<usize>()
.map_err(|_| anyhow!("Invalid value for Hash"))?; .map_err(|_| anyhow!("Invalid value for Hash"))?;
game.tt = TranspositionTable::new_with_mb_size(size); game.tt = TranspositionTable::with_capacity(size);
} }
} }

View File

@@ -1,4 +1,4 @@
use anyhow::{anyhow, bail, Result}; use anyhow::{bail, Result};
use crate::{ use crate::{
board::game::Game, board::game::Game,
@@ -38,8 +38,19 @@ pub fn negamax(
} }
if depth == 0 { if depth == 0 {
let q_score = quiescence(game, alpha, beta, time, nodes).map_err(|e| anyhow!("{e}"))?; return quiescence(game, alpha, beta, time, nodes);
return Ok(q_score); }
let entry = game.tt.lookup(game.hash);
if let Some(entry) = entry {
if plies > 0
&& entry.hash == game.hash
&& entry.depth >= depth
&& entry.node_type.cutoff_eligible(entry.score, alpha, beta)
{
return Ok(entry.score);
}
} }
if plies != 0 && !in_check && depth >= 3 && do_nmp && game.board.non_pawn_materials() > 0 { if plies != 0 && !in_check && depth >= 3 && do_nmp && game.board.non_pawn_materials() > 0 {
@@ -61,32 +72,20 @@ pub fn negamax(
} }
} }
let mut legal_moves = 0;
let mut best_score = MIN_SCORE;
let mut best_move = None;
let mut moves = game.board.pseudo_moves_all(); let mut moves = game.board.pseudo_moves_all();
let tt_entry = game.tt.lookup(game.hash);
if let Some(tt_entry) = tt_entry {
if plies > 0 && tt_entry.hash == game.hash && tt_entry.depth >= depth {
match tt_entry.node_type {
NodeType::Exact => return Ok(tt_entry.score),
NodeType::LowerBound if tt_entry.score >= beta => return Ok(tt_entry.score),
NodeType::UpperBound if tt_entry.score <= alpha => return Ok(tt_entry.score),
_ => (),
}
}
}
moves.sort_unstable_by_key(|mv| { moves.sort_unstable_by_key(|mv| {
score_move( score_move(
&game.mailbox, &game.mailbox,
*mv, *mv,
game.killer[plies as usize], game.killer[plies as usize],
tt_entry.and_then(|entry| entry.mv), entry.and_then(|entry| entry.mv),
) )
}); });
let mut legal_moves = 0;
let mut best_move = entry.and_then(|entry| entry.mv);
let mut best_score = MIN_SCORE;
for mv in moves { for mv in moves {
game.make_move(&mv); game.make_move(&mv);

View File

@@ -2,7 +2,7 @@ use anyhow::{bail, Result};
use crate::{board::game::Game, evaluation::pesto::pesto}; use crate::{board::game::Game, evaluation::pesto::pesto};
use super::{move_ordering::score_move, time::TimeInfo, transposition_table::NodeType}; use super::{move_ordering::score_move, time::TimeInfo};
pub fn quiescence( pub fn quiescence(
game: &mut Game, game: &mut Game,
@@ -25,29 +25,20 @@ pub fn quiescence(
alpha = stand_pat; alpha = stand_pat;
} }
let color = game.current_player(); let entry = game.tt.lookup(game.hash);
let mut best_score = stand_pat;
let mut moves = game.board.pseudo_moves_all_captures();
let tt_entry = game.tt.lookup(game.hash);
if let Some(tt_entry) = tt_entry { if let Some(entry) = entry {
if tt_entry.hash == game.hash { if entry.hash == game.hash && entry.node_type.cutoff_eligible(entry.score, alpha, beta) {
match tt_entry.node_type { return Ok(entry.score);
NodeType::Exact => return Ok(tt_entry.score),
NodeType::LowerBound if tt_entry.score >= beta => return Ok(tt_entry.score),
NodeType::UpperBound if tt_entry.score <= alpha => return Ok(tt_entry.score),
_ => (),
}
} }
} }
let color = game.current_player();
let mut best_score = stand_pat;
let mut moves = game.board.pseudo_moves_all_captures();
moves.sort_unstable_by_key(|mv| { moves.sort_unstable_by_key(|mv| {
score_move( score_move(&game.mailbox, *mv, None, entry.and_then(|entry| entry.mv))
&game.mailbox,
*mv,
None,
tt_entry.and_then(|entry| entry.mv),
)
}); });
for mv in moves { for mv in moves {
@@ -59,7 +50,7 @@ pub fn quiescence(
} }
*nodes += 1; *nodes += 1;
let score = -quiescence(game, -beta, -alpha, time, &mut 0)?; let score = -quiescence(game, -beta, -alpha, time, nodes)?;
game.unmake_move(); game.unmake_move();
if score > best_score { if score > best_score {

View File

@@ -10,11 +10,11 @@ pub struct TranspositionTable {
impl TranspositionTable { impl TranspositionTable {
pub fn new() -> Self { pub fn new() -> Self {
Self::new_with_mb_size(TT_SIZE_IN_MB) Self::with_capacity(TT_SIZE_IN_MB)
} }
pub fn new_with_mb_size(size_in_mega_bytes: usize) -> Self { pub fn with_capacity(size_in_mb: usize) -> Self {
let size_in_bytes = size_in_mega_bytes * 1024 * 1024; let size_in_bytes = size_in_mb * 1024 * 1024;
let num_of_entries = size_in_bytes / std::mem::size_of::<TTEntry>(); let num_of_entries = size_in_bytes / std::mem::size_of::<TTEntry>();
Self { Self {
@@ -23,19 +23,19 @@ impl TranspositionTable {
} }
} }
pub fn lookup(&self, zobrist_hash: ZobristHash) -> Option<&TTEntry> { pub fn lookup(&self, zobrist_hash: ZobristHash) -> Option<TTEntry> {
self.positions self.positions
.get((zobrist_hash.0 % self.size) as usize) .get((zobrist_hash.0 % self.size) as usize)
.and_then(|entry| entry.as_ref()) .and_then(|entry| *entry)
} }
pub fn insert(&mut self, tt_entry: TTEntry) { pub fn insert(&mut self, entry: TTEntry) {
let idx = (tt_entry.hash.0 % self.size) as usize; let idx = (entry.hash.0 % self.size) as usize;
self.positions[idx] = Some(tt_entry); self.positions[idx] = Some(entry);
} }
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TTEntry { pub struct TTEntry {
pub hash: ZobristHash, pub hash: ZobristHash,
pub mv: Option<Move>, pub mv: Option<Move>,
@@ -62,13 +62,21 @@ impl TTEntry {
} }
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NodeType { pub enum NodeType {
Exact, Exact,
LowerBound, LowerBound,
UpperBound, UpperBound,
} }
impl NodeType {
pub fn cutoff_eligible(self, score: i16, alpha: i16, beta: i16) -> bool {
self == Self::Exact
|| self == Self::LowerBound && score >= beta
|| self == Self::UpperBound && score <= alpha
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
@@ -96,14 +104,14 @@ mod tests {
dbg!(time_info.instant.elapsed()); dbg!(time_info.instant.elapsed());
let will_be_hash = from_fen(FEN_POSSIBLE).unwrap().hash; let will_be_hash = from_fen(FEN_POSSIBLE).unwrap().hash;
let tt_entry = game.tt.lookup(will_be_hash); let entry = game.tt.lookup(will_be_hash);
assert!(tt_entry.is_some()); assert!(entry.is_some());
let wont_be_hash = from_fen(FEN_IMPOSSIBLE).unwrap().hash; let wont_be_hash = from_fen(FEN_IMPOSSIBLE).unwrap().hash;
let tt_entry = game.tt.lookup(wont_be_hash); let entry = game.tt.lookup(wont_be_hash);
assert!(tt_entry.is_none()); assert!(entry.is_none());
Ok(()) Ok(())
} }