Skip to content

Commit

Permalink
Parametrize search by PV/NonPV node type
Browse files Browse the repository at this point in the history
  • Loading branch information
bsamseth committed Aug 21, 2024
1 parent 1548fb5 commit 3d59728
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 23 deletions.
1 change: 0 additions & 1 deletion engine/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ mod search;
mod tt;

use anyhow::Context;
use chess::ChessMove;
use search::Searcher;
use uci::UciOptions;

Expand Down
7 changes: 5 additions & 2 deletions engine/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand All @@ -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:
//
Expand Down
9 changes: 5 additions & 4 deletions engine/src/search/cuts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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<const PV: PvNode>(
&mut self,
board: &Board,
depth: Depth,
Expand All @@ -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::<NON_PV_NODE>(
board,
depth - Depth::new(1),
-alpha - Value::new(1),
Expand All @@ -49,7 +49,7 @@ impl Searcher<'_> {
}
}

-Value::from(self.negamax(
-Value::from(self.negamax::<PV>(
board,
depth - Depth::new(1),
-beta,
Expand Down Expand Up @@ -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<draw
#[inline]
pub fn return_if_draw(&self, board: &Board, ply: Ply) -> Result<(), Value> {
if self.is_draw(board, ply) {
Expand Down
4 changes: 4 additions & 0 deletions engine/src/search/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
12 changes: 7 additions & 5 deletions engine/src/search/negamax.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -10,7 +11,7 @@ use crate::tt::Bound;

impl Searcher<'_> {
/// Negamax search with alpha-beta pruning.
pub fn negamax(
pub fn negamax<const PV: PvNode>(
&mut self,
board: &Board,
mut depth: Depth,
Expand All @@ -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::<PV>(board, alpha, beta, ply);
}

// Step 3: Expand the node.
Expand All @@ -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::<PV>(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::<PV>(board, tt_move, depth, alpha, beta, ply);

// Step 6: Move generation and ordering.
let moves = MoveVec::from(MoveGen::new_legal(board))
Expand All @@ -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::<PV>(&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.
Expand Down
11 changes: 8 additions & 3 deletions engine/src/search/quiescence.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use chess::{Board, MoveGen};

use super::PvNode;
use super::{cuts, speculate, Searcher};
use crate::board::BoardExt;
use crate::movelist::MoveVec;
use crate::newtypes::{Ply, Value};

impl Searcher<'_> {
/// Quiescence search with alpha-beta pruning.
pub fn quiescence_search(
pub fn quiescence_search<const PV: PvNode>(
&mut self,
board: &Board,
mut alpha: Value,
Expand Down Expand Up @@ -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::<PV>(
&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.
Expand Down
3 changes: 2 additions & 1 deletion engine/src/search/root.rs
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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::<PV_NODE>(&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.
Expand Down
17 changes: 10 additions & 7 deletions engine/src/search/speculate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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::<NON_PV_NODE>(
&board,
new_depth,
-*beta,
Expand Down Expand Up @@ -111,7 +112,7 @@ impl Searcher<'_> {
///
/// Source: <http://www.frayn.net/beowulf/theory.html/>
#[inline]
pub fn futility_pruning(
pub fn futility_pruning<const PV: PvNode>(
&mut self,
board: &Board,
alpha: Value,
Expand All @@ -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::<PV>(board, alpha, beta, ply),
));
}
_ => {}
}
Expand All @@ -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<const PV: PvNode>(
&mut self,
board: &Board,
tt_move: Option<ChessMove>,
Expand All @@ -154,9 +157,9 @@ impl Searcher<'_> {
beta: Value,
ply: Ply,
) -> Option<ChessMove> {
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::<PV>(board, depth, alpha, beta, ply);
if let Some((mv, _, _)) =
self.transposition_table
.get(self.stack_state(ply).zobrist, depth, ply)
Expand Down

0 comments on commit 3d59728

Please sign in to comment.