Add TT cutoffs
This commit is contained in:
@@ -7,10 +7,7 @@ use crate::{
|
||||
movegen::r#move::Move,
|
||||
};
|
||||
|
||||
use super::{
|
||||
negamax,
|
||||
time::TimeInfo,
|
||||
};
|
||||
use super::{negamax, time::TimeInfo};
|
||||
|
||||
pub fn iterative_deepening(
|
||||
game: &mut Game,
|
||||
|
||||
@@ -11,4 +11,4 @@ pub const TIME: u128 = 1000;
|
||||
pub const INC: u128 = 1000;
|
||||
pub const HARD_LIMIT_DIVISION: u128 = 10;
|
||||
pub const SOFT_LIMIT_DIVISION: u128 = HARD_LIMIT_DIVISION * 2;
|
||||
pub const MAX_TT_SIZE: u64 = 1500000;
|
||||
pub const MAX_TT_SIZE: u64 = 750000;
|
||||
|
||||
@@ -9,7 +9,7 @@ use super::{
|
||||
move_ordering::score_move,
|
||||
quiescence::quiescence,
|
||||
time::TimeInfo,
|
||||
transposition_table::TTEntry,
|
||||
transposition_table::{NodeType, TTEntry},
|
||||
};
|
||||
|
||||
pub fn negamax(
|
||||
@@ -30,8 +30,7 @@ pub fn negamax(
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
let q_score =
|
||||
quiescence(game, alpha, beta, time, nodes).map_err(|e| anyhow!("{e}"))?;
|
||||
let q_score = quiescence(game, alpha, beta, time, nodes).map_err(|e| anyhow!("{e}"))?;
|
||||
return Ok(q_score);
|
||||
}
|
||||
|
||||
@@ -40,9 +39,22 @@ pub fn negamax(
|
||||
let mut best_score = MIN_SCORE;
|
||||
let mut best_move = None;
|
||||
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 {
|
||||
game.make_move(&mv);
|
||||
@@ -57,15 +69,7 @@ pub fn negamax(
|
||||
let score = if legal_moves == 1 {
|
||||
-negamax(game, -beta, -alpha, depth - 1, plies + 1, time, nodes)?
|
||||
} else {
|
||||
let mut score = -negamax(
|
||||
game,
|
||||
-alpha - 1,
|
||||
-alpha,
|
||||
depth - 1,
|
||||
plies + 1,
|
||||
time,
|
||||
nodes,
|
||||
)?;
|
||||
let mut score = -negamax(game, -alpha - 1, -alpha, depth - 1, plies + 1, time, nodes)?;
|
||||
if score > alpha && score < beta {
|
||||
score = -negamax(game, -beta, -alpha, depth - 1, plies + 1, time, nodes)?;
|
||||
}
|
||||
@@ -95,7 +99,15 @@ pub fn negamax(
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,7 @@ use anyhow::{bail, Result};
|
||||
|
||||
use crate::{board::game::Game, evaluation::pesto::pesto};
|
||||
|
||||
use super::{
|
||||
move_ordering::score_move,
|
||||
time::TimeInfo,
|
||||
};
|
||||
use super::{move_ordering::score_move, time::TimeInfo, transposition_table::NodeType};
|
||||
|
||||
pub fn quiescence(
|
||||
game: &mut Game,
|
||||
@@ -31,9 +28,22 @@ pub fn quiescence(
|
||||
let color = game.current_player();
|
||||
let mut best_score = stand_pat;
|
||||
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 {
|
||||
game.make_move(&mv);
|
||||
|
||||
@@ -15,12 +15,10 @@ impl TranspositionTable {
|
||||
}
|
||||
|
||||
pub fn lookup(&self, zobrist_hash: ZobristHash) -> Option<&TTEntry> {
|
||||
let entry = self
|
||||
self
|
||||
.positions
|
||||
.get((zobrist_hash.0 % self.size) as usize)
|
||||
.and_then(|entry| entry.as_ref());
|
||||
|
||||
entry
|
||||
.and_then(|entry| entry.as_ref())
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, tt_entry: TTEntry) {
|
||||
@@ -33,13 +31,35 @@ impl TranspositionTable {
|
||||
pub struct TTEntry {
|
||||
pub hash: ZobristHash,
|
||||
pub mv: Option<Move>,
|
||||
pub depth: u8,
|
||||
pub score: i32,
|
||||
pub node_type: NodeType,
|
||||
}
|
||||
|
||||
impl TTEntry {
|
||||
pub const fn new(hash: ZobristHash, mv: Option<Move>) -> Self {
|
||||
Self { hash, mv }
|
||||
pub const fn new(
|
||||
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)]
|
||||
mod tests {
|
||||
|
||||
Reference in New Issue
Block a user