aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2023-08-21 23:29:58 +0300
committereug-vs <eugene@eug-vs.xyz>2023-08-21 23:29:58 +0300
commit2645673ae3e16ee5d7ce24a1efbf367d9fe9a8f0 (patch)
treeb35f4eb5fef532685b3c4d52b0985765a32f31a5
parent90190dc10c9d4ed00d4bb976d42c73f2f30098a5 (diff)
downloadchessnost-2645673ae3e16ee5d7ce24a1efbf367d9fe9a8f0.tar.gz
feat: add pondering
Also use SeqCst as the strongest ordering to avoid data races
-rw-r--r--src/grossmeister/UCI.rs69
-rw-r--r--src/grossmeister/search.rs15
2 files changed, 58 insertions, 26 deletions
diff --git a/src/grossmeister/UCI.rs b/src/grossmeister/UCI.rs
index 1997221..e10088a 100644
--- a/src/grossmeister/UCI.rs
+++ b/src/grossmeister/UCI.rs
@@ -1,4 +1,4 @@
-use std::{io::stdin, thread::{JoinHandle, spawn, sleep}, str::Split, time::Duration, sync::{Arc, atomic::AtomicBool}};
+use std::{io::stdin, thread::{JoinHandle, spawn, sleep}, str::Split, time::Duration, sync::{Arc, atomic::{AtomicBool, Ordering}}};
use crate::{board::{Board, io::IO, color::Color}, moves::{Move, MoveKind}};
@@ -96,12 +96,27 @@ impl Grossmeister {
self.transposition_table = better_self.transposition_table;
}
- search_handle = Some(self.parse_go(tokens));
+ search_handle = Some(self.parse_go(tokens, false));
}
"stop" => {
- self.should_halt.store(true, std::sync::atomic::Ordering::Relaxed);
+ self.should_halt.store(true, std::sync::atomic::Ordering::SeqCst);
+ }
+ "ponderhit" => {
+ // Since we were pondering without any scheduled halting previously,
+ // in case of ponderhit we need to schedule a new halting based on clock
+ // TODO: isn't the color flipped here? Might lead to time management bugs
+ let color = self.board.color();
+ let duration = (self.board.clock.time[color as usize] + self.board.clock.increment[color as usize]) / 20;
+ let halt_scheduled = self.schedule_halt(duration);
+
+ // Join to the current pondering search
+ if let Some(hand) = search_handle.take() {
+ hand.join().unwrap();
+ halt_scheduled.store(false, Ordering::SeqCst); // Cancel scheduled halting
+ } else {
+ panic!("Search thread not found!");
+ }
}
- "ponderhit" => todo!(),
"quit" => break,
_ => {},
}
@@ -109,11 +124,14 @@ impl Grossmeister {
}
}
- fn parse_go(&mut self, mut tokens: Split<char>) -> JoinHandle<Self> {
+ fn parse_go(&mut self, mut tokens: Split<char>, ponder: bool) -> JoinHandle<Self> {
if let Some(token) = tokens.next() {
match token {
"searchmoves" => todo!(),
- "ponder" => todo!(),
+ "ponder" => {
+ println!("info will start in pondering mode");
+ return self.parse_go(tokens, true)
+ }
"wtime" => {
if let Some(time) = tokens.next() {
let time: u64 = time.parse().unwrap();
@@ -160,40 +178,53 @@ impl Grossmeister {
},
_ => {}
}
- return self.parse_go(tokens)
+ return self.parse_go(tokens, ponder);
}
let color = self.board.color();
- let duration = (self.board.clock.time[color as usize] + self.board.clock.increment[color as usize]) / 20;
+
+ let duration = if ponder {
+ Duration::MAX
+ } else {
+ (self.board.clock.time[color as usize] + self.board.clock.increment[color as usize]) / 20
+ };
self.create_search_thread(u8::MAX, duration)
}
- fn create_search_thread(&self, depth: u8, duration: Duration) -> JoinHandle<Self> {
- let ordering = std::sync::atomic::Ordering::Relaxed;
- let should_halt = self.should_halt.clone();
- // Make sure we don't halt right away
- should_halt.store(false, std::sync::atomic::Ordering::Relaxed);
-
- let should_terminate = Arc::new(AtomicBool::new(true));
+ fn schedule_halt(&self, duration: Duration) -> Arc<AtomicBool> {
+ let halt_scheduled = Arc::new(AtomicBool::new(false));
if duration < Duration::MAX {
+ halt_scheduled.store(true, Ordering::SeqCst);
+ let should_halt = self.should_halt.clone();
+
// Create a thread that will terminate search when the duration is expired
- let should_terminate = should_terminate.clone();
+ let halt_scheduled = halt_scheduled.clone();
spawn(move || {
println!("info string terminating search in {:?}", duration);
sleep(duration);
- if should_terminate.load(ordering) {
- should_halt.store(true, ordering);
+ // Make sure schedule is not cancelled externally
+ if halt_scheduled.load(Ordering::SeqCst) {
+ should_halt.store(true, Ordering::SeqCst);
}
});
}
+ halt_scheduled
+ }
+
+ fn create_search_thread(&self, depth: u8, duration: Duration) -> JoinHandle<Self> {
+ // Make sure we don't halt right away
+ self.should_halt.store(false, Ordering::SeqCst);
+
+ let halt_scheduled = self.schedule_halt(duration);
+
// Clone current self and move it into thread to analyze a position
let mut gm = self.clone();
spawn(move || {
gm.iterative_deepening(depth);
- should_terminate.store(false, ordering); // No need to terminate search anymore
+ halt_scheduled.store(false, Ordering::SeqCst); // Cancel the scheduled halting
gm // Return better version of Self
})
}
diff --git a/src/grossmeister/search.rs b/src/grossmeister/search.rs
index 532e9ed..7b40fab 100644
--- a/src/grossmeister/search.rs
+++ b/src/grossmeister/search.rs
@@ -133,7 +133,7 @@ impl Grossmeister {
}
// Could not finish in time, return what we have so far
- if self.should_halt.load(std::sync::atomic::Ordering::Relaxed) {
+ if self.should_halt.load(std::sync::atomic::Ordering::SeqCst) {
break;
}
}
@@ -227,11 +227,8 @@ impl Grossmeister {
break;
}
- if self.should_halt.load(std::sync::atomic::Ordering::Relaxed) {
- if self.debug {
- println!("info string aborting");
- }
- self.should_halt.store(false, std::sync::atomic::Ordering::Relaxed);
+ if self.should_halt.load(std::sync::atomic::Ordering::SeqCst) {
+ println!("info string halting search");
break;
}
@@ -273,7 +270,11 @@ impl Grossmeister {
match result {
Some(r) => {
- println!("bestmove {}", r.1[0]);
+ print!("bestmove {}", r.1[0]);
+ if r.1.len() > 1 {
+ print!(" ponder {}", r.1[1])
+ }
+ println!();
r
}
None => {