Do TT cutoffs before LMP, use TT move as starting best_move and readability changes
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user