Skip to content

Commit

Permalink
add singular move extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
Heiaha committed Dec 1, 2024
1 parent 5b3def6 commit 25b6f35
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 22 deletions.
91 changes: 79 additions & 12 deletions src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub struct Search<'a> {
timer: Timer,
tt: &'a TT,
move_sorter: MoveSorter,
excluded_moves: [Option<Move>; 256],
}

impl<'a> Search<'a> {
Expand All @@ -24,6 +25,7 @@ impl<'a> Search<'a> {
tt,
sel_depth: 0,
move_sorter: MoveSorter::new(),
excluded_moves: [None; 256],
}
}

Expand Down Expand Up @@ -209,14 +211,15 @@ impl<'a> Search<'a> {
// Check if we're in a pv node
///////////////////////////////////////////////////////////////////
let is_pv = alpha != beta - 1;
let excluded_move = self.excluded_moves[ply];

///////////////////////////////////////////////////////////////////
// Probe the hash table and adjust the value.
// If appropriate, produce a cutoff.
///////////////////////////////////////////////////////////////////
let tt_entry = self.tt.probe(board);
if let Some(tt_entry) = tt_entry {
if tt_entry.depth() >= depth && !is_pv {
if tt_entry.depth() >= depth && !is_pv && excluded_move.is_none() {
match tt_entry.flag() {
Bound::Exact => return tt_entry.value(),
Bound::Lower => alpha = alpha.max(tt_entry.value()),
Expand All @@ -226,14 +229,14 @@ impl<'a> Search<'a> {
return tt_entry.value();
}
}
} else if Self::can_apply_iid(depth, in_check, is_pv) {
} else if Self::can_apply_iid(depth, in_check, is_pv, excluded_move) {
depth -= Self::IID_DEPTH_REDUCTION;
}

///////////////////////////////////////////////////////////////////
// Reverse Futility Pruning
///////////////////////////////////////////////////////////////////
if Self::can_apply_rfp(depth, in_check, is_pv, beta) {
if Self::can_apply_rfp(depth, in_check, is_pv, beta, excluded_move) {
let eval = tt_entry.map_or_else(|| board.eval(), |entry| entry.value());

if eval - Self::rfp_margin(depth) >= beta {
Expand All @@ -244,7 +247,7 @@ impl<'a> Search<'a> {
///////////////////////////////////////////////////////////////////
// Null move pruning.
///////////////////////////////////////////////////////////////////
if Self::can_apply_null(board, depth, beta, in_check, is_pv) {
if Self::can_apply_null(board, depth, beta, in_check, is_pv, excluded_move) {
let r = Self::null_reduction(depth);
board.push_null();
let value = -self.search(board, depth - r - 1, -beta, -beta + 1, ply);
Expand Down Expand Up @@ -274,6 +277,27 @@ impl<'a> Search<'a> {
);

while let Some(m) = moves.next_best(idx) {
if Some(m) == excluded_move {
idx += 1;
continue;
}

let extension = tt_entry
.filter(|&entry| Self::can_singular_extend(entry, m, depth, excluded_move))
.map(|entry| {
let target = entry.value() - (2 * depth as Value);
self.excluded_moves[ply] = Some(m);
let extension =
if self.search(board, (depth - 1) / 2, target - 1, target, ply) < target {
1
} else {
0
};
self.excluded_moves[ply] = None;
extension
})
.unwrap_or(0);

///////////////////////////////////////////////////////////////////
// Make move and deepen search via principal variation search.
///////////////////////////////////////////////////////////////////
Expand All @@ -285,7 +309,7 @@ impl<'a> Search<'a> {

let mut value;
if idx == 0 {
value = -self.search(board, depth - 1, -beta, -alpha, ply + 1);
value = -self.search(board, depth + extension - 1, -beta, -alpha, ply + 1);
} else {
///////////////////////////////////////////////////////////////////
// Late move reductions.
Expand All @@ -297,9 +321,21 @@ impl<'a> Search<'a> {
};

loop {
value = -self.search(board, depth - reduction - 1, -alpha - 1, -alpha, ply + 1);
value = -self.search(
board,
depth + extension - reduction - 1,
-alpha - 1,
-alpha,
ply + 1,
);
if value > alpha {
value = -self.search(board, depth - reduction - 1, -beta, -alpha, ply + 1);
value = -self.search(
board,
depth + extension - reduction - 1,
-beta,
-alpha,
ply + 1,
);
}

///////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -343,7 +379,7 @@ impl<'a> Search<'a> {
///////////////////////////////////////////////////////////////////
// Checkmate and stalemate check.
///////////////////////////////////////////////////////////////////
if moves.len() == 0 {
if moves.len() == 0 && excluded_move.is_none() {
if in_check {
alpha = -mate_value;
} else {
Expand Down Expand Up @@ -445,6 +481,7 @@ impl<'a> Search<'a> {
beta: Value,
in_check: bool,
is_pv: bool,
excluded_move: Option<Move>,
) -> bool {
!is_pv
&& !in_check
Expand All @@ -453,20 +490,50 @@ impl<'a> Search<'a> {
&& board.has_non_pawn_material()
&& board.eval() >= beta
&& !Self::is_checkmate(beta)
&& excluded_move.is_none()
}

fn can_apply_iid(depth: Depth, in_check: bool, is_pv: bool) -> bool {
depth >= Self::IID_MIN_DEPTH && !in_check && !is_pv
fn can_apply_iid(
depth: Depth,
in_check: bool,
is_pv: bool,
excluded_move: Option<Move>,
) -> bool {
depth >= Self::IID_MIN_DEPTH && !in_check && !is_pv && excluded_move.is_none()
}

fn can_apply_rfp(depth: Depth, in_check: bool, is_pv: bool, beta: Value) -> bool {
depth <= Self::RFP_MAX_DEPTH && !in_check && !is_pv && !Self::is_checkmate(beta)
fn can_apply_rfp(
depth: Depth,
in_check: bool,
is_pv: bool,
beta: Value,
excluded_move: Option<Move>,
) -> bool {
depth <= Self::RFP_MAX_DEPTH
&& !in_check
&& !is_pv
&& !Self::is_checkmate(beta)
&& excluded_move.is_none()
}

fn can_apply_lmr(m: Move, depth: Depth, move_index: usize) -> bool {
depth >= Self::LMR_MIN_DEPTH && move_index >= Self::LMR_MOVE_WO_REDUCTION && m.is_quiet()
}

fn can_singular_extend(
entry: TTEntry,
m: Move,
depth: Depth,
excluded_move: Option<Move>,
) -> bool {
entry.best_move() == Some(m)
&& depth >= 4
&& !Self::is_checkmate(entry.value())
&& excluded_move.is_none()
&& entry.depth() + 2 >= depth
&& matches!(entry.flag(), Bound::Lower | Bound::Exact)
}

fn null_reduction(depth: Depth) -> Depth {
// Idea of dividing in null move depth taken from Cosette
Self::NULL_MIN_DEPTH_REDUCTION + (depth - Self::NULL_MIN_DEPTH) / Self::NULL_DEPTH_DIVIDER
Expand Down
26 changes: 16 additions & 10 deletions src/tt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,25 +157,31 @@ impl TT {
///////////////////////////////////////////////////////////////////

#[derive(Default)]
struct AtomicEntry(AtomicU64, AtomicU64);
struct AtomicEntry {
checksum: AtomicU64,
data: AtomicU64,
}

impl AtomicEntry {
fn read(&self, lookup_hash: Hash) -> Option<TTEntry> {
let entry_hash = self.0.load(Ordering::Relaxed);
let data = self.1.load(Ordering::Relaxed);
if entry_hash ^ data == lookup_hash {
return Some(TTEntry::from(data));
fn read(&self, hash: Hash) -> Option<TTEntry> {
let (checksum, data) = (
self.checksum.load(Ordering::Relaxed),
self.data.load(Ordering::Relaxed),
);
if checksum ^ data == hash {
Some(TTEntry::from(data))
} else {
None
}
None
}

fn write(&self, hash: Hash, entry: TTEntry) {
let data = Hash::from(entry);
self.0.store(hash ^ data, Ordering::Relaxed);
self.1.store(data, Ordering::Relaxed);
self.checksum.store(hash ^ data, Ordering::Relaxed);
self.data.store(data, Ordering::Relaxed);
}

fn is_used(&self) -> bool {
self.0.load(Ordering::Relaxed) != Hash::default()
self.checksum.load(Ordering::Relaxed) != Hash::default()
}
}

0 comments on commit 25b6f35

Please sign in to comment.