diff --git a/chess_uci/src/lib.rs b/chess_uci/src/lib.rs index 91b9be2..fce245d 100644 --- a/chess_uci/src/lib.rs +++ b/chess_uci/src/lib.rs @@ -6,10 +6,13 @@ pub mod uci_command; use log::{info, warn}; use std::io::*; use std::time::Duration; +use std::result::Result; use crate::uci_command::*; use chess::{Game, Board, ChessMove}; +const DEFAULT_TIME: (Option, Option) = (Some(Duration::from_secs(120 * 60)), None); + /// Structure used to manage a chess game with a uci engine. /// /// It needs a in / out communication channel to read / push line by line uci commands @@ -40,9 +43,9 @@ pub struct UciEngine { initial: Board, game: Game, /// white (total, incr), black (total, incr) - time: ((Option, Option), (Option, Option)), + time: [(Option, Option); 2], /// white, black - player: (Player, Player) + player: [Player; 2], } pub enum UciOption { @@ -74,26 +77,26 @@ impl UciEngine { uciok: false, initial: Board::default(), game: Game::new(), - time: ((None, None), (None, None)), - player: (Player::Human{elo: None}, Player::Human{elo: None}), + time: [DEFAULT_TIME; 2], + player: [Player::Human{elo: None}; 2], } } pub fn game_option(&mut self, option: GameOption) -> GameOption { let old_value: GameOption; match option { - GameOption::WhiteTotalTime{value} => {old_value = GameOption::WhiteTotalTime { value: self.time.0.0 }; - self.time.0.0 = value}, - GameOption::BlackTotalTime{value} => {old_value = GameOption::BlackTotalTime { value: self.time.1.0 }; - self.time.1.0 = value}, - GameOption::WhiteIncrement{value} => {old_value = GameOption::WhiteIncrement { value: self.time.0.1 }; - self.time.0.1 = value}, - GameOption::BlackIncrement{value} => {old_value = GameOption::BlackIncrement { value: self.time.1.1 }; - self.time.1.1 = value}, - GameOption::WhitePlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player.0 }; - self.player.0 = value}, - GameOption::BlackPlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player.1 }; - self.player.1 = value}, + GameOption::WhiteTotalTime{value} => {old_value = GameOption::WhiteTotalTime { value: self.time[Color::White.to_index()].0 }; + self.time[Color::White.to_index()].0 = value}, + GameOption::BlackTotalTime{value} => {old_value = GameOption::BlackTotalTime { value: self.time[Color::Black.to_index()].0 }; + self.time[Color::Black.to_index()].0 = value}, + GameOption::WhiteIncrement{value} => {old_value = GameOption::WhiteIncrement { value: self.time[Color::White.to_index()].1 }; + self.time[Color::White.to_index()].1 = value}, + GameOption::BlackIncrement{value} => {old_value = GameOption::BlackIncrement { value: self.time[Color::Black.to_index()].1 }; + self.time[Color::Black.to_index()].1 = value}, + GameOption::WhitePlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player[Color::White.to_index()] }; + self.player[Color::White.to_index()] = value}, + GameOption::BlackPlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player[Color::Black.to_index()] }; + self.player[Color::Black.to_index()] = value}, } old_value @@ -172,20 +175,11 @@ impl UciEngine { self.id.update_from_str(name, author); } - fn uciok(&mut self){ - self.uciok = true; - } - - /// Tell whether the uci engine has initialized - pub fn is_uciok(&self) -> bool { - self.uciok - } - /// Execute a uci command /// /// if `EngineCommand::Id`: update name or authorship of the engine /// if `EngineCommand::UciOk`: end engine initialization phase - pub fn exec(&mut self, command: &str) -> std::result::Result { + pub fn exec(&mut self, command: &str) -> Result { let uci_command = parse (&mut command.to_string()); match uci_command.clone() { Ok(EngineCommand::Id{id}) => self.update(&id), @@ -259,6 +253,52 @@ impl UciEngine { } } +impl UciEngine { + fn uciok(&mut self){ + self.uciok = true; + } + + /// Tell whether the uci engine has initialized + pub fn is_uciok(&self) -> bool { + self.uciok + } + + pub fn human_play(&mut self, chess_move: ChessMove) -> Result { + match self.player[self.side_to_move().to_index()] { + Player::Human{..} => { + if self.make_move(chess_move) { + Ok(chess_move) + } else { + Err("Invalid move for human player") + } + }, + Player::Machine {..} => Err("Not a human to play for current color.") + } + } + + pub fn go(&mut self) -> Result<(), &'static str> { + match self.player[self.side_to_move().to_index()] { + Player::Human{..} => Err("Not a machine to play for current color."), + Player::Machine {elo} => + { + if self.is_uciok() { + if let Some(elo) = elo { + self.push(GuiCommand::SetOption {option: Opt::UCILimitStrength{value: true}}); + self.push(GuiCommand::SetOption {option: Opt::UCIElo{value: elo}}); + } + let (wtime, wincr) = self.time[Color::White.to_index()]; + let (btime, bincr) = self.time[Color::Black.to_index()]; + self.push(GuiCommand::Go{wtime, wincr, btime, bincr}); + + Ok(()) + } else { + Err("UCI engine not ready") + } + } + } + } +} + use chess::{Square, Piece, Color}; impl UciEngine { pub fn piece_on(&self, square: Square) -> Option { @@ -271,5 +311,9 @@ impl UciEngine { pub fn current_position(&self) -> Board { self.game.current_position() } + + pub fn side_to_move(&self) -> Color { + self.game.side_to_move() + } } // LocalWords: uci diff --git a/chess_uci/src/uci_command.rs b/chess_uci/src/uci_command.rs index d702eed..78489dd 100644 --- a/chess_uci/src/uci_command.rs +++ b/chess_uci/src/uci_command.rs @@ -34,7 +34,10 @@ impl Id { #[derive(Debug, Clone)] pub struct Info {} #[derive(Debug, Clone)] -pub struct Opt{} +pub enum Opt{ + UCILimitStrength{value: bool}, + UCIElo{value: u32}, +} #[derive(Clone)] pub enum EngineCommand { @@ -58,7 +61,8 @@ pub enum GuiCommand { Register, // unimplemented UciNewGame, Position{position: Option, moves: Vec}, - Go, + Go{wtime: Option, wincr: Option, + btime: Option, bincr: Option}, SearchMoves{moves: Vec}, Ponder, WTime{time: Duration}, @@ -117,6 +121,20 @@ impl fmt::Display for GuiCommand { {chessmove.to_string()} else {"".to_string()}), "") ) } + GuiCommand::SetOption { option } => { + match option { + Opt::UCILimitStrength { value } => write!(f, "setoption name UCI_LimitStrength value {value}"), + Opt::UCIElo { value } => write!(f,"setoption name UCI_Elo value {value}"), + } + } + GuiCommand::Go { wtime, wincr, btime, bincr } => { + let wtime = if let Some(wtime) = wtime {format!(" wtime {}", wtime.as_millis())} else {"".to_string()}; + let btime = if let Some(btime) = btime {format!(" btime {}", btime.as_millis())} else {"".to_string()}; + let wincr = if let Some(wincr) = wincr {format!(" wincr {}", wincr.as_millis())} else {"".to_string()}; + let bincr = if let Some(bincr) = bincr {format!(" bincr {}", bincr.as_millis())} else {"".to_string()}; + write!(f, "go{}{}{}{}", wtime, btime, wincr, bincr) + } + a => unimplemented!("{:?}", a), }