Add TT cutoffs

This commit is contained in:
stefiosif
2025-01-26 23:58:44 +02:00
parent 53beda7fe3
commit a01310c7b7
5 changed files with 71 additions and 32 deletions

View File

@@ -7,10 +7,7 @@ use crate::{
movegen::r#move::Move, movegen::r#move::Move,
}; };
use super::{ use super::{negamax, time::TimeInfo};
negamax,
time::TimeInfo,
};
pub fn iterative_deepening( pub fn iterative_deepening(
game: &mut Game, game: &mut Game,

View File

@@ -11,4 +11,4 @@ pub const TIME: u128 = 1000;
pub const INC: u128 = 1000; pub const INC: u128 = 1000;
pub const HARD_LIMIT_DIVISION: u128 = 10; pub const HARD_LIMIT_DIVISION: u128 = 10;
pub const SOFT_LIMIT_DIVISION: u128 = HARD_LIMIT_DIVISION * 2; pub const SOFT_LIMIT_DIVISION: u128 = HARD_LIMIT_DIVISION * 2;
pub const MAX_TT_SIZE: u64 = 1500000; pub const MAX_TT_SIZE: u64 = 750000;

View File

@@ -9,7 +9,7 @@ use super::{
move_ordering::score_move, move_ordering::score_move,
quiescence::quiescence, quiescence::quiescence,
time::TimeInfo, time::TimeInfo,
transposition_table::TTEntry, transposition_table::{NodeType, TTEntry},
}; };
pub fn negamax( pub fn negamax(
@@ -30,8 +30,7 @@ pub fn negamax(
} }
if depth == 0 { if depth == 0 {
let q_score = let q_score = quiescence(game, alpha, beta, time, nodes).map_err(|e| anyhow!("{e}"))?;
quiescence(game, alpha, beta, time, nodes).map_err(|e| anyhow!("{e}"))?;
return Ok(q_score); return Ok(q_score);
} }
@@ -40,9 +39,22 @@ pub fn negamax(
let mut best_score = MIN_SCORE; let mut best_score = MIN_SCORE;
let mut best_move = None; let mut best_move = None;
let mut moves = game.board.pseudo_moves_all(); let mut moves = game.board.pseudo_moves_all();
let tt_move = game.tt.lookup(game.hash).and_then(|entry| entry.mv); let tt_entry = game.tt.lookup(game.hash);
moves.sort_unstable_by_key(|mv| score_move(&game.mailbox, *mv, tt_move)); 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| {
score_move(&game.mailbox, *mv, tt_entry.and_then(|entry| entry.mv))
});
for mv in moves { for mv in moves {
game.make_move(&mv); game.make_move(&mv);
@@ -57,15 +69,7 @@ pub fn negamax(
let score = if legal_moves == 1 { let score = if legal_moves == 1 {
-negamax(game, -beta, -alpha, depth - 1, plies + 1, time, nodes)? -negamax(game, -beta, -alpha, depth - 1, plies + 1, time, nodes)?
} else { } else {
let mut score = -negamax( let mut score = -negamax(game, -alpha - 1, -alpha, depth - 1, plies + 1, time, nodes)?;
game,
-alpha - 1,
-alpha,
depth - 1,
plies + 1,
time,
nodes,
)?;
if score > alpha && score < beta { if score > alpha && score < beta {
score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, time, nodes)?; score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, time, nodes)?;
} }
@@ -95,7 +99,15 @@ pub fn negamax(
return Ok(0); return Ok(0);
} }
game.tt.insert(TTEntry::new(game.hash, best_move)); let node_type = match best_score {
s if s >= beta => NodeType::LowerBound,
s if s <= alpha => NodeType::UpperBound,
_ => NodeType::Exact,
};
game.tt.insert(TTEntry::new(
game.hash, best_move, depth, best_score, node_type,
));
Ok(best_score) Ok(best_score)
} }

View File

@@ -2,10 +2,7 @@ use anyhow::{bail, Result};
use crate::{board::game::Game, evaluation::pesto::pesto}; use crate::{board::game::Game, evaluation::pesto::pesto};
use super::{ use super::{move_ordering::score_move, time::TimeInfo, transposition_table::NodeType};
move_ordering::score_move,
time::TimeInfo,
};
pub fn quiescence( pub fn quiescence(
game: &mut Game, game: &mut Game,
@@ -31,9 +28,22 @@ pub fn quiescence(
let color = game.current_player(); let color = game.current_player();
let mut best_score = stand_pat; let mut best_score = stand_pat;
let mut moves = game.board.pseudo_moves_all_captures(); let mut moves = game.board.pseudo_moves_all_captures();
let tt_move = game.tt.lookup(game.hash).and_then(|entry| entry.mv); let tt_entry = game.tt.lookup(game.hash);
moves.sort_unstable_by_key(|mv| score_move(&game.mailbox, *mv, tt_move)); if let Some(tt_entry) = tt_entry {
if tt_entry.hash == game.hash {
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| {
score_move(&game.mailbox, *mv, tt_entry.and_then(|entry| entry.mv))
});
for mv in moves { for mv in moves {
game.make_move(&mv); game.make_move(&mv);

View File

@@ -15,12 +15,10 @@ impl TranspositionTable {
} }
pub fn lookup(&self, zobrist_hash: ZobristHash) -> Option<&TTEntry> { pub fn lookup(&self, zobrist_hash: ZobristHash) -> Option<&TTEntry> {
let entry = self self
.positions .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.as_ref())
entry
} }
pub fn insert(&mut self, tt_entry: TTEntry) { pub fn insert(&mut self, tt_entry: TTEntry) {
@@ -33,14 +31,36 @@ impl TranspositionTable {
pub struct TTEntry { pub struct TTEntry {
pub hash: ZobristHash, pub hash: ZobristHash,
pub mv: Option<Move>, pub mv: Option<Move>,
pub depth: u8,
pub score: i32,
pub node_type: NodeType,
} }
impl TTEntry { impl TTEntry {
pub const fn new(hash: ZobristHash, mv: Option<Move>) -> Self { pub const fn new(
Self { hash, mv } hash: ZobristHash,
mv: Option<Move>,
depth: u8,
score: i32,
node_type: NodeType,
) -> Self {
Self {
hash,
mv,
depth,
score,
node_type,
}
} }
} }
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum NodeType {
Exact,
LowerBound,
UpperBound,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{