123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438 |
- 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};
- use zobrist::ZobristTable;
- use hash::{Cache, RepetitionTable};
- use std::sync::Arc;
- use std::sync::mpsc::{Receiver, Sender};
- use std::panic;
- ///
- /// Message types that are sent from the uci interface to the engine thread.
- ///
- pub enum EngineMsg {
- SetBoard{ pos: Game, moves: Vec<String> },
- SetPiece(Bitboard),
- Search(SearchInfo),
- Print,
- IsReady,
- Ping,
- Stop,
- NewGame,
- GetState,
- GetInfo,
- }
- ///
- /// Structure holding information for a search command.
- ///
- pub struct SearchInfo {
- pub depth: Option<i32>,
- pub movetime: Option<i32>,
- pub wtime: Option<isize>,
- pub btime: Option<isize>,
- pub winc: Option<isize>,
- pub binc: Option<isize>,
- pub movestogo: Option<isize>,
- pub perft: bool,
- pub infinite: bool
- }
- pub enum InterfaceMsg {
- }
- ///
- /// Stores the state of the chess engine.
- ///
- /// The engine is run in a dedicated thread and will receive messages on [Engine::r]
- ///
- pub struct Engine {
- game: Game,
- move_history: RepetitionTable,
- messages: VecDeque<EngineMsg>,
- r: Receiver<EngineMsg>,
- #[allow(unused)]
- s: Sender<InterfaceMsg>,
-
- hash: Cache,
- zobrist_table: Arc<ZobristTable>
- }
- impl SearchInfo {
- pub fn new() -> Self {
- SearchInfo {
- depth: None,
- movetime: None,
- wtime: None,
- btime: None,
- winc: None,
- binc: None,
- movestogo: None,
- perft: false,
- infinite: false
- }
- }
- }
- impl Engine {
- pub fn new(r: Receiver<EngineMsg>, s: Sender<InterfaceMsg>) -> Self {
- let mut eng = Engine {
- game: Game::default(),
- move_history: RepetitionTable::new(),
- messages: VecDeque::new(),
- r, s,
- hash: Cache::new(),
- zobrist_table: Arc::new(ZobristTable::new())
- };
- eng.game.zobrist = Some((eng.zobrist_table.clone(), eng.game.calculate_zobrist(&eng.zobrist_table)));
- eng
- }
- pub fn run(&mut self) {
- panic::set_hook(Box::new(|panic_info| {
- if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
- info!("panic occurred: {s:?}");
- } else {
- info!("panic occurred");
- }
- }));
- while let Some(msg) = self.dequeue_message() {
- match msg {
- EngineMsg::SetBoard{ pos, moves } => {
- self.set_position(pos, &moves);
- },
- EngineMsg::Search(ref info) => {
- self.start_search(info);
- },
- EngineMsg::Print => {
- println!("{}", self.game.beautiful_print());
- },
- EngineMsg::IsReady => {
- println!("readyok");
- }
- _ => {}
- }
- }
- }
- fn set_position(&mut self, pos: Game, moves: &Vec<String>) {
- self.game = pos;
- self.game.zobrist = Some((self.zobrist_table.clone(), self.game.calculate_zobrist(&self.zobrist_table)));
- self.move_history.clear();
- self.move_history.increment(self.game.calculate_zobrist(&self.zobrist_table));
- for mov in moves {
- let m = self.game.parse_move(&mov);
- if let Ok(mm) = m {
- self.game.apply(mm);
- self.move_history.increment(self.game.calculate_zobrist(&self.zobrist_table));
- }
- else {
- println!("{}", self.game.beautiful_print());
- println!("error parsing move {}", mov);
- break;
- }
- }
- }
- /**
- * blocks until a message is available
- */
- fn dequeue_message(&mut self) -> Option<EngineMsg> {
- if self.messages.is_empty() {
- self.r.recv().ok()
- }
- else {
- self.messages.pop_front()
- }
- }
- #[allow(unused)]
- fn try_dequeue_message(&mut self) -> Option<EngineMsg> {
- if self.messages.is_empty() {
- self.r.try_recv().ok()
- }
- else {
- self.messages.pop_front()
- }
- }
- fn reconstruct_pv(game: &Game, hash: &Cache) -> Vec<Move> {
- let mut pv: Vec<Move> = Vec::new();
- let mut g = game.clone();
- loop {
- let mce = hash.lookup(&g);
- if let Some(ce) = mce {
- pv.push(ce.mov);
- g.apply(ce.mov);
- }
- else {
- break;
- }
- }
- return pv;
- }
- fn start_search(&mut self, si: &SearchInfo) {
- if let None = self.game.zobrist {
- self.game.zobrist = Some((self.zobrist_table.clone(), self.game.calculate_zobrist(&self.zobrist_table)));
- }
- let side = self.game.turn;
- let receiver = &mut self.r;
- let messages = &mut self.messages;
- let before = Instant::now();
- let mut depth = 1;
- let mut check_fn =
- || -> bool {
- if !messages.is_empty() {
- if let EngineMsg::Stop = messages[0] {
- return true;
- }
- }
- if let Ok(msg) = receiver.try_recv() {
- if let EngineMsg::Stop = msg {
- messages.push_back(msg);
- return true;
- }
- if let EngineMsg::IsReady = msg {
- println!("readyok");
- return false;
- }
- else {
- messages.push_back(msg);
- }
- }
- if let Some(millis) = si.movetime {
- if before.elapsed() > Duration::from_millis(millis as _) {
- return true;
- }
- }
- else if side == WHITE {
- if let Some(wtime) = si.wtime {
- if before.elapsed() > Duration::from_millis((wtime / 17) as _) {
- return true;
- }
- }
- }
- else if side == BLACK {
- if let Some(btime) = si.btime {
- if before.elapsed() > Duration::from_millis((btime / 17) as _) {
- return true;
- }
- }
- }
- return false;
- };
- let mut sc = SearchControl::new(&mut check_fn, &mut self.move_history, 0);
- let mut best_move = Move::default();
- let mut best_val: PosValue;
- if si.perft {
- if let Some(dep) = si.depth {
- depth = dep;
- }
- let invalid = perft(&mut self.game, &mut sc, depth as i32);
- if !invalid {
- let elapsed = before.elapsed();
- let nodes = sc.nodes;
- let nps = (nodes as f64 / elapsed.as_nanos() as f64 * 1000000000.0) as i64;
- println!("info depth {} nodes {} nps {}", depth, nodes, nps);
- }
- return;
- }
- let mut alpha = crate::evaluate::MIN_VALUE;
- let mut beta = crate::evaluate::MAX_VALUE;
- let window_size = 10;
- let mut is_windowed = false;
- loop {
- sc.set_depth(depth);
- let search_result = search(&mut self.game, &mut sc, &mut self.hash, alpha, beta, depth as i32);
- if let SearchResult::Invalid = search_result {
- if is_windowed {
- alpha = crate::evaluate::MIN_VALUE;
- beta = crate::evaluate::MAX_VALUE;
- is_windowed = false;
- continue;
- }
- }
- if let SearchResult::Finished(bm, bv) = search_result {
- info!("depth: {} bm: {}, bv: {}", depth, bm.to_string(), bv);
- if bv >= beta || bv <= alpha {
- alpha = crate::evaluate::MIN_VALUE;
- beta = crate::evaluate::MAX_VALUE;
- is_windowed = false;
- continue;
- }
- else {
- //alpha = crate::evaluate::MIN_VALUE;
- //beta = crate::evaluate::MAX_VALUE;
- is_windowed = true;
- alpha = bv - window_size;
- beta = bv + window_size;
- }
- 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;
- let hashfull = self.hash.fullness_permill();
-
- let pv_array = Self::reconstruct_pv(&self.game, &self.hash);
- let pv = &pv_array.iter().filter(|&m| !m.is_null()).map(|m| m.to_string()).fold(String::new(), |a, b| a + " " + &b)[0..];
- if let Some(plies) = crate::evaluate::is_mate_in_p1(bv) {
- let turns = plies / 2;
- let infostring = format!("info depth {} score mate {} time {} nodes {} nps {} hashfull {} pv{}", depth, turns, elapsed.as_millis(), nodes, nps, hashfull, pv);
- println!("{}", infostring);
- info!("{}", infostring);
- }
- else {
- let infostring = format!("info depth {} score cp {} time {} nodes {} nps {} hashfull {} pv{}", depth, cp, elapsed.as_millis(), nodes, nps, hashfull, pv);
- println!("{}", infostring);
- info!("{}", infostring);
- }
- depth += 1;
- }
- else if let SearchResult::Cancelled(Some((bm, _bv))) = search_result {
- if best_move == Move::Nullmove {
- best_move = bm;
- //best_val = bv;
- }
- break;
- }
- else {
- break;
- }
- if let Some(d) = si.depth {
- if depth > d {
- break;
- }
- }
- }
- info!("bestmove {}", best_move.to_string());
- println!("bestmove {}", best_move.to_string());
- }
- }
- /*
- fn dequeue_message(queue: &mut VecDeque<EngineMsg>, r: &Receiver<EngineMsg>) -> Option<EngineMsg> {
- if queue.is_empty() {
- r.recv().ok()
- }
- else {
- queue.pop_front()
- }
- }
- pub fn run_engine(r: Receiver<EngineMsg>, _s: Sender<InterfaceMsg>) {
- let mut game = Game::default();
- let mut messages: VecDeque<EngineMsg> = 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);
- }
- }
- */
|