123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- use bitboard::Bitboard;
- use board::Board;
- use search::*;
- use movegen::*;
- use std::io::BufRead;
- use std::process::exit;
- use std::sync::mpsc::{Sender, self};
- use std::time::{Duration, Instant};
- use zobrist::ZobristTable;
- use std::thread;
- use std::sync::{Arc, RwLock};
- use crate::ttable::{RepetitionTable, Cache};
- use crate::{uci, zobrist};
- ///
- /// Message types that are sent from the uci interface to the engine thread.
- ///
- pub enum Command {
- Uci,
- Position{ pos: Board, moves: Vec<String> },
- SetPiece(Bitboard),
- Go(SearchInfo),
- SetOption{ name: String, val: String },
- Print,
- IsReady,
- Ping,
- Stop,
- NewGame,
- GetState,
- GetInfo,
- Quit,
- }
- pub enum EngineOptionType {
- Spin{ min: i64, max: i64, default: i64 },
- }
- ///
- /// 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
- }
- #[derive(PartialEq)]
- pub enum SearchMessage {
- Stop
- }
- ///
- /// Stores the state of the chess engine.
- ///
- pub struct Engine {
- board: Board,
- move_history: Arc<RwLock<RepetitionTable>>,
- hash: Arc<RwLock<Cache>>,
- zobrist_table: Arc<ZobristTable>,
- search_thread: Option<SearchThread>,
- }
- struct SearchThread {
- channel: Sender<SearchMessage>,
- join_handle: thread::JoinHandle<bool>,
- }
- 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() -> Self {
- Engine {
- board: Board::default(),
- move_history: Arc::new(RwLock::new(RepetitionTable::new())),
- hash: Arc::new(RwLock::new(Cache::new_in_megabytes(4))),
- zobrist_table: Arc::new(ZobristTable::new()),
- search_thread: None,
- }
- }
- pub fn run(&mut self) {
- for line_m in std::io::stdin().lock().lines() {
- let line = line_m.unwrap();
- //println!("received line: {}", line);
- let command = uci::parse_command(&line);
- if let Ok(cmd) = command {
- self.run_command(cmd);
- }
- else if let Err(err) = command {
- println!("Error parsing command: {}", err);
- }
- }
- }
- fn run_command(&mut self, cmd: Command) {
- match cmd {
- Command::Uci => {
- println!("id name bishop 1.0");
- println!("id author Geile Siech");
- println!("option name Hash type spin default 4 min 0 max 100000000");
- println!("uciok");
- },
- Command::IsReady => {
- println!("readyok");
- },
- Command::NewGame => {
- },
- Command::Position { pos, moves } => {
- self.set_position(pos, &moves);
- },
- Command::SetOption { name, val } => {
- self.set_option(&name, &val);
- },
- Command::Go(si) => {
- self.stop_search();
- self.start_search(&si);
- },
- Command::Stop => {
- self.stop_search();
- },
- Command::Quit => {
- exit(0);
- }
- _ => {
- println!("not yet implemented!");
- }
- }
- }
- fn set_position(&mut self, pos: Board, moves: &Vec<String>) {
- generate_legal_moves_new(&mut pos.clone(), WHITE, false);
- self.board = pos;
- // ensure correct zobrist hash
- self.board.zobrist_hash = self.board.calculate_zobrist(zobrist::get_zobrist());
- let mut move_history = self.move_history.write().unwrap();
- move_history.increment(self.board.calculate_zobrist(&self.zobrist_table));
- if !moves.is_empty() {
- move_history.clear();
- }
- for mov in moves {
- let m = self.board.parse_move(&mov);
- if let Ok(mm) = m {
- self.board.apply(mm);
- if mm.is_capture() {
- move_history.clear();
- }
-
- move_history.increment(self.board.calculate_zobrist(&self.zobrist_table));
- }
- else {
- println!("{}", self.board.beautiful_print());
- println!("error parsing move {}: {}", mov, m.err().unwrap_or(String::new()));
- break;
- }
- }
- }
- fn start_search(&mut self, si: &SearchInfo) {
- let (snd, rcv) = mpsc::channel::<SearchMessage>();
- let join_handle =
- if si.perft {
- let depth = si.depth.unwrap_or(0);
- let mut pc = SearchControl::new_perft(&self.board, rcv, depth);
- let search_rtn = move || {
- let before = Instant::now();
- let rv = pc.perft(depth);
- if !rv {
- let after = Instant::now();
- let nodes = pc.nodes;
- let duration = after - before;
- let nps = (nodes * 1000).checked_div(duration.as_millis() as _);
- println!("finished in {} ms", duration.as_millis());
- println!("nodes: {} nps: {}", nodes, nps.unwrap_or(0));
- }
- rv
- };
- thread::spawn(search_rtn)
- }
- else {
- let mut sc = SearchControl::new(&self.board, rcv, self.hash.clone(), self.move_history.clone());
- sc.hash = self.hash.clone();
- sc.movetime = si.movetime.map(|ms| Duration::from_millis(ms as _));
- if self.board.turn == WHITE {
- sc.remaining_time = si.wtime.map(|ms| Duration::from_millis(ms as _));
- }
- else if self.board.turn == BLACK {
- sc.remaining_time = si.btime.map(|ms| Duration::from_millis(ms as _));
- }
- let depth = si.depth;
- let search_rtn = move || sc.iterative_deepening(depth);
- thread::spawn(search_rtn)
- };
- self.search_thread = Some(SearchThread {
- channel: snd,
- join_handle
- });
- //self.search_threads.push(st);
- }
- fn stop_search(&mut self) {
- if let Some(st) = std::mem::replace(&mut self.search_thread, None) {
- let _msg_res = st.channel.send(SearchMessage::Stop);
- let _res = st.join_handle.join().unwrap();
- }
- }
- fn newgame(&mut self) {
- self.set_position(Board::default(), &Vec::new());
- self.hash.write().unwrap().clear();
- self.move_history.write().unwrap().clear();
- }
- fn set_option(&mut self, name: &str, val: &str) {
- match name {
- "Hash" => {
- let size = val.parse::<usize>();
- if let Ok(megabytes) = size {
- self.hash = Arc::new(RwLock::new(Cache::new_in_megabytes(megabytes)));
- }
- else {
- println!("invalid hash size");
- }
- },
- n => {
- println!("unknown option '{}'", n);
- }
- }
- }
- }
|