From 5b3def6b35c2bed38b924544203979c801c757f9 Mon Sep 17 00:00:00 2001 From: Kyle Krowpman Date: Fri, 29 Nov 2024 19:48:58 -0800 Subject: [PATCH] put node count in timer --- src/search.rs | 44 ++++++++--------------- src/search_master.rs | 11 +++++- src/timer.rs | 85 +++++++++++++++++++++++++------------------- 3 files changed, 73 insertions(+), 67 deletions(-) diff --git a/src/search.rs b/src/search.rs index 5338847..8071c17 100644 --- a/src/search.rs +++ b/src/search.rs @@ -10,11 +10,9 @@ use super::types::*; pub struct Search<'a> { id: u16, - stop: bool, sel_depth: Ply, timer: Timer, tt: &'a TT, - nodes: u64, move_sorter: MoveSorter, } @@ -24,9 +22,7 @@ impl<'a> Search<'a> { id, timer, tt, - stop: false, sel_depth: 0, - nodes: 0, move_sorter: MoveSorter::new(), } } @@ -55,11 +51,7 @@ impl<'a> Search<'a> { return Some(moves[0].m); } - while !self.stop - && self.timer.start_check(depth) - && !Self::is_checkmate(value) - && depth < Depth::MAX - { + while self.timer.start_check(depth) && !Self::is_checkmate(value) && depth < Depth::MAX { (best_move, value) = self.search_root(&mut board, depth, alpha, beta); /////////////////////////////////////////////////////////////////// @@ -79,7 +71,7 @@ impl<'a> Search<'a> { beta = Self::MATE; } else { // Only print info if we're in the main thread - if self.id == 0 && !self.stop { + if self.id == 0 && !self.timer.local_stop() { best_move.inspect(|&m| self.print_info(&mut board, depth, m, value)); } alpha = value - Self::ASPIRATION_WINDOW; @@ -139,7 +131,7 @@ impl<'a> Search<'a> { } board.pop(); - if self.stop { + if self.timer.local_stop() { break; } @@ -163,7 +155,7 @@ impl<'a> Search<'a> { } }); - if !self.stop { + if !self.timer.local_stop() { self.tt.insert(board, depth, alpha, best_move, Bound::Exact); } (best_move, alpha) @@ -177,11 +169,6 @@ impl<'a> Search<'a> { mut beta: Value, ply: Ply, ) -> Value { - if self.stop || self.timer.stop_check() { - self.stop = true; - return 0; - } - /////////////////////////////////////////////////////////////////// // Mate distance pruning - will help reduce // some nodes when checkmate is near. @@ -190,7 +177,6 @@ impl<'a> Search<'a> { alpha = alpha.max(-mate_value); beta = beta.min(mate_value - 1); if alpha >= beta { - self.nodes += 1; return alpha; } @@ -211,7 +197,9 @@ impl<'a> Search<'a> { return self.q_search(board, alpha, beta, ply); } - self.nodes += 1; + if self.timer.stop_check() { + return 0; + } if board.is_draw() { return 0; @@ -261,7 +249,7 @@ impl<'a> Search<'a> { board.push_null(); let value = -self.search(board, depth - r - 1, -beta, -beta + 1, ply); board.pop_null(); - if self.stop { + if self.timer.local_stop() { return 0; } if value >= beta { @@ -328,7 +316,7 @@ impl<'a> Search<'a> { board.pop(); - if self.stop { + if self.timer.local_stop() { return 0; } @@ -363,7 +351,7 @@ impl<'a> Search<'a> { } } - if !self.stop { + if !self.timer.local_stop() { best_move = best_move.or_else(|| { if moves.len() > 0 { Some(moves[0].m) @@ -384,13 +372,10 @@ impl<'a> Search<'a> { mut beta: Value, ply: Ply, ) -> Value { - if self.stop || self.timer.stop_check() { - self.stop = true; + if self.timer.stop_check() { return 0; } - self.nodes += 1; - if board.is_draw() { return 0; } @@ -439,7 +424,7 @@ impl<'a> Search<'a> { let value = -self.q_search(board, -beta, -alpha, ply + 1); board.pop(); - if self.stop { + if self.timer.local_stop() { return 0; } @@ -533,6 +518,7 @@ impl<'a> Search<'a> { }; let elapsed = self.timer.elapsed(); + let nodes = self.timer.nodes(); println!("info currmove {m} depth {depth} seldepth {sel_depth} time {time} score {score_str} nodes {nodes} nps {nps} pv {pv}", m = m, @@ -540,8 +526,8 @@ impl<'a> Search<'a> { sel_depth = self.sel_depth, time = elapsed.as_millis(), score_str = score_str, - nodes = self.nodes, - nps = (self.nodes as f64 / elapsed.as_secs_f64()) as u64, + nodes = nodes, + nps = (nodes as f64 / elapsed.as_secs_f64()) as u64, pv = self.get_pv(board, depth)); } diff --git a/src/search_master.rs b/src/search_master.rs index f78d1a4..31c54d4 100644 --- a/src/search_master.rs +++ b/src/search_master.rs @@ -1,6 +1,7 @@ use std::sync; use std::sync::atomic::*; use std::sync::mpsc::Receiver; +use std::sync::Arc; use std::thread; use std::time::Duration; @@ -79,11 +80,18 @@ impl SearchMaster { fn go(&mut self, time_control: TimeControl) { self.stop.store(false, Ordering::SeqCst); + let nodes = Arc::new(AtomicU64::new(0)); let best_move = thread::scope(|s| { // Create main search thread with the actual time control. This thread controls self.stop. let mut main_search_thread = Search::new( - Timer::new(&self.board, time_control, self.stop.clone(), self.overhead), + Timer::new( + &self.board, + time_control, + self.stop.clone(), + nodes.clone(), + self.overhead, + ), &self.tt, 0, ); @@ -96,6 +104,7 @@ impl SearchMaster { &thread_board, TimeControl::Infinite, self.stop.clone(), + nodes.clone(), self.overhead, ), &self.tt, diff --git a/src/timer.rs b/src/timer.rs index d77ef0d..b1a154d 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -3,8 +3,7 @@ use super::moov::*; use super::types::*; use crate::piece::*; use regex::{Match, Regex}; -use std::sync; -use std::sync::atomic::AtomicBool; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::{Arc, LazyLock}; use std::time::{Duration, Instant}; @@ -142,7 +141,9 @@ static GO_RE: LazyLock = LazyLock::new(|| { pub struct Timer { control: TimeControl, start_time: Instant, - stop: Arc, + global_stop: Arc, + local_stop: bool, + nodes: Arc, times_checked: u64, time_target: Duration, time_maximum: Duration, @@ -155,6 +156,7 @@ impl Timer { board: &Board, control: TimeControl, stop: Arc, + nodes: Arc, overhead: Duration, ) -> Self { let (time_target, time_maximum) = if let TimeControl::Variable { .. } = control { @@ -165,7 +167,9 @@ impl Timer { Self { start_time: Instant::now(), - stop, + global_stop: stop, + local_stop: false, + nodes, control, overhead, time_target, @@ -192,13 +196,7 @@ impl Timer { Color::Black => (btime, binc), }; - let mtg = moves_to_go.unwrap_or_else(|| { - (Self::MTG_INTERCEPT - + Self::MTG_EVAL_WEIGHT * (board.simple_eval().abs() as f32) - + Self::MTG_MOVE_WEIGHT * (board.fullmove_number() as f32)) - .ceil() - .max(1.0) as u32 - }); + let mtg = moves_to_go.unwrap_or(40); let time_target = time.min(time / mtg + inc.unwrap_or(Duration::ZERO)); let time_maximum = time_target + (time - time_target) / 4; @@ -206,8 +204,13 @@ impl Timer { (time_target, time_maximum) } - pub fn start_check(&self, depth: Depth) -> bool { - if self.stop.load(sync::atomic::Ordering::Relaxed) { + pub fn start_check(&mut self, depth: Depth) -> bool { + if self.local_stop { + return false; + } + + if self.global_stop.load(Ordering::Relaxed) { + self.nodes.fetch_add(self.times_checked, Ordering::Relaxed); return false; } @@ -225,54 +228,65 @@ impl Timer { }; if !start { - self.stop.store(true, sync::atomic::Ordering::Relaxed); + self.local_stop = true; + self.global_stop.store(true, Ordering::Relaxed); } start } pub fn stop_check(&mut self) -> bool { + if self.local_stop { + return true; + } + self.times_checked += 1; - let should_check = self.times_checked & Self::CHECK_FLAG == 0; + if self.times_checked < Self::CHECK_FREQ { + return false; + } + + let nodes = self.nodes.fetch_add(self.times_checked, Ordering::Relaxed); + self.times_checked = 0; - if should_check && self.stop.load(sync::atomic::Ordering::Relaxed) { + if self.global_stop.load(Ordering::Relaxed) { + self.local_stop = true; return true; } let stop = match self.control { TimeControl::Infinite => false, - TimeControl::FixedDuration(duration) => { - if should_check { - self.elapsed() + self.overhead >= duration - } else { - false - } - } - TimeControl::Variable { .. } => { - if should_check { - self.elapsed() + self.overhead >= self.time_maximum - } else { - false - } - } + TimeControl::FixedDuration(duration) => self.elapsed() + self.overhead >= duration, + TimeControl::Variable { .. } => self.elapsed() + self.overhead >= self.time_maximum, TimeControl::FixedDepth(_) => false, - TimeControl::FixedNodes(nodes) => self.times_checked >= nodes, + TimeControl::FixedNodes(stop_nodes) => nodes >= stop_nodes, }; if stop { - self.stop.store(true, sync::atomic::Ordering::Relaxed); + self.nodes.fetch_add(self.times_checked, Ordering::Relaxed); + self.local_stop = true; + self.global_stop.store(true, Ordering::Relaxed); } + stop } pub fn stop(&mut self) { - self.stop.store(true, sync::atomic::Ordering::SeqCst); + self.local_stop = true; + self.global_stop.store(true, Ordering::SeqCst); } pub fn elapsed(&self) -> Duration { self.start_time.elapsed() } + pub fn nodes(&self) -> u64 { + self.nodes.load(Ordering::Relaxed) + self.times_checked + } + + pub fn local_stop(&self) -> bool { + self.local_stop + } + pub fn update(&mut self, best_move: Option) { if self .last_best_move @@ -286,8 +300,5 @@ impl Timer { } impl Timer { - const CHECK_FLAG: u64 = 0x1000 - 1; - const MTG_INTERCEPT: f32 = 52.52; - const MTG_EVAL_WEIGHT: f32 = -0.01833; - const MTG_MOVE_WEIGHT: f32 = -0.4657; + const CHECK_FREQ: u64 = 4096; }