summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
authorMicha White <botahamec@outlook.com>2023-12-28 10:26:06 -0500
committerMicha White <botahamec@outlook.com>2023-12-28 10:26:06 -0500
commit923aeb11d61b6e20ad33598ef7f44d0a6dfa6c15 (patch)
tree03c2139625f32257b2ac3c246ae623e8fdfce16f /engine
parentef59ee8eb6562dfd724c1cb0bbd37aebc7d798ad (diff)
Changes made to support the eventual C API
Diffstat (limited to 'engine')
-rw-r--r--engine/src/engine.rs48
-rw-r--r--engine/src/lib.rs3
-rw-r--r--engine/src/main.rs54
-rw-r--r--engine/src/search.rs52
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)
}