use search::apply_move; use bitboard::Bitboard; use game::Game; use search::*; use movegen::*; use evaluate::PosValue; use log::{info}; use std::time::{Duration, Instant}; use std::collections::{VecDeque, HashMap}; use zobrist::ZobristTable; use hash::Cache; use std::thread::sleep_ms; use std::sync::mpsc::{Receiver, Sender}; pub enum EngineMsg { SetBoard(Game), SetPiece(Bitboard), Search(SearchInfo), Ping, Stop, NewGame, GetState, GetInfo, } pub enum SearchInfo { Depth(isize), Movetime(isize), Time { wtime: isize, btime: isize, winc: isize, binc: isize, movestogo: isize }, Infinite, } pub enum InterfaceMsg { } pub struct Engine { game: Game, messages: VecDeque, r: Receiver, s: Sender, hash: Cache, zobrist_table: ZobristTable } impl Engine { pub fn new(r: Receiver, s: Sender) -> Self { let mut eng = Engine { game: Game::default(), messages: VecDeque::new(), r, s, hash: Cache::new(), zobrist_table: ZobristTable::new() }; eng.game.zobrist = Some(eng.game.calculate_zobrist(&eng.zobrist_table)); eng } pub fn run(&mut self) { while let Some(msg) = self.dequeue_message() { match msg { EngineMsg::SetBoard(g) => { self.game = g; }, EngineMsg::Search(ref info) => { self.start_search(info); } _ => {} } } } /** * blocks until a message is available */ fn dequeue_message(&mut self) -> Option { if self.messages.is_empty() { self.r.recv().ok() } else { self.messages.pop_front() } } fn start_search(&mut self, si: &SearchInfo) { match si { SearchInfo::Depth(depth) => { let receiver = &mut self.r; let messages = &mut self.messages; let mut check_fn = || -> bool { if let Ok(msg) = receiver.try_recv() { if let EngineMsg::Stop = msg { return true; } else { messages.push_back(msg); } } return false; }; let mut sc = SearchControl{ nodes: 0, check: &mut check_fn }; let before = Instant::now(); let search_result = search(&self.game, &mut sc, &mut self.hash, *depth as i32); if let SearchResult::Finished(best_move, best_val) = search_result { let time = before.elapsed(); let nps = (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64; info!("bestmove {}", best_move.to_string()); info!("searched {} nodes in {} ms ({} nodes/s)", sc.nodes, time.as_millis(), (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64); println!("info nps {}", nps.to_string()); println!("bestmove {}", best_move.to_string()); } }, SearchInfo::Movetime(millis) => { let before = Instant::now(); let mut depth = 1; let receiver = &mut self.r; let messages = &mut self.messages; let mut check_fn = || -> bool { if let Ok(msg) = receiver.try_recv() { if let EngineMsg::Stop = msg { return true; } else { messages.push_back(msg); } } if before.elapsed() > Duration::from_millis(*millis as _) { return true; } return false; }; let mut sc = SearchControl{ nodes: 0, check: &mut check_fn }; let mut best_move = Move::default(); let mut best_val: PosValue = i32::max_value() as _; loop { let search_result = search(&mut self.game, &mut sc, &mut self.hash, depth as i32); if let SearchResult::Finished(bm, bv) = search_result { info!("depth: {} bm: {}, bv: {}", depth, bm.to_string(), bv); best_move = bm; best_val = bv; let elapsed = before.elapsed(); let nodes = sc.nodes; let nps = (nodes as f64 / elapsed.as_nanos() as f64 * 1000000000.0) as i64; let cp = best_val as i64; println!("info depth {} score cp {} time {} nodes {} nps {} pv {}", depth, cp, elapsed.as_millis(), nodes, nps, best_move.to_string()); info!("info depth {} score cp {} time {} nodes {} nps {} pv {}", depth, cp, elapsed.as_millis(), nodes, nps, best_move.to_string()); depth += 1; } else if let SearchResult::Mate(bm, in_turns) = search_result { best_move = bm; let elapsed = before.elapsed(); let nodes = sc.nodes; let nps = (nodes as f64 / elapsed.as_nanos() as f64 * 1000000000.0) as i64; let pv = best_val as i64; println!("info depth {} score mate {} time {} nodes {} nps {} pv {}", depth, in_turns, elapsed.as_millis(), nodes, nps, bm.to_string()); info!("info depth {} score mate {} time {} nodes {} nps {} pv {}", depth, pv, elapsed.as_millis(), nodes, nps, bm.to_string()); break; } else { break; } } info!("bestmove {}", best_move.to_string()); println!("bestmove {}", best_move.to_string()); }, _ => {} } } } /* fn dequeue_message(queue: &mut VecDeque, r: &Receiver) -> Option { if queue.is_empty() { r.recv().ok() } else { queue.pop_front() } } pub fn run_engine(r: Receiver, _s: Sender) { let mut game = Game::default(); let mut messages: VecDeque = VecDeque::new(); loop { let msg = dequeue_message(&mut messages, &r).unwrap(); match msg { EngineMsg::SetBoard(g) => { game = g; }, EngineMsg::SetPiece(_) => { println!("SetPiece") }, EngineMsg::Search(info) => { match info { SearchInfo::Depth(depth) => { let mut check_fn = || -> bool { if let Ok(msg) = r.try_recv() { if let EngineMsg::Stop = msg { return true; } else { messages.push_back(msg); } } return false; }; let mut sc = SearchControl{ nodes: 0, check: &mut check_fn }; let before = Instant::now(); let best_move = search(&game, &mut sc, depth as i32); let time = before.elapsed(); let nps = (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64; info!("bestmove {}", best_move.to_string()); info!("searched {} nodes in {} ms ({} nodes/s)", sc.nodes, time.as_millis(), (sc.nodes as f64 / time.as_millis() as f64 * 1000.0) as i64); println!("info nps {}", nps.to_string()); println!("bestmove {}", best_move.to_string()); }, SearchInfo::Movetime(millis) => { let before = Instant::now(); let mut depth = 1; let mut check_fn = || -> bool { if let Ok(msg) = r.try_recv() { if let EngineMsg::Stop = msg { return true; } else { messages.push_back(msg); } } if before.elapsed() > Duration::from_millis(millis as _) { return true; } return false; }; let mut sc = SearchControl{ nodes: 0, check: &mut check_fn }; let mut best_move = Move::default(); loop { let bm = search(&game, &mut sc, depth as i32); if bm != Move::default() { best_move = bm; } else { break; } depth += 1; } println!("bestmove {}", best_move.to_string()); }, _ => {} } }, EngineMsg::Ping => { println!("Ping") }, EngineMsg::Stop => { println!("Stop") }, EngineMsg::NewGame => { //println!("NewGame") }, EngineMsg::GetState => { println!("GetState") }, EngineMsg::GetInfo => { println!("GetInfo") }, } //println!("{}", game.beautiful_print()); //let moves = generate_moves(&game, WHITE); } } */