diff --git a/engine/src/lib.rs b/engine/src/lib.rs index 9883a5d..5b1ad7a 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -11,7 +11,6 @@ mod search; mod tt; use anyhow::Context; -use chess::ChessMove; use search::Searcher; use uci::UciOptions; diff --git a/engine/src/opts.rs b/engine/src/opts.rs index f39e2e1..18f523f 100644 --- a/engine/src/opts.rs +++ b/engine/src/opts.rs @@ -31,7 +31,7 @@ pub struct Opts { // Search speculation options: // /// Razor margin in centipawns. - #[uci(default = "650", kind = "spin", min = "0")] + #[uci(default = "600", kind = "spin", min = "0")] pub razor_margin: Value, /// Extended futility margin in centipawns. #[uci(default = "500", kind = "spin", min = "0")] @@ -43,8 +43,11 @@ pub struct Opts { #[uci(default = "250", kind = "spin", min = "0")] pub delta_margin: Value, /// Internal iterative deepening depth reduction. - #[uci(default = "7", kind = "spin", min = "0")] + #[uci(default = "2", kind = "spin", min = "0", max = "4")] pub iid_depth_reduction: Depth, + /// Internal iterative deepening lower depth limit. + #[uci(default = "5", kind = "spin", min = "3")] + pub iid_depth_lower_bound: Depth, // Move ordering options: // diff --git a/engine/src/search/cuts.rs b/engine/src/search/cuts.rs index 1b29efe..a8c17db 100644 --- a/engine/src/search/cuts.rs +++ b/engine/src/search/cuts.rs @@ -9,7 +9,7 @@ use chess::{Board, ChessMove, MoveGen}; use fathom::Wdl; -use super::Searcher; +use super::{PvNode, Searcher, NON_PV_NODE}; use crate::board::BoardExt; use crate::evaluate::Evaluate; use crate::newtypes::{Depth, Ply, Value}; @@ -26,7 +26,7 @@ impl Searcher<'_> { /// If we find a later move that actually improves alpha, we must search this properly to find /// its value. The idea is that this drawback is smaller than the improvements gained. #[inline] - pub fn pv_search( + pub fn pv_search( &mut self, board: &Board, depth: Depth, @@ -36,7 +36,7 @@ impl Searcher<'_> { mv_nr: usize, ) -> Value { if depth > Depth::new(1) && mv_nr > 0 { - let value = -Value::from(self.negamax( + let value = -Value::from(self.negamax::( board, depth - Depth::new(1), -alpha - Value::new(1), @@ -49,7 +49,7 @@ impl Searcher<'_> { } } - -Value::from(self.negamax( + -Value::from(self.negamax::( board, depth - Depth::new(1), -beta, @@ -77,6 +77,7 @@ impl Searcher<'_> { } /// Early return with [`Value::DRAW`] if the position is a draw. + /// TODO: check for upcomming draw by repetition and increase alpha if alpha Result<(), Value> { if self.is_draw(board, ply) { diff --git a/engine/src/search/mod.rs b/engine/src/search/mod.rs index 4de5712..9c69563 100644 --- a/engine/src/search/mod.rs +++ b/engine/src/search/mod.rs @@ -35,6 +35,10 @@ pub struct Searcher<'a> { history_stats: [[usize; 64]; 64], } +type PvNode = bool; +const PV_NODE: bool = true; +const NON_PV_NODE: bool = false; + impl<'a> Searcher<'a> { /// Create a new [`Searcher`]. pub fn best_move( diff --git a/engine/src/search/negamax.rs b/engine/src/search/negamax.rs index 62ecf1c..4ef0fa5 100644 --- a/engine/src/search/negamax.rs +++ b/engine/src/search/negamax.rs @@ -1,6 +1,7 @@ use chess::{Board, MoveGen}; use super::cuts; +use super::PvNode; use super::Searcher; use crate::board::BoardExt; use crate::evaluate::Evaluate; @@ -10,7 +11,7 @@ use crate::tt::Bound; impl Searcher<'_> { /// Negamax search with alpha-beta pruning. - pub fn negamax( + pub fn negamax( &mut self, board: &Board, mut depth: Depth, @@ -29,7 +30,7 @@ impl Searcher<'_> { // Step 2: Quiescence search if at max depth. if depth == Depth::new(0) { - return self.quiescence_search(board, alpha, beta, ply); + return self.quiescence_search::(board, alpha, beta, ply); } // Step 3: Expand the node. @@ -40,12 +41,13 @@ impl Searcher<'_> { depth += Depth::new(1); // Extend search if in check. } else { self.stack_state_mut(ply).eval = board.evaluate(); - self.futility_pruning(board, alpha, beta, &mut depth, ply)?; + self.futility_pruning::(board, alpha, beta, &mut depth, ply)?; self.null_move_pruning(board, &mut beta, depth, ply)?; } // Step 5: Internal iterative deepening. - let tt_move = self.internal_iterative_deepening(board, tt_move, depth, alpha, beta, ply); + let tt_move = + self.internal_iterative_deepening::(board, tt_move, depth, alpha, beta, ply); // Step 6: Move generation and ordering. let moves = MoveVec::from(MoveGen::new_legal(board)) @@ -68,7 +70,7 @@ impl Searcher<'_> { for (mv_nr, mv) in moves.iter().map(|entry| entry.mv).enumerate() { self.make_move(board, mv, &mut new_board, ply); - let value = self.pv_search(&new_board, depth, alpha, beta, ply, mv_nr); + let value = self.pv_search::(&new_board, depth, alpha, beta, ply, mv_nr); if self.should_stop() { // If we're stopping, we don't trust the value, because it was likely cut off. diff --git a/engine/src/search/quiescence.rs b/engine/src/search/quiescence.rs index ac1b670..0e1bbf4 100644 --- a/engine/src/search/quiescence.rs +++ b/engine/src/search/quiescence.rs @@ -1,5 +1,6 @@ use chess::{Board, MoveGen}; +use super::PvNode; use super::{cuts, speculate, Searcher}; use crate::board::BoardExt; use crate::movelist::MoveVec; @@ -7,7 +8,7 @@ use crate::newtypes::{Ply, Value}; impl Searcher<'_> { /// Quiescence search with alpha-beta pruning. - pub fn quiescence_search( + pub fn quiescence_search( &mut self, board: &Board, mut alpha: Value, @@ -43,8 +44,12 @@ impl Searcher<'_> { } self.make_move(board, mv, &mut new_board, ply); - let value = - -Value::from(self.quiescence_search(&new_board, -beta, -alpha, ply + Ply::new(1))); + let value = -Value::from(self.quiescence_search::( + &new_board, + -beta, + -alpha, + ply + Ply::new(1), + )); if self.should_stop() { // If we're stopping, we don't trust the value, because it was likely cut off. diff --git a/engine/src/search/root.rs b/engine/src/search/root.rs index 47f8617..70720d0 100644 --- a/engine/src/search/root.rs +++ b/engine/src/search/root.rs @@ -1,6 +1,7 @@ use super::Searcher; use crate::movelist::MoveEntry; use crate::newtypes::{Depth, Ply, Value}; +use crate::search::PV_NODE; impl Searcher<'_> { /// Search the root position to `depth` moves deep. @@ -21,7 +22,7 @@ impl Searcher<'_> { self.make_move(&root_board, mv.mv, &mut board, Ply::new(0)); - let value = self.pv_search(&board, depth, alpha, beta, Ply::new(0), mv_nr); + let value = self.pv_search::(&board, depth, alpha, beta, Ply::new(0), mv_nr); if self.should_stop() { // If we're stopping, we don't trust the value, because it was likely cut off. diff --git a/engine/src/search/speculate.rs b/engine/src/search/speculate.rs index 654edf5..bacdb86 100644 --- a/engine/src/search/speculate.rs +++ b/engine/src/search/speculate.rs @@ -10,7 +10,8 @@ use chess::{Board, ChessMove, Piece}; -use super::Searcher; +use super::PvNode; +use super::{Searcher, NON_PV_NODE}; use crate::chessmove::ChessMoveExt; use crate::opts::OPTS; use crate::{ @@ -71,7 +72,7 @@ impl Searcher<'_> { // This is so that 1) it's the other side's turn in sibling nodes, and 2) this allows // recursive null moves. let new_depth = Depth::new(depth.as_inner().saturating_sub(3)); - let mut value = -Value::from(self.negamax( + let mut value = -Value::from(self.negamax::( &board, new_depth, -*beta, @@ -111,7 +112,7 @@ impl Searcher<'_> { /// /// Source: #[inline] - pub fn futility_pruning( + pub fn futility_pruning( &mut self, board: &Board, alpha: Value, @@ -133,7 +134,9 @@ impl Searcher<'_> { *depth = Depth::new(1); } 1 if eval + OPTS.futility_margin <= alpha => { - return Err(Value::from(self.quiescence_search(board, alpha, beta, ply))); + return Err(Value::from( + self.quiescence_search::(board, alpha, beta, ply), + )); } _ => {} } @@ -145,7 +148,7 @@ impl Searcher<'_> { /// When we have no good guess for the best move, do a reduced search first to find a likely /// candidate. Only do this if a search would lead to a new entry in the TT. #[inline] - pub fn internal_iterative_deepening( + pub fn internal_iterative_deepening( &mut self, board: &Board, tt_move: Option, @@ -154,9 +157,9 @@ impl Searcher<'_> { beta: Value, ply: Ply, ) -> Option { - if tt_move.is_none() && depth > OPTS.iid_depth_reduction { + if tt_move.is_none() && depth >= OPTS.iid_depth_lower_bound { let depth = depth - OPTS.iid_depth_reduction; - let _ = self.negamax(board, depth, alpha, beta, ply); + let _ = self.negamax::(board, depth, alpha, beta, ply); if let Some((mv, _, _)) = self.transposition_table .get(self.stack_state(ply).zobrist, depth, ply)