engine.rs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. use bitboard::Bitboard;
  2. use board::Board;
  3. use search::*;
  4. use movegen::*;
  5. use std::io::BufRead;
  6. use std::process::exit;
  7. use std::sync::mpsc::{Sender, self};
  8. use std::time::{Duration, Instant};
  9. use zobrist::ZobristTable;
  10. use std::thread;
  11. use std::sync::{Arc, RwLock};
  12. use crate::ttable::{RepetitionTable, Cache};
  13. use crate::{uci, zobrist};
  14. ///
  15. /// Message types that are sent from the uci interface to the engine thread.
  16. ///
  17. pub enum Command {
  18. Uci,
  19. Position{ pos: Board, moves: Vec<String> },
  20. SetPiece(Bitboard),
  21. Go(SearchInfo),
  22. SetOption{ name: String, val: String },
  23. Print,
  24. IsReady,
  25. Ping,
  26. Stop,
  27. NewGame,
  28. GetState,
  29. GetInfo,
  30. Quit,
  31. }
  32. pub enum EngineOptionType {
  33. Spin{ min: i64, max: i64, default: i64 },
  34. }
  35. ///
  36. /// Structure holding information for a search command.
  37. ///
  38. pub struct SearchInfo {
  39. pub depth: Option<i32>,
  40. pub movetime: Option<i32>,
  41. pub wtime: Option<isize>,
  42. pub btime: Option<isize>,
  43. pub winc: Option<isize>,
  44. pub binc: Option<isize>,
  45. pub movestogo: Option<isize>,
  46. pub perft: bool,
  47. pub infinite: bool
  48. }
  49. #[derive(PartialEq)]
  50. pub enum SearchMessage {
  51. Stop
  52. }
  53. ///
  54. /// Stores the state of the chess engine.
  55. ///
  56. pub struct Engine {
  57. board: Board,
  58. move_history: Arc<RwLock<RepetitionTable>>,
  59. hash: Arc<RwLock<Cache>>,
  60. zobrist_table: Arc<ZobristTable>,
  61. search_thread: Option<SearchThread>,
  62. }
  63. struct SearchThread {
  64. channel: Sender<SearchMessage>,
  65. join_handle: thread::JoinHandle<bool>,
  66. }
  67. impl SearchInfo {
  68. pub fn new() -> Self {
  69. SearchInfo {
  70. depth: None,
  71. movetime: None,
  72. wtime: None,
  73. btime: None,
  74. winc: None,
  75. binc: None,
  76. movestogo: None,
  77. perft: false,
  78. infinite: false
  79. }
  80. }
  81. }
  82. impl Engine {
  83. pub fn new() -> Self {
  84. Engine {
  85. board: Board::default(),
  86. move_history: Arc::new(RwLock::new(RepetitionTable::new())),
  87. hash: Arc::new(RwLock::new(Cache::new_in_megabytes(4))),
  88. zobrist_table: Arc::new(ZobristTable::new()),
  89. search_thread: None,
  90. }
  91. }
  92. pub fn run(&mut self) {
  93. for line_m in std::io::stdin().lock().lines() {
  94. let line = line_m.unwrap();
  95. //println!("received line: {}", line);
  96. let command = uci::parse_command(&line);
  97. if let Ok(cmd) = command {
  98. self.run_command(cmd);
  99. }
  100. else if let Err(err) = command {
  101. println!("Error parsing command: {}", err);
  102. }
  103. }
  104. }
  105. fn run_command(&mut self, cmd: Command) {
  106. match cmd {
  107. Command::Uci => {
  108. println!("id name bishop 1.0");
  109. println!("id author Geile Siech");
  110. println!("option name Hash type spin default 4 min 0 max 100000000");
  111. println!("uciok");
  112. },
  113. Command::IsReady => {
  114. println!("readyok");
  115. },
  116. Command::NewGame => {
  117. },
  118. Command::Position { pos, moves } => {
  119. self.set_position(pos, &moves);
  120. },
  121. Command::SetOption { name, val } => {
  122. self.set_option(&name, &val);
  123. },
  124. Command::Go(si) => {
  125. self.stop_search();
  126. self.start_search(&si);
  127. },
  128. Command::Stop => {
  129. self.stop_search();
  130. },
  131. Command::Quit => {
  132. exit(0);
  133. }
  134. _ => {
  135. println!("not yet implemented!");
  136. }
  137. }
  138. }
  139. fn set_position(&mut self, pos: Board, moves: &Vec<String>) {
  140. generate_legal_moves_new(&mut pos.clone(), WHITE, false);
  141. self.board = pos;
  142. // ensure correct zobrist hash
  143. self.board.zobrist_hash = self.board.calculate_zobrist(zobrist::get_zobrist());
  144. let mut move_history = self.move_history.write().unwrap();
  145. move_history.increment(self.board.calculate_zobrist(&self.zobrist_table));
  146. if !moves.is_empty() {
  147. move_history.clear();
  148. }
  149. for mov in moves {
  150. let m = self.board.parse_move(&mov);
  151. if let Ok(mm) = m {
  152. self.board.apply(mm);
  153. if mm.is_capture() {
  154. move_history.clear();
  155. }
  156. move_history.increment(self.board.calculate_zobrist(&self.zobrist_table));
  157. }
  158. else {
  159. println!("{}", self.board.beautiful_print());
  160. println!("error parsing move {}: {}", mov, m.err().unwrap_or(String::new()));
  161. break;
  162. }
  163. }
  164. }
  165. fn start_search(&mut self, si: &SearchInfo) {
  166. let (snd, rcv) = mpsc::channel::<SearchMessage>();
  167. let join_handle =
  168. if si.perft {
  169. let depth = si.depth.unwrap_or(0);
  170. let mut pc = SearchControl::new_perft(&self.board, rcv, depth);
  171. let search_rtn = move || {
  172. let before = Instant::now();
  173. let rv = pc.perft(depth);
  174. if !rv {
  175. let after = Instant::now();
  176. let nodes = pc.nodes;
  177. let duration = after - before;
  178. let nps = (nodes * 1000).checked_div(duration.as_millis() as _);
  179. println!("finished in {} ms", duration.as_millis());
  180. println!("nodes: {} nps: {}", nodes, nps.unwrap_or(0));
  181. }
  182. rv
  183. };
  184. thread::spawn(search_rtn)
  185. }
  186. else {
  187. let mut sc = SearchControl::new(&self.board, rcv, self.hash.clone(), self.move_history.clone());
  188. sc.hash = self.hash.clone();
  189. sc.movetime = si.movetime.map(|ms| Duration::from_millis(ms as _));
  190. if self.board.turn == WHITE {
  191. sc.remaining_time = si.wtime.map(|ms| Duration::from_millis(ms as _));
  192. }
  193. else if self.board.turn == BLACK {
  194. sc.remaining_time = si.btime.map(|ms| Duration::from_millis(ms as _));
  195. }
  196. let depth = si.depth;
  197. let search_rtn = move || sc.iterative_deepening(depth);
  198. thread::spawn(search_rtn)
  199. };
  200. self.search_thread = Some(SearchThread {
  201. channel: snd,
  202. join_handle
  203. });
  204. //self.search_threads.push(st);
  205. }
  206. fn stop_search(&mut self) {
  207. if let Some(st) = std::mem::replace(&mut self.search_thread, None) {
  208. let _msg_res = st.channel.send(SearchMessage::Stop);
  209. let _res = st.join_handle.join().unwrap();
  210. }
  211. }
  212. fn newgame(&mut self) {
  213. self.set_position(Board::default(), &Vec::new());
  214. self.hash.write().unwrap().clear();
  215. self.move_history.write().unwrap().clear();
  216. }
  217. fn set_option(&mut self, name: &str, val: &str) {
  218. match name {
  219. "Hash" => {
  220. let size = val.parse::<usize>();
  221. if let Ok(megabytes) = size {
  222. self.hash = Arc::new(RwLock::new(Cache::new_in_megabytes(megabytes)));
  223. }
  224. else {
  225. println!("invalid hash size");
  226. }
  227. },
  228. n => {
  229. println!("unknown option '{}'", n);
  230. }
  231. }
  232. }
  233. }