|  | @@ -7,14 +7,13 @@ use log::{info};
 | 
	
		
			
				|  |  |  use std::time::{Duration, Instant};
 | 
	
		
			
				|  |  |  use std::collections::VecDeque;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +use std::thread::sleep_ms;
 | 
	
		
			
				|  |  |  use std::sync::mpsc::{Receiver, Sender};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  pub enum EngineMsg {
 | 
	
		
			
				|  |  |      SetBoard(Game),
 | 
	
		
			
				|  |  |      SetPiece(Bitboard),
 | 
	
		
			
				|  |  | -    Search(i32),
 | 
	
		
			
				|  |  | +    Search(SearchInfo),
 | 
	
		
			
				|  |  |      Ping,
 | 
	
		
			
				|  |  |      Stop,
 | 
	
		
			
				|  |  |      NewGame,
 | 
	
	
		
			
				|  | @@ -22,19 +21,136 @@ pub enum EngineMsg {
 | 
	
		
			
				|  |  |      GetInfo,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +pub enum SearchInfo {
 | 
	
		
			
				|  |  | +    Depth(isize),
 | 
	
		
			
				|  |  | +    Movetime(isize),
 | 
	
		
			
				|  |  | +    Time {
 | 
	
		
			
				|  |  | +        wtime: isize,
 | 
	
		
			
				|  |  | +        btime: isize,
 | 
	
		
			
				|  |  | +        winc: isize,
 | 
	
		
			
				|  |  | +        binc: isize,
 | 
	
		
			
				|  |  | +        movestogo: isize
 | 
	
		
			
				|  |  | +    },
 | 
	
		
			
				|  |  | +    Infinite,
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -pub enum EngineState {
 | 
	
		
			
				|  |  | -    Idle,
 | 
	
		
			
				|  |  | -    Searching
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  pub enum InterfaceMsg {
 | 
	
		
			
				|  |  | -    BestMove(Move),
 | 
	
		
			
				|  |  | -    State(EngineState)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +pub struct Engine {
 | 
	
		
			
				|  |  | +    game: Game,
 | 
	
		
			
				|  |  | +    messages: VecDeque<EngineMsg>,
 | 
	
		
			
				|  |  | +    r: Receiver<EngineMsg>,
 | 
	
		
			
				|  |  | +    s: Sender<InterfaceMsg>
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +impl Engine {
 | 
	
		
			
				|  |  | +    pub fn new(r: Receiver<EngineMsg>, s: Sender<InterfaceMsg>) -> Self {
 | 
	
		
			
				|  |  | +        Engine {
 | 
	
		
			
				|  |  | +            game: Game::default(),
 | 
	
		
			
				|  |  | +            messages: VecDeque::new(),
 | 
	
		
			
				|  |  | +            r, s
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    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<EngineMsg> {
 | 
	
		
			
				|  |  | +        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 best_move = search(&self.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 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();
 | 
	
		
			
				|  |  | +                loop {
 | 
	
		
			
				|  |  | +                    let bm = search(&mut self.game, &mut sc, depth as i32);
 | 
	
		
			
				|  |  | +                    if bm != Move::default() {
 | 
	
		
			
				|  |  | +                        best_move = bm;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    else {
 | 
	
		
			
				|  |  | +                        break;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                    depth += 1;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                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()
 | 
	
	
		
			
				|  | @@ -44,7 +160,7 @@ fn dequeue_message(queue: &mut VecDeque<EngineMsg>, r: &Receiver<EngineMsg>) ->
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -pub fn run_engine(r: Receiver<EngineMsg>, s: Sender<InterfaceMsg>) {
 | 
	
		
			
				|  |  | +pub fn run_engine(r: Receiver<EngineMsg>, _s: Sender<InterfaceMsg>) {
 | 
	
		
			
				|  |  |      let mut game = Game::default();
 | 
	
		
			
				|  |  |      let mut messages: VecDeque<EngineMsg> = VecDeque::new();
 | 
	
		
			
				|  |  |      loop {
 | 
	
	
		
			
				|  | @@ -52,34 +168,71 @@ pub fn run_engine(r: Receiver<EngineMsg>, s: Sender<InterfaceMsg>) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          match msg {
 | 
	
		
			
				|  |  |              EngineMsg::SetBoard(g) => {
 | 
	
		
			
				|  |  | -                //println!("SetBoard");
 | 
	
		
			
				|  |  |                  game = g;
 | 
	
		
			
				|  |  |              },
 | 
	
		
			
				|  |  |              EngineMsg::SetPiece(_) => { println!("SetPiece") },
 | 
	
		
			
				|  |  | -            EngineMsg::Search(depth) => {
 | 
	
		
			
				|  |  | -                //info!("searching in pos\n {}", game.beautiful_print());
 | 
	
		
			
				|  |  | -                //println!("Search {}", depth);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                let mut check_fn = || -> bool {
 | 
	
		
			
				|  |  | -                    if let Ok(msg) = r.try_recv() {
 | 
	
		
			
				|  |  | -                        if let EngineMsg::Stop = msg {
 | 
	
		
			
				|  |  | -                            return true;
 | 
	
		
			
				|  |  | +            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;
 | 
	
		
			
				|  |  |                          }
 | 
	
		
			
				|  |  | -                        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);
 | 
	
		
			
				|  |  | -                let time = before.elapsed();
 | 
	
		
			
				|  |  | -                println!("bestmove {}", best_move.to_string());
 | 
	
		
			
				|  |  | -                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!("bestmove {}", best_move.to_string());
 | 
	
		
			
				|  |  | +                    },
 | 
	
		
			
				|  |  | +                    _ => {}
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |              },
 | 
	
		
			
				|  |  |              EngineMsg::Ping => { println!("Ping") },
 | 
	
		
			
				|  |  |              EngineMsg::Stop => { println!("Stop") },
 | 
	
	
		
			
				|  | @@ -96,3 +249,4 @@ pub fn run_engine(r: Receiver<EngineMsg>, s: Sender<InterfaceMsg>) {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +*/
 |