implement go

This commit is contained in:
Baptiste Fouques 2023-01-27 14:59:13 +01:00
parent a46ff43db4
commit d68d7023e1
2 changed files with 90 additions and 28 deletions

View File

@ -6,10 +6,13 @@ pub mod uci_command;
use log::{info, warn}; use log::{info, warn};
use std::io::*; use std::io::*;
use std::time::Duration; use std::time::Duration;
use std::result::Result;
use crate::uci_command::*; use crate::uci_command::*;
use chess::{Game, Board, ChessMove}; use chess::{Game, Board, ChessMove};
const DEFAULT_TIME: (Option<Duration>, Option<Duration>) = (Some(Duration::from_secs(120 * 60)), None);
/// Structure used to manage a chess game with a uci engine. /// 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 /// It needs a in / out communication channel to read / push line by line uci commands
@ -40,9 +43,9 @@ pub struct UciEngine<Fi: Read, Fo: Write> {
initial: Board, initial: Board,
game: Game, game: Game,
/// white (total, incr), black (total, incr) /// white (total, incr), black (total, incr)
time: ((Option<Duration>, Option<Duration>), (Option<Duration>, Option<Duration>)), time: [(Option<Duration>, Option<Duration>); 2],
/// white, black /// white, black
player: (Player, Player) player: [Player; 2],
} }
pub enum UciOption { pub enum UciOption {
@ -74,26 +77,26 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
uciok: false, uciok: false,
initial: Board::default(), initial: Board::default(),
game: Game::new(), game: Game::new(),
time: ((None, None), (None, None)), time: [DEFAULT_TIME; 2],
player: (Player::Human{elo: None}, Player::Human{elo: None}), player: [Player::Human{elo: None}; 2],
} }
} }
pub fn game_option(&mut self, option: GameOption) -> GameOption { pub fn game_option(&mut self, option: GameOption) -> GameOption {
let old_value: GameOption; let old_value: GameOption;
match option { match option {
GameOption::WhiteTotalTime{value} => {old_value = GameOption::WhiteTotalTime { value: self.time.0.0 }; GameOption::WhiteTotalTime{value} => {old_value = GameOption::WhiteTotalTime { value: self.time[Color::White.to_index()].0 };
self.time.0.0 = value}, self.time[Color::White.to_index()].0 = value},
GameOption::BlackTotalTime{value} => {old_value = GameOption::BlackTotalTime { value: self.time.1.0 }; GameOption::BlackTotalTime{value} => {old_value = GameOption::BlackTotalTime { value: self.time[Color::Black.to_index()].0 };
self.time.1.0 = value}, self.time[Color::Black.to_index()].0 = value},
GameOption::WhiteIncrement{value} => {old_value = GameOption::WhiteIncrement { value: self.time.0.1 }; GameOption::WhiteIncrement{value} => {old_value = GameOption::WhiteIncrement { value: self.time[Color::White.to_index()].1 };
self.time.0.1 = value}, self.time[Color::White.to_index()].1 = value},
GameOption::BlackIncrement{value} => {old_value = GameOption::BlackIncrement { value: self.time.1.1 }; GameOption::BlackIncrement{value} => {old_value = GameOption::BlackIncrement { value: self.time[Color::Black.to_index()].1 };
self.time.1.1 = value}, self.time[Color::Black.to_index()].1 = value},
GameOption::WhitePlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player.0 }; GameOption::WhitePlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player[Color::White.to_index()] };
self.player.0 = value}, self.player[Color::White.to_index()] = value},
GameOption::BlackPlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player.1 }; GameOption::BlackPlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player[Color::Black.to_index()] };
self.player.1 = value}, self.player[Color::Black.to_index()] = value},
} }
old_value old_value
@ -172,20 +175,11 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
self.id.update_from_str(name, author); 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 /// Execute a uci command
/// ///
/// if `EngineCommand::Id`: update name or authorship of the engine /// if `EngineCommand::Id`: update name or authorship of the engine
/// if `EngineCommand::UciOk`: end engine initialization phase /// if `EngineCommand::UciOk`: end engine initialization phase
pub fn exec(&mut self, command: &str) -> std::result::Result<EngineCommand, &'static str> { pub fn exec(&mut self, command: &str) -> Result<EngineCommand, &'static str> {
let uci_command = parse (&mut command.to_string()); let uci_command = parse (&mut command.to_string());
match uci_command.clone() { match uci_command.clone() {
Ok(EngineCommand::Id{id}) => self.update(&id), Ok(EngineCommand::Id{id}) => self.update(&id),
@ -259,6 +253,52 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
} }
} }
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
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<ChessMove, &'static str> {
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}; use chess::{Square, Piece, Color};
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> { impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
pub fn piece_on(&self, square: Square) -> Option<Piece> { pub fn piece_on(&self, square: Square) -> Option<Piece> {
@ -271,5 +311,9 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
pub fn current_position(&self) -> Board { pub fn current_position(&self) -> Board {
self.game.current_position() self.game.current_position()
} }
pub fn side_to_move(&self) -> Color {
self.game.side_to_move()
}
} }
// LocalWords: uci // LocalWords: uci

View File

@ -34,7 +34,10 @@ impl Id {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Info {} pub struct Info {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Opt{} pub enum Opt{
UCILimitStrength{value: bool},
UCIElo{value: u32},
}
#[derive(Clone)] #[derive(Clone)]
pub enum EngineCommand { pub enum EngineCommand {
@ -58,7 +61,8 @@ pub enum GuiCommand {
Register, // unimplemented Register, // unimplemented
UciNewGame, UciNewGame,
Position{position: Option<Board>, moves: Vec<Action>}, Position{position: Option<Board>, moves: Vec<Action>},
Go, Go{wtime: Option<Duration>, wincr: Option<Duration>,
btime: Option<Duration>, bincr: Option<Duration>},
SearchMoves{moves: Vec<ChessMove>}, SearchMoves{moves: Vec<ChessMove>},
Ponder, Ponder,
WTime{time: Duration}, WTime{time: Duration},
@ -117,6 +121,20 @@ impl fmt::Display for GuiCommand {
{chessmove.to_string()} else {"".to_string()}), "") {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), a => unimplemented!("{:?}", a),
} }