blunders_engine/
engine.rs1use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::mpsc::{self, Sender};
5use std::sync::Arc;
6use std::thread::JoinHandle;
7
8use crate::error::{self, ErrorKind};
9use crate::position::{Game, Position};
10use crate::search::{self, SearchResult};
11use crate::timeman::Mode;
12use crate::TranspositionTable;
13
14#[derive(Debug, Clone, Eq, PartialEq)]
24pub struct EngineBuilder {
25 game: Game,
26 transpositions_mb: usize,
27 num_threads: usize,
28 debug: bool,
29}
30
31impl EngineBuilder {
32 pub fn new() -> Self {
34 Self {
35 game: Game::start_position(),
36 transpositions_mb: 1,
37 num_threads: 1,
38 debug: true,
39 }
40 }
41
42 pub fn build(&self) -> Engine {
44 let tt = Arc::new(TranspositionTable::with_mb(self.transpositions_mb));
45 let stopper = Arc::new(AtomicBool::new(false));
46
47 Engine {
48 game: self.game.clone(),
49 tt,
50 stopper,
51 debug: self.debug,
52 search_handle: None,
53 }
54 }
55
56 pub fn game(mut self, game: Game) -> Self {
58 self.game = game;
59 self
60 }
61
62 pub fn position(mut self, position: Position) -> Self {
64 self.game = Game::from(position);
65 self
66 }
67
68 pub fn threads(mut self, num_threads: usize) -> Self {
70 self.num_threads = num_threads;
71 self
72 }
73
74 pub fn transpositions_mb(mut self, transpositions_mb: usize) -> Self {
76 self.transpositions_mb = transpositions_mb;
77 self
78 }
79
80 pub fn debug(mut self, debug: bool) -> Self {
82 self.debug = debug;
83 self
84 }
85}
86
87pub struct Engine {
93 game: Game,
95 tt: Arc<TranspositionTable>,
96 stopper: Arc<AtomicBool>,
97 debug: bool,
98
99 search_handle: Option<JoinHandle<()>>,
101}
102
103impl Engine {
104 pub fn new() -> Self {
105 Self {
106 game: Game::from(Position::start_position()),
107 tt: Arc::new(TranspositionTable::new()),
108 stopper: Arc::new(AtomicBool::new(false)),
109 debug: true,
110 search_handle: None,
111 }
112 }
113
114 pub fn game(&self) -> &Game {
116 &self.game
117 }
118
119 pub fn debug(&self) -> &bool {
121 &self.debug
122 }
123
124 pub fn transposition_table(&self) -> &TranspositionTable {
126 &self.tt
127 }
128
129 pub fn set_game<T: Into<Game>>(&mut self, game: T) {
131 self.game = game.into();
132 }
133
134 pub fn set_debug(&mut self, new_debug: bool) {
136 self.debug = new_debug;
137 }
138
139 pub fn new_game(&mut self) -> error::Result<()> {
142 self.try_clear_transpositions()
143 }
144
145 pub fn try_set_transpositions_mb(&mut self, new_mb: usize) -> error::Result<usize> {
149 Arc::get_mut(&mut self.tt)
150 .map(|inner_tt| inner_tt.set_mb(new_mb))
151 .ok_or(ErrorKind::EngineTranspositionTableInUse.into())
152 }
153
154 pub fn try_clear_transpositions(&mut self) -> error::Result<()> {
158 Arc::get_mut(&mut self.tt)
159 .map(|inner_tt| inner_tt.clear())
160 .ok_or(ErrorKind::EngineTranspositionTableInUse.into())
161 }
162
163 pub fn search_sync(&mut self, mode: Mode) -> SearchResult {
165 self.stop();
167 self.wait();
168 self.unstop();
169
170 let (sender, receiver) = mpsc::channel();
171 self.search(mode, sender).unwrap();
172 self.wait();
173 receiver.recv().unwrap()
174 }
175
176 pub fn search<T>(&mut self, mode: Mode, sender: Sender<T>) -> error::Result<()>
180 where
181 T: From<SearchResult> + Send + 'static,
182 {
183 if self.search_handle.is_none() {
184 self.unstop();
185
186 let handle = search::search_nonblocking(
187 self.game.clone(),
188 mode,
189 Arc::clone(&self.tt),
190 Arc::clone(&self.stopper),
191 self.debug,
192 sender,
193 );
194 self.search_handle = Some(handle);
195
196 Ok(())
197 } else {
198 Err((ErrorKind::EngineAlreadySearching, "failed to begin search").into())
199 }
200 }
201
202 pub fn ponder(&self) {
203 todo!()
204 }
205
206 pub fn stop(&self) {
208 self.stopper.store(true, Ordering::Relaxed);
209 }
210
211 pub fn unstop(&self) {
213 self.stopper.store(false, Ordering::Relaxed);
214 }
215
216 pub fn wait(&mut self) {
218 let handle_opt = self.search_handle.take();
219
220 if let Some(handle) = handle_opt {
221 handle.join().unwrap();
222 }
223 }
224
225 pub fn ready(&self) -> bool {
228 self.search_handle.is_none()
229 }
230
231 pub fn shutdown(self) {}
237}
238
239impl Default for Engine {
240 fn default() -> Self {
241 Self::new()
242 }
243}
244
245impl Drop for Engine {
246 fn drop(&mut self) {
247 self.stop();
248 self.wait();
249 }
250}