diff options
| author | Micha White <botahamec@outlook.com> | 2023-12-28 10:26:06 -0500 |
|---|---|---|
| committer | Micha White <botahamec@outlook.com> | 2023-12-28 10:26:06 -0500 |
| commit | 923aeb11d61b6e20ad33598ef7f44d0a6dfa6c15 (patch) | |
| tree | 03c2139625f32257b2ac3c246ae623e8fdfce16f /engine | |
| parent | ef59ee8eb6562dfd724c1cb0bbd37aebc7d798ad (diff) | |
Changes made to support the eventual C API
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/src/engine.rs | 48 | ||||
| -rw-r--r-- | engine/src/lib.rs | 3 | ||||
| -rw-r--r-- | engine/src/main.rs | 54 | ||||
| -rw-r--r-- | engine/src/search.rs | 52 |
4 files changed, 116 insertions, 41 deletions
diff --git a/engine/src/engine.rs b/engine/src/engine.rs index 9bc4893..6402f21 100644 --- a/engine/src/engine.rs +++ b/engine/src/engine.rs @@ -15,6 +15,8 @@ pub const ENGINE_NAME: &str = "Ampere"; pub const ENGINE_AUTHOR: &str = "Mica White"; pub const ENGINE_ABOUT: &str = "Ampere Checkers Bot v1.0\nCopyright Mica White"; +type EvalThread = JoinHandle<(Evaluation, Option<Move>)>; + pub struct Engine<'a> { position: Mutex<CheckersBitBoard>, transposition_table: TranspositionTable, @@ -22,7 +24,7 @@ pub struct Engine<'a> { debug: AtomicBool, frontend: &'a dyn Frontend, - current_thread: Mutex<Option<JoinHandle<Evaluation>>>, + current_thread: Mutex<Option<EvalThread>>, current_task: Mutex<Option<Arc<EvaluationTask<'a>>>>, pondering_task: Mutex<Option<Arc<EvaluationTask<'a>>>>, } @@ -119,6 +121,7 @@ pub enum SearchLimit { } #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[repr(C)] pub struct ActualLimit { pub nodes: Option<NonZeroUsize>, pub depth: Option<NonZeroU8>, @@ -155,6 +158,10 @@ impl<'a> Engine<'a> { PossibleMoves::moves(*position).contains(checker_move) } + pub fn current_position(&self) -> CheckersBitBoard { + *self.position.lock() + } + pub fn reset_position(&self) { self.set_position(CheckersBitBoard::starting_position()) } @@ -176,6 +183,41 @@ impl<'a> Engine<'a> { } } + pub fn evaluate( + &self, + cancel: Option<&AtomicBool>, + settings: EvaluationSettings, + ) -> (Evaluation, Option<Move>) { + // finish the pondering thread + let mut pondering_task = self.pondering_task.lock(); + if let Some(task) = pondering_task.take() { + task.end_ponder_flag.store(true, Ordering::Release); + } + + let position = *self.position.lock(); + let transposition_table = self.transposition_table.get_ref(); + let limits = settings.get_limits(position.turn()); + let allowed_moves = settings.restrict_moves; + let cancel_flag = AtomicBool::new(false); + let end_ponder_flag = AtomicBool::new(false); + + let nodes_explored = AtomicUsize::new(0); + + let task = EvaluationTask { + position, + transposition_table, + allowed_moves, + limits, + ponder: false, + cancel_flag, + end_ponder_flag, + + nodes_explored, + }; + + search(Arc::new(task), self.frontend, cancel) + } + pub fn start_evaluation(&'static self, settings: EvaluationSettings) { // finish the pondering thread let mut pondering_task = self.pondering_task.lock(); @@ -215,7 +257,7 @@ impl<'a> Engine<'a> { *pondering_task = Some(task_ref.clone()); } - let thread = std::thread::spawn(move || search(task_ref, self.frontend)); + let thread = std::thread::spawn(move || search(task_ref, self.frontend, None)); let mut thread_ptr = self.current_thread.lock(); *thread_ptr = Some(thread); } @@ -224,7 +266,7 @@ impl<'a> Engine<'a> { let current_task = self.current_task.lock().take()?; current_task.cancel_flag.store(true, Ordering::Release); - self.current_thread.lock().take(); + let _ = self.current_thread.lock().take()?.join(); Some(()) } diff --git a/engine/src/lib.rs b/engine/src/lib.rs index c75d9c2..d87c225 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -7,9 +7,10 @@ pub use engine::{ ENGINE_AUTHOR, ENGINE_NAME, }; pub use eval::Evaluation; -pub use model::{CheckersBitBoard, Move, Piece, PieceColor, PossibleMoves}; +pub use model::{CheckersBitBoard, Move, MoveDirection, Piece, PieceColor, PossibleMoves}; pub use transposition_table::{TranspositionTable, TranspositionTableRef}; +pub mod c_abi; mod engine; mod eval; mod lazysort; diff --git a/engine/src/main.rs b/engine/src/main.rs index 32e0f62..d4bcc48 100644 --- a/engine/src/main.rs +++ b/engine/src/main.rs @@ -1,7 +1,8 @@ -use std::{num::NonZeroU8, thread::sleep, time::Duration}; +use std::num::NonZeroU8; use engine::{ActualLimit, Engine, EvaluationSettings, Frontend}; use mimalloc::MiMalloc; +use model::CheckersBitBoard; #[global_allocator] static ALLOCATOR: MiMalloc = MiMalloc; @@ -11,28 +12,47 @@ const DEPTH: u8 = 19; struct BasicFrontend; impl Frontend for BasicFrontend { - fn debug(&self, _: &str) {} + fn debug(&self, msg: &str) { + println!("{msg}"); + } fn report_best_move(&self, best_move: model::Move) { println!("{best_move}"); - std::process::exit(0); } } fn main() { let engine = Box::leak(Box::new(Engine::new(1_000_000, &BasicFrontend))); - engine.start_evaluation(EvaluationSettings { - restrict_moves: None, - ponder: false, - clock: engine::Clock::Unlimited, - search_until: engine::SearchLimit::Limited(ActualLimit { - nodes: None, - depth: Some(NonZeroU8::new(DEPTH).unwrap()), - time: None, - }), - }); - - loop { - sleep(Duration::from_millis(200)) - } + let (_, best) = engine.evaluate( + None, + EvaluationSettings { + restrict_moves: None, + ponder: false, + clock: engine::Clock::Unlimited, + search_until: engine::SearchLimit::Limited(ActualLimit { + nodes: None, + depth: Some(NonZeroU8::new(DEPTH).unwrap()), + time: None, + }), + }, + ); + engine.set_position(CheckersBitBoard::new( + 4294967295, + 2206409603, + 3005432691, + model::PieceColor::Light, + )); + engine.evaluate( + None, + EvaluationSettings { + restrict_moves: None, + ponder: false, + clock: engine::Clock::Unlimited, + search_until: engine::SearchLimit::Limited(ActualLimit { + nodes: None, + depth: Some(NonZeroU8::new(DEPTH).unwrap()), + time: None, + }), + }, + ); } diff --git a/engine/src/search.rs b/engine/src/search.rs index b8fd982..9c8ea26 100644 --- a/engine/src/search.rs +++ b/engine/src/search.rs @@ -99,9 +99,13 @@ pub fn negamax( } } -pub fn search(task: Arc<EvaluationTask>, frontend: &dyn Frontend) -> Evaluation { +pub fn search( + task: Arc<EvaluationTask>, + frontend: &dyn Frontend, + cancel: Option<&AtomicBool>, +) -> (Evaluation, Option<Move>) { let board = task.position; - let cancel_flag = &task.cancel_flag; + let cancel_flag = cancel.unwrap_or(&task.cancel_flag); let allowed_moves = task.allowed_moves.clone(); let limits = task.limits; @@ -115,25 +119,28 @@ pub fn search(task: Arc<EvaluationTask>, frontend: &dyn Frontend) -> Evaluation let mut eval = Evaluation::DRAW; let mut best_move = None; loop { - if let Some(max_depth) = max_depth { - if depth > max_depth.get() { - break; + // don't leave search is no good moves have been found + if best_move.is_some() { + if let Some(max_depth) = max_depth { + if depth > max_depth.get() { + break; + } } - } - if let Some(max_time) = max_time { - if Instant::now() > max_time { - break; + if let Some(max_time) = max_time { + if Instant::now() > max_time { + break; + } } - } - if let Some(max_nodes) = max_nodes { - if task - .nodes_explored - .load(std::sync::atomic::Ordering::Acquire) - > max_nodes.get() - { - break; + if let Some(max_nodes) = max_nodes { + if task + .nodes_explored + .load(std::sync::atomic::Ordering::Acquire) + > max_nodes.get() + { + break; + } } } @@ -148,7 +155,7 @@ pub fn search(task: Arc<EvaluationTask>, frontend: &dyn Frontend) -> Evaluation ); // prevent incomplete search from overwriting evaluation - if cancel_flag.load(std::sync::atomic::Ordering::Acquire) { + if best_move.is_some() && cancel_flag.load(std::sync::atomic::Ordering::Acquire) { break; } @@ -167,7 +174,7 @@ pub fn search(task: Arc<EvaluationTask>, frontend: &dyn Frontend) -> Evaluation ); // prevent incomplete search from overwriting evaluation - if cancel_flag.load(std::sync::atomic::Ordering::Acquire) { + if best_move.is_some() && cancel_flag.load(std::sync::atomic::Ordering::Acquire) { break; } @@ -193,6 +200,11 @@ pub fn search(task: Arc<EvaluationTask>, frontend: &dyn Frontend) -> Evaluation beta = eval.add_f32(0.125); } + if eval.is_force_sequence() { + // we don't need to search any deeper + return (eval, best_move); + } + depth += 1; } @@ -231,5 +243,5 @@ pub fn search(task: Arc<EvaluationTask>, frontend: &dyn Frontend) -> Evaluation } } - eval + (eval, best_move) } |
