From 6b231ca521b4e482091b7fd0a7b35327aa86ea79 Mon Sep 17 00:00:00 2001 From: heofthetea Date: Sat, 25 Apr 2026 19:12:27 +0200 Subject: [PATCH 1/6] lmr? --- Cargo.toml | 17 +++------- src/alpha_beta.rs | 81 +++++++++++++++++++++++++++++++++++++++-------- src/settings.rs | 8 ++++- 3 files changed, 78 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 22cff9a..f193d1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,20 +23,10 @@ all = [ "rfp", "pvs", "killers", - "histories" -] -all-no-killers = [ - "ab", - "qs", - "tt-ab", - "tt-qs", - "mvv-lva", - "order-tt-mv-first", - "tt-cuttoffs", - "nmp", - "rfp", - "pvs", + "histories", + "lmr" ] + ab = [] qs = [] tt-ab = [] @@ -49,6 +39,7 @@ rfp = [] pvs = [] killers = [] histories = [] +lmr = [] [dependencies] rayon = "1.11.0" diff --git a/src/alpha_beta.rs b/src/alpha_beta.rs index 8ced271..a3b0d94 100644 --- a/src/alpha_beta.rs +++ b/src/alpha_beta.rs @@ -10,7 +10,7 @@ use crate::{ transposition_table::{Bound, TT}, }; -use std::{cmp::min, sync::atomic::Ordering}; +use std::{cmp::min, f64, sync::atomic::Ordering}; /// There's different approaches to this one as well. CPW suggests 150 * depth, smol.cs does 75 * depth. /// Generally: The smaller `rfp_margin`, the more aggressively we prune. @@ -69,12 +69,10 @@ pub fn alpha_beta( sd.total_tt_hits.fetch_add(1, Ordering::Relaxed); let bound = tt_hit.bound(); - // let depth_cond = tt_hit.depth() >= depth as i32 - 3; tt_move = tt_hit.best_move(); tt_score = tt_hit.score(); - // TODO: When using pvs add !pv_node as additionell condition let depth_req = depth as i32 + i32::from(tt_score >= beta); if settings::TT_CUTTOFFS @@ -130,6 +128,7 @@ pub fn alpha_beta( } } } + // set the best evaluation very low to begin with let mut best_eval = i32::MIN + 1; let mut best_move: Option = None; @@ -141,10 +140,10 @@ pub fn alpha_beta( let mut quiets_tried: ArrayVec = ArrayVec::new(); let mut movepicker = MovePicker::new(tt_move, killer_mv, false); - let mut i = 0; + let mut moves_visited = 0; while let Some(mv) = movepicker.next(sd.board) { - i += 1; + moves_visited += 1; // cancels search if time is over if sd.stop.load(Ordering::Relaxed) { sd.timeout_occurred.store(true, Ordering::Relaxed); @@ -152,20 +151,39 @@ pub fn alpha_beta( } sd.board.make_move(mv); let mut eval; - if i == 1 || !settings::PVS { + if moves_visited == 1 || !settings::PVS { // Principal Variation Search // We assume that the first move from the move ordering is the PV move; // Since the TT move, if existant, is in first place anyway this automatically includes information from shallower search depths eval = -alpha_beta::(depth - 1, -beta, -alpha, sd, ply + 1, true); } else { - // Search non-PV moves with null window - eval = -alpha_beta::(depth - 1, -alpha - 1, -alpha, sd, ply + 1, true); - // Re-search if non-PV move raised Alpha - // ONLY do re-searching if the current Node is on PV - we don't care for re-searching OffPV nodes - // (if they actually improve over the PV that variation will be searched again at the last PV node anyway) - if eval > alpha && PV_NODE { - eval = -alpha_beta::(depth - 1, -beta, -alpha, sd, ply + 1, true); + #[allow(clippy::useless_let_if_seq)] + let mut reduction = 1; + if settings::LMR && moves_visited >= settings::MOVES_BEFORE_LMR { + reduction = LMP_REDUCTION[depth][moves_visited.clamp(0, 63)]; + } + eval = -alpha_beta::( + depth - reduction as usize, + -alpha - 1, + -alpha, + sd, + ply + 1, + true, + ); + + if eval > alpha { + // If our shallow-depth search raised alpha, we perform a search at full depth but still with a null window + // in hopes that we can avoid a full search + if reduction > 1 { + // Search non-PV moves with null window + eval = -alpha_beta::(depth - 1, -alpha - 1, -alpha, sd, ply + 1, true); + } + // ONLY do full-window full-depth re-searching if the current Node is on PV - we don't care for re-searching OffPV nodes + // (if they actually improve over the PV that variation will be searched again at the last PV node anyway) + if eval > alpha && PV_NODE { + eval = -alpha_beta::(depth - 1, -beta, -alpha, sd, ply + 1, true); + } } } @@ -194,7 +212,7 @@ pub fn alpha_beta( // When i still 0 than no move found // returns the mate score (very low) when in check but adds the ply to give a later check a better eval because the depth is lowers the further you go - if i == 0 { + if moves_visited == 0 { if sd.board.is_in_check() { #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] return -MATE_SCORE + (ply as i32); @@ -248,3 +266,38 @@ pub fn alpha_beta( best_eval } + +// store the base LMP reductions statically +const LMP_REDUCTION: [[u32; 64]; 64] = { + let mut out = [[0u32; 64]; 64]; + + let mut depth = 1; + let mut moves_visited = 1; + while depth < 64 { + while moves_visited < 64 { + out[depth][moves_visited] = lmr_reduction(depth, moves_visited); + moves_visited += 1; + } + depth += 1; + } + + out +}; + +// TODO const-memoize this for O(1) lookup +const fn lmr_reduction(depth: usize, moves_visited: usize) -> u32 { + // let r: u32 = match is_quiet { + // false => 20 * int_ln(depth) * int_ln(moves_visited) / 335, + // true => 135 * int_ln(depth) * int_ln(moves_visited) / 275 + // }; + 135 * int_ln(depth) * int_ln(moves_visited) / 275 +} + +#[allow( + clippy::cast_sign_loss, + clippy::cast_precision_loss, + clippy::cast_possible_truncation +)] +const fn int_ln(n: usize) -> u32 { + (1.0 / f64::consts::LOG2_E * n.ilog2() as f64) as u32 +} diff --git a/src/settings.rs b/src/settings.rs index df332bb..b550563 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -10,8 +10,9 @@ pub const RFP: bool = cfg!(feature = "rfp"); pub const PVS: bool = cfg!(feature = "pvs"); pub const KILLERS: bool = cfg!(feature = "killers"); pub const HISTORIES: bool = cfg!(feature = "histories"); +pub const LMR: bool = cfg!(feature = "lmr"); -// Can be tweaked, hava an effect on elo +// These can be tweaked, have an effect on elo pub const QS_CHECK_EVASION_LIMIT: usize = 2; pub const MAX_QS_DEPTH: usize = 12; // Maximum search depth. In practice likely never reached, but has an effect on memory usage of the program @@ -19,6 +20,11 @@ pub const MAX_AB_DEPTH: usize = 128; // How far a static eval needs to be over beta to initiate an RFP cutoff pub const RFP_MARGIN: usize = 50; +// How early we start Late Move Reductions +// The better our move ordering is, the earlier we can do LMR, the more we hopefully prune +pub const MOVES_BEFORE_LMR: usize = 4; + + #[inline] pub fn repr() -> String { format!( From 85c052277277856abd0cd460357f78e23188637a Mon Sep 17 00:00:00 2001 From: heofthetea Date: Sat, 25 Apr 2026 20:01:32 +0200 Subject: [PATCH 2/6] late move reductions formula stolen plainly from CPW --- src/alpha_beta.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/alpha_beta.rs b/src/alpha_beta.rs index a3b0d94..6e3a1af 100644 --- a/src/alpha_beta.rs +++ b/src/alpha_beta.rs @@ -160,9 +160,16 @@ pub fn alpha_beta( } else { #[allow(clippy::useless_let_if_seq)] let mut reduction = 1; - if settings::LMR && moves_visited >= settings::MOVES_BEFORE_LMR { - reduction = LMP_REDUCTION[depth][moves_visited.clamp(0, 63)]; + if settings::LMR && moves_visited >= settings::MOVES_BEFORE_LMR && depth > 2 { + // ensure we always reduce less than `depth`, otherwise we run into overflows and search until the end of the universe + reduction += LMP_REDUCTION[depth][moves_visited.clamp(0, 63)].min(depth as u32); } + + debug_assert!( + reduction as usize <= depth, + "have fun at the end of the universe" + ); + eval = -alpha_beta::( depth - reduction as usize, -alpha - 1, @@ -267,20 +274,22 @@ pub fn alpha_beta( best_eval } +// const LMP_LAZYLOCK = s + // store the base LMP reductions statically const LMP_REDUCTION: [[u32; 64]; 64] = { let mut out = [[0u32; 64]; 64]; let mut depth = 1; - let mut moves_visited = 1; while depth < 64 { + let mut moves_visited = 1; while moves_visited < 64 { out[depth][moves_visited] = lmr_reduction(depth, moves_visited); + // assert!(lmr_reduction(depth, moves_visited) != 0, "we have a non-zero value"); moves_visited += 1; } depth += 1; } - out }; @@ -301,3 +310,13 @@ const fn lmr_reduction(depth: usize, moves_visited: usize) -> u32 { const fn int_ln(n: usize) -> u32 { (1.0 / f64::consts::LOG2_E * n.ilog2() as f64) as u32 } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn print_lmp_board() { + println!("{LMP_REDUCTION:?}"); + } +} From 54594a2912d26602d7e4910bd0b473581717e027 Mon Sep 17 00:00:00 2001 From: heofthetea Date: Sun, 26 Apr 2026 13:16:26 +0200 Subject: [PATCH 3/6] lmr debug prints --- src/alpha_beta.rs | 13 +++++++------ src/iterative_deepening.rs | 12 ++++++++++-- src/types/search_data.rs | 4 ++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/alpha_beta.rs b/src/alpha_beta.rs index 6e3a1af..8fa710f 100644 --- a/src/alpha_beta.rs +++ b/src/alpha_beta.rs @@ -155,14 +155,13 @@ pub fn alpha_beta( // Principal Variation Search // We assume that the first move from the move ordering is the PV move; // Since the TT move, if existant, is in first place anyway this automatically includes information from shallower search depths - eval = -alpha_beta::(depth - 1, -beta, -alpha, sd, ply + 1, true); } else { #[allow(clippy::useless_let_if_seq)] let mut reduction = 1; if settings::LMR && moves_visited >= settings::MOVES_BEFORE_LMR && depth > 2 { // ensure we always reduce less than `depth`, otherwise we run into overflows and search until the end of the universe - reduction += LMP_REDUCTION[depth][moves_visited.clamp(0, 63)].min(depth as u32); + reduction += LMR_REDUCTION[depth][moves_visited.clamp(0, 63)].min(depth as u32); } debug_assert!( @@ -181,14 +180,16 @@ pub fn alpha_beta( if eval > alpha { // If our shallow-depth search raised alpha, we perform a search at full depth but still with a null window - // in hopes that we can avoid a full search + // in hopes that we can still avoid a full search if reduction > 1 { // Search non-PV moves with null window + sd.total_lmr_researches.fetch_add(1, Ordering::Relaxed); eval = -alpha_beta::(depth - 1, -alpha - 1, -alpha, sd, ply + 1, true); } // ONLY do full-window full-depth re-searching if the current Node is on PV - we don't care for re-searching OffPV nodes // (if they actually improve over the PV that variation will be searched again at the last PV node anyway) if eval > alpha && PV_NODE { + sd.total_pvs_researches.fetch_add(1, Ordering::Relaxed); eval = -alpha_beta::(depth - 1, -beta, -alpha, sd, ply + 1, true); } } @@ -276,8 +277,8 @@ pub fn alpha_beta( // const LMP_LAZYLOCK = s -// store the base LMP reductions statically -const LMP_REDUCTION: [[u32; 64]; 64] = { +// store the base LMR reductions statically +const LMR_REDUCTION: [[u32; 64]; 64] = { let mut out = [[0u32; 64]; 64]; let mut depth = 1; @@ -317,6 +318,6 @@ mod tests { #[test] fn print_lmp_board() { - println!("{LMP_REDUCTION:?}"); + println!("{LMR_REDUCTION:?}"); } } diff --git a/src/iterative_deepening.rs b/src/iterative_deepening.rs index 2741bf1..371a9c3 100644 --- a/src/iterative_deepening.rs +++ b/src/iterative_deepening.rs @@ -38,6 +38,8 @@ pub fn iterative_deepening( println!("AB Nodes: Nodes visited in standard Alpha-Beta"); println!("QS nodes: Nodes visited in Quiescence search"); println!("TT Hits : Times a TT entry was reused"); + println!("LMR Res : Total Late Move Reduction re-searches"); + println!("PVS Res : Total Principal Variation Search re-searches"); println!("GlobTime: Total elapsed time since search started (ms)"); println!( "EBF : Effective Branch Factor (Relative to the previous depth iteration)" @@ -62,7 +64,7 @@ pub fn iterative_deepening( } println!( - "{:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} PV", + "{:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} PV", "Depth", "Seldepth", "Score", @@ -73,6 +75,8 @@ pub fn iterative_deepening( "AB Nodes", "QS Nodes", "TT Hits", + "LMR Res", + "PVS Res", "GlobTime", "EBF", "AB EBF" @@ -167,6 +171,8 @@ pub fn iterative_deepening( #[allow(clippy::cast_precision_loss)] if debug { let iteration_tt_hits = iteration_search_data.total_tt_hits.load(Ordering::Relaxed); + let iteration_lmr_researches = iteration_search_data.total_lmr_researches.load(Ordering::Relaxed); + let iteration_pvs_researches = iteration_search_data.total_pvs_researches.load(Ordering::Relaxed); let global_duration = global_start.elapsed(); let current_total_nodes = iteration_nodes as f64; @@ -186,7 +192,7 @@ pub fn iterative_deepening( }; println!( - "{:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {}", + "{:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8} {}", depth, seldepth, best_eval_overall, @@ -197,6 +203,8 @@ pub fn iterative_deepening( format_usize(iteration_ab_nodes), format_usize(iteration_qs_nodes), format_usize(iteration_tt_hits), + format_usize(iteration_lmr_researches), + format_usize(iteration_pvs_researches), format_usize(global_duration.as_millis() as usize), format_f64(ebf), format_f64(ab_ebf), diff --git a/src/types/search_data.rs b/src/types/search_data.rs index 86938d2..8957e0f 100644 --- a/src/types/search_data.rs +++ b/src/types/search_data.rs @@ -22,6 +22,8 @@ pub struct SharedSearchData<'sd> { pub total_qs_nodes: AtomicUsize, pub total_eval_nodes: AtomicUsize, pub total_tt_hits: AtomicUsize, + pub total_lmr_researches: AtomicUsize, + pub total_pvs_researches: AtomicUsize, // stores whether the current search got cancelled due to timeout // TODO find out whether this can be eliminated in favor of using only `stop` pub timeout_occurred: AtomicBool, @@ -45,6 +47,8 @@ impl<'sd> SharedSearchData<'sd> { total_qs_nodes: AtomicUsize::new(0), total_eval_nodes: AtomicUsize::new(0), total_tt_hits: AtomicUsize::new(0), + total_lmr_researches: AtomicUsize::new(0), + total_pvs_researches: AtomicUsize::new(0), } } } From 6e1fab3bd0a056909fc9134cae032d9c2ec5b10c Mon Sep 17 00:00:00 2001 From: heofthetea Date: Sun, 26 Apr 2026 17:59:29 +0200 Subject: [PATCH 4/6] fix lmr formula lol --- src/alpha_beta.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/alpha_beta.rs b/src/alpha_beta.rs index 8fa710f..9dbd219 100644 --- a/src/alpha_beta.rs +++ b/src/alpha_beta.rs @@ -294,13 +294,8 @@ const LMR_REDUCTION: [[u32; 64]; 64] = { out }; -// TODO const-memoize this for O(1) lookup const fn lmr_reduction(depth: usize, moves_visited: usize) -> u32 { - // let r: u32 = match is_quiet { - // false => 20 * int_ln(depth) * int_ln(moves_visited) / 335, - // true => 135 * int_ln(depth) * int_ln(moves_visited) / 275 - // }; - 135 * int_ln(depth) * int_ln(moves_visited) / 275 + (1.35 + int_ln(depth) as f64 * int_ln(moves_visited) as f64 / 2.75) as u32 } #[allow( From 691f177828e81ac326643b4a01cd06c5f868ff92 Mon Sep 17 00:00:00 2001 From: heofthetea Date: Sun, 26 Apr 2026 22:06:05 +0200 Subject: [PATCH 5/6] clamp depth on LMR table lookup also fix clippy --- src/alpha_beta.rs | 12 +++++++----- src/iterative_deepening.rs | 8 ++++++-- src/move_scoring.rs | 11 ----------- src/settings.rs | 1 - 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/alpha_beta.rs b/src/alpha_beta.rs index 9dbd219..2329ea9 100644 --- a/src/alpha_beta.rs +++ b/src/alpha_beta.rs @@ -59,9 +59,6 @@ pub fn alpha_beta( let mut tt_move: Option = None; let tt_score; - // TODO: Implement Proper PVS if you want to use pv_node checks - //let pv_node = beta > alpha + 1; // TODO - if settings::TT_AB { // TODO: legal detection to prevent collisions #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] @@ -157,11 +154,13 @@ pub fn alpha_beta( // Since the TT move, if existant, is in first place anyway this automatically includes information from shallower search depths eval = -alpha_beta::(depth - 1, -beta, -alpha, sd, ply + 1, true); } else { - #[allow(clippy::useless_let_if_seq)] let mut reduction = 1; + + #[allow(clippy::useless_let_if_seq, clippy::cast_possible_truncation)] if settings::LMR && moves_visited >= settings::MOVES_BEFORE_LMR && depth > 2 { // ensure we always reduce less than `depth`, otherwise we run into overflows and search until the end of the universe - reduction += LMR_REDUCTION[depth][moves_visited.clamp(0, 63)].min(depth as u32); + reduction += + LMR_REDUCTION[depth.clamp(0, 63)][moves_visited.clamp(0, 63)].min(depth as u32); } debug_assert!( @@ -294,10 +293,13 @@ const LMR_REDUCTION: [[u32; 64]; 64] = { out }; +#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] const fn lmr_reduction(depth: usize, moves_visited: usize) -> u32 { (1.35 + int_ln(depth) as f64 * int_ln(moves_visited) as f64 / 2.75) as u32 } +/// Method really is only there because `f64::ln` is not a const function and so cannot be called at compile time +/// This here expresses a ln on integers using the constant log 2 #[allow( clippy::cast_sign_loss, clippy::cast_precision_loss, diff --git a/src/iterative_deepening.rs b/src/iterative_deepening.rs index 371a9c3..df2946b 100644 --- a/src/iterative_deepening.rs +++ b/src/iterative_deepening.rs @@ -171,8 +171,12 @@ pub fn iterative_deepening( #[allow(clippy::cast_precision_loss)] if debug { let iteration_tt_hits = iteration_search_data.total_tt_hits.load(Ordering::Relaxed); - let iteration_lmr_researches = iteration_search_data.total_lmr_researches.load(Ordering::Relaxed); - let iteration_pvs_researches = iteration_search_data.total_pvs_researches.load(Ordering::Relaxed); + let iteration_lmr_researches = iteration_search_data + .total_lmr_researches + .load(Ordering::Relaxed); + let iteration_pvs_researches = iteration_search_data + .total_pvs_researches + .load(Ordering::Relaxed); let global_duration = global_start.elapsed(); let current_total_nodes = iteration_nodes as f64; diff --git a/src/move_scoring.rs b/src/move_scoring.rs index 6d646f2..7da3c68 100644 --- a/src/move_scoring.rs +++ b/src/move_scoring.rs @@ -140,12 +140,6 @@ const MAX_HISTORY_VALUE: i32 = i16::MAX as i32; /// vectors are two-dimensional arrays indexed by `[from_square][to_square]` pub struct HistoryTable([[AtomicI32; 64]; 64], [[AtomicI32; 64]; 64]); -// impl Default for HistoryTable { -// fn default() -> Self { -// Self::new() -// } -// } - impl HistoryTable { pub fn new() -> Self { Self( @@ -183,11 +177,6 @@ impl HistoryTable { } } - #[allow(dead_code)] - pub fn get_relative_history(&self, _mv: DecodedMove, _color: Color) -> i32 { - todo!() - } - /// Age history values between search iterations /// I have no idea why this is useful, but the Relative History Paper (Winands et. al.) suggests it /// and apparently Histories lose ELO without it diff --git a/src/settings.rs b/src/settings.rs index b550563..5b50856 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -24,7 +24,6 @@ pub const RFP_MARGIN: usize = 50; // The better our move ordering is, the earlier we can do LMR, the more we hopefully prune pub const MOVES_BEFORE_LMR: usize = 4; - #[inline] pub fn repr() -> String { format!( From d339b1a570d8062bfead94332ba0bbdf963c4547 Mon Sep 17 00:00:00 2001 From: heofthetea Date: Mon, 27 Apr 2026 02:14:25 +0200 Subject: [PATCH 6/6] fix crashes (again) --- src/alpha_beta.rs | 3 +++ src/communication.rs | 5 +++-- src/iterative_deepening.rs | 2 +- src/types/search_data.rs | 4 ++-- testing/sprt.sh | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/alpha_beta.rs b/src/alpha_beta.rs index 2329ea9..8907125 100644 --- a/src/alpha_beta.rs +++ b/src/alpha_beta.rs @@ -38,6 +38,8 @@ pub fn alpha_beta( *sd.local_seldepth = (*sd.local_seldepth).max(ply); sd.total_alpha_beta_nodes.fetch_add(1, Ordering::Relaxed); + assert!(depth <= MAX_AB_DEPTH); + if sd.board.is_threefold_repetition() || sd.board.is_50_move_rule() { return 0; } @@ -315,6 +317,7 @@ mod tests { #[test] fn print_lmp_board() { + println!("{}", u32::from(false)); println!("{LMR_REDUCTION:?}"); } } diff --git a/src/communication.rs b/src/communication.rs index d55a3fb..c98a1c7 100644 --- a/src/communication.rs +++ b/src/communication.rs @@ -9,13 +9,14 @@ use crate::{ }; use std::{ env, - io::{self, BufRead, Write}, + io::{self, BufRead, BufReader, Write}, process::exit, time::Duration, }; pub fn handle_communication(board: &mut Board) { let stdin = io::stdin(); + let reader = BufReader::with_capacity(65536, stdin.lock()); let mut stdout = io::stdout(); let args: Vec = env::args().collect(); @@ -34,7 +35,7 @@ pub fn handle_communication(board: &mut Board) { exit(0); } - for line_res in stdin.lock().lines() { + for line_res in reader.lines() { let Ok(line) = line_res else { break }; let mut parts = line.split_whitespace(); diff --git a/src/iterative_deepening.rs b/src/iterative_deepening.rs index df2946b..4c3340e 100644 --- a/src/iterative_deepening.rs +++ b/src/iterative_deepening.rs @@ -99,7 +99,7 @@ pub fn iterative_deepening( let global_start = Instant::now(); let mut previouse_iteration_ab_nodes: usize = 0; let mut previouse_iteration_qs_nodes: usize = 0; - let mut killers = [EncodedMove(0); MAX_AB_DEPTH]; + let mut killers = [EncodedMove(0); MAX_AB_DEPTH + 1]; HISTORY_TABLE.age(); for depth in 1..=max_depth { diff --git a/src/types/search_data.rs b/src/types/search_data.rs index 8957e0f..eac8950 100644 --- a/src/types/search_data.rs +++ b/src/types/search_data.rs @@ -14,7 +14,7 @@ pub struct SharedSearchData<'sd> { pub board: &'sd mut Board, pub stop: &'sd Arc, pub local_seldepth: &'sd mut usize, - pub killers: &'sd mut [EncodedMove; MAX_AB_DEPTH], + pub killers: &'sd mut [EncodedMove; MAX_AB_DEPTH + 1], pub ab_ply: usize, // From here these are only used for additional info collection @@ -34,7 +34,7 @@ impl<'sd> SharedSearchData<'sd> { board: &'sd mut Board, stop: &'sd Arc, local_seldepth: &'sd mut usize, - killers: &'sd mut [EncodedMove; MAX_AB_DEPTH], + killers: &'sd mut [EncodedMove; MAX_AB_DEPTH + 1], ) -> Self { Self { board, diff --git a/testing/sprt.sh b/testing/sprt.sh index 772095f..dc4fb56 100755 --- a/testing/sprt.sh +++ b/testing/sprt.sh @@ -19,7 +19,7 @@ run_sprt() { -openings file=8moves_v3.pgn format=pgn order=random \ -concurrency 8 \ -rounds 5000 \ - -recover \ + -log append=false engine=true file=sprt.log \ -sprt elo0=0 elo1=10 alpha=0.05 beta=0.05 } run_sprt $@