Manage engine go command
This commit is contained in:
parent
d68d7023e1
commit
b48f52c41c
|
@ -71,9 +71,9 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
/// Requires line by line input and output streams to communicate with uci engine
|
||||
pub fn new(source: Option<Fi>, destination: Option<Fo>) -> UciEngine<Fi, Fo> {
|
||||
UciEngine::<Fi, Fo>{
|
||||
source: if let Some(source) = source {Some(BufReader::new(source))} else {None},
|
||||
source: source.map(|x| BufReader::new(x)),
|
||||
destination,
|
||||
id: Id::new(),
|
||||
id: Id::default(),
|
||||
uciok: false,
|
||||
initial: Board::default(),
|
||||
game: Game::new(),
|
||||
|
@ -185,7 +185,12 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
Ok(EngineCommand::Id{id}) => self.update(&id),
|
||||
Ok(EngineCommand::UciOk) => self.uciok(),
|
||||
Ok(EngineCommand::Opt { options: _ }) => {},
|
||||
Ok(_) => {unimplemented!("command not implemented")},
|
||||
Ok(EngineCommand::Info { infos: _ }) |
|
||||
Ok(EngineCommand::BestMove { best_move: _, ponder: _ }) |
|
||||
Ok(EngineCommand::ReadyOk)
|
||||
=> {},
|
||||
Ok(EngineCommand::Registration)|
|
||||
Ok(EngineCommand::CopyProtection)=> {unimplemented!("command not implemented")},
|
||||
Err(_) => warn!("Not a command"),
|
||||
};
|
||||
uci_command
|
||||
|
@ -226,10 +231,10 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
}
|
||||
|
||||
/// Push a Uci Gui command to the engine
|
||||
fn push(&mut self, command: GuiCommand) {
|
||||
pub fn push(&mut self, command: GuiCommand) {
|
||||
if let Some(destination) = &mut self.destination {
|
||||
let command_str = command.to_string();
|
||||
match destination.write(&command_str.as_bytes()){
|
||||
match destination.write(command_str.as_bytes()){
|
||||
Ok(n) if n == command_str.len() => info!("→ gui: {command_str}"),
|
||||
Ok(n) => warn!("⚠ gui: {command_str} truncated at {n}"),
|
||||
Err(reason) => warn!("Unable to send command {command_str}: {reason}"),
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
use itertools::join;
|
||||
use std::time::Duration;
|
||||
use std::{time::Duration, str::FromStr};
|
||||
use std::fmt;
|
||||
|
||||
use chess::{ChessMove, Board, Action};
|
||||
|
||||
mod uci_command{}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Id {name: Option<String>, author: Option<String>}
|
||||
impl Id {
|
||||
pub fn new() -> Id {
|
||||
Id{name: None, author: None}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, id: &Id){
|
||||
if let Some(name) = &id.name {self.name = Some(name.clone());}
|
||||
if let Some(author) = &id.author {self.author = Some(author.clone());}
|
||||
|
@ -37,6 +31,25 @@ pub struct Info {}
|
|||
pub enum Opt{
|
||||
UCILimitStrength{value: bool},
|
||||
UCIElo{value: u32},
|
||||
SlowMover{value: u32},
|
||||
}
|
||||
impl Opt {
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Opt::UCILimitStrength { .. } => "UCI_LimitStrength",
|
||||
Opt::UCIElo { .. } => "UCI_Elo",
|
||||
Opt::SlowMover { .. } => "Slow Mover",
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Display for Opt {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result{
|
||||
match self {
|
||||
Opt::UCILimitStrength { value } => write!(f, "{value}"),
|
||||
Opt::UCIElo { value } => write!(f, "{value}"),
|
||||
Opt::SlowMover { value } => write!(f, "{value}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -63,24 +76,12 @@ pub enum GuiCommand {
|
|||
Position{position: Option<Board>, moves: Vec<Action>},
|
||||
Go{wtime: Option<Duration>, wincr: Option<Duration>,
|
||||
btime: Option<Duration>, bincr: Option<Duration>},
|
||||
SearchMoves{moves: Vec<ChessMove>},
|
||||
Ponder,
|
||||
WTime{time: Duration},
|
||||
BTime{time: Duration},
|
||||
WInc{increment: Duration},
|
||||
BInc{increment: Duration},
|
||||
MovesToGo{number: u16},
|
||||
Depth{number: u16},
|
||||
Nodes{number: u16},
|
||||
Mate{number: u16},
|
||||
MoveTime{time: Duration},
|
||||
Infinite,
|
||||
Stop,
|
||||
PonderHit,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub fn parse(message: &mut String) -> Result<EngineCommand,&'static str> {
|
||||
pub fn parse(message: &mut str) -> Result<EngineCommand,&'static str> {
|
||||
let mut message_iter = message.split_whitespace();
|
||||
|
||||
match message_iter.next() {
|
||||
|
@ -91,16 +92,25 @@ pub fn parse(message: &mut String) -> Result<EngineCommand,&'static str> {
|
|||
[] => Err("Empty id command"),
|
||||
["name"] => Err("Empty id name command"),
|
||||
["author"] => Err("Empty id author command"),
|
||||
["name", tail @ ..] => Ok(EngineCommand::Id{id: { let mut id = Id::new(); id.update_from_str(Some(&join(tail, " ")), None); id}}),
|
||||
["author", tail @ ..] => Ok(EngineCommand::Id{id: { let mut id = Id::new(); id.update_from_str(None, Some(&join(tail, " "))); id}}),
|
||||
["name", tail @ ..] => Ok(EngineCommand::Id{id: { let mut id = Id::default(); id.update_from_str(Some(&join(tail, " ")), None); id}}),
|
||||
["author", tail @ ..] => Ok(EngineCommand::Id{id: { let mut id = Id::default(); id.update_from_str(None, Some(&join(tail, " "))); id}}),
|
||||
_ => Err("Invalid id subcommand")
|
||||
},
|
||||
Some("uciok") => Ok(EngineCommand::UciOk),
|
||||
Some("readyok") => unimplemented!(),
|
||||
Some("bestmove") => unimplemented!(),
|
||||
Some("bestmove") =>
|
||||
match message_iter.collect::<Vec<&str>>().as_slice() {
|
||||
[] => Err("Empty bestmove command"),
|
||||
[chessmove] => Ok(EngineCommand::BestMove { best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"), ponder: None }),
|
||||
[_, "ponder"] => Err("Empty ponder in bestmove command"),
|
||||
[chessmove, "ponder", chess_ponder] => Ok(EngineCommand::BestMove {
|
||||
best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"),
|
||||
ponder: Some(ChessMove::from_str(chess_ponder).expect("chessmove ponder is invalid")) }),
|
||||
_ => Err("Invalid chessmove subcommand")
|
||||
},
|
||||
Some("copyprotection") => unimplemented!(),
|
||||
Some("registration") => unimplemented!(),
|
||||
Some("info") => unimplemented!(),
|
||||
Some("info") => Ok(EngineCommand::Info { infos: Vec::new() }), //todo!("parse info lines")
|
||||
Some("option") => Ok(EngineCommand::Opt { options: Vec::new() }), // todo!("Parse options lines")
|
||||
Some(_) => Err("Unknown command provided"),
|
||||
}
|
||||
|
@ -110,34 +120,35 @@ impl fmt::Display for GuiCommand {
|
|||
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result{
|
||||
match self {
|
||||
GuiCommand::Uci => write!(f, "uci\n"),
|
||||
GuiCommand::UciNewGame => write!(f, "ucinewgame\n"),
|
||||
GuiCommand::Uci => writeln!(f, "uci"),
|
||||
GuiCommand::UciNewGame => writeln!(f, "ucinewgame"),
|
||||
GuiCommand::Position { position, moves } => {
|
||||
write!(f, "position {} moves {}\n",
|
||||
writeln!(f, "position {} moves {}",
|
||||
match position { None => "startpos".to_string(),
|
||||
Some(board) if *board == Board::default() => "startpos".to_string(),
|
||||
Some(board) =>"fen ".to_string() + &board.to_string()},
|
||||
join(moves.into_iter().map(|x| if let Action::MakeMove(chessmove) = x
|
||||
{chessmove.to_string()} else {"".to_string()}), "")
|
||||
join(moves.iter().map(|x| if let Action::MakeMove(chessmove) = x
|
||||
{" ".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}"),
|
||||
}
|
||||
writeln!(f, "setoption name {} value {}", option.name(), option)
|
||||
}
|
||||
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)
|
||||
writeln!(f, "go{}{}{}{}", wtime, btime, wincr, bincr)
|
||||
}
|
||||
|
||||
a => unimplemented!("{:?}", a),
|
||||
GuiCommand::Stop => writeln!(f, "stop"),
|
||||
GuiCommand::PonderHit => writeln!(f, "ponderhit"),
|
||||
GuiCommand::Quit => writeln!(f, "quit"),
|
||||
|
||||
GuiCommand::Register|GuiCommand::Debug { .. }|GuiCommand::IsReady
|
||||
=> unimplemented!("{:?}", self),
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ extern crate chess_uci;
|
|||
|
||||
use chess::ChessMove;
|
||||
use chess_uci::*;
|
||||
use chess_uci::uci_command::EngineCommand;
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
use std::str::FromStr;
|
||||
|
@ -23,18 +24,37 @@ pub fn main(){
|
|||
|
||||
let (sf_in,sf_out) = (process.stdin.expect("Program stdin"), process.stdout.expect("Program stdout"));
|
||||
let mut uci = UciEngine::new(Some(sf_out), Some(sf_in));
|
||||
uci.game_option(GameOption::WhitePlayer { value: Player::Human { elo: None } });
|
||||
uci.game_option(GameOption::BlackPlayer { value: Player::Machine { elo: Some(1500) } });
|
||||
uci.init();
|
||||
|
||||
println!("Engine: {} \nby: {}",
|
||||
if let Some(name) = uci.name() {name} else {"Not defined".to_string()},
|
||||
if let Some(author) = uci.author() {author} else {"Not defined".to_string()});
|
||||
uci.push(uci_command::GuiCommand::SetOption { option: uci_command::Opt::SlowMover { value: 11 }});
|
||||
|
||||
uci.new_game();
|
||||
uci.make_move(ChessMove::from_str("e2e4").expect("error converting e2e4"));
|
||||
uci.push_raw("d\n");
|
||||
|
||||
uci.human_play(ChessMove::from_str("e2e4").expect("error converting e2e4")).expect("can not make human move");
|
||||
|
||||
uci.push_raw("d\n");
|
||||
|
||||
uci.go().expect("can not make engine move");
|
||||
|
||||
loop {
|
||||
match uci.pull() {
|
||||
Some(EngineCommand::BestMove { best_move, .. }) => {
|
||||
uci.make_move(best_move);
|
||||
break
|
||||
},
|
||||
Some(EngineCommand::Info { .. }) => continue,
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
uci.push_raw("d\n");
|
||||
|
||||
uci.push_raw("quit\n");
|
||||
while uci.pull_raw() != None {}
|
||||
|
||||
while uci.pull_raw().is_some() {}
|
||||
}
|
||||
|
|
|
@ -79,10 +79,10 @@ These are all the command the engine gets from the interface.
|
|||
to ping the engine to find out if it is still alive.
|
||||
E.g. this should be sent after setting the path to the tablebases as this can take some time.
|
||||
This command is also required once before the engine is asked to do any search
|
||||
to wait for the engine to finish initializing.
|
||||
$ to wait for the engine to finish initializing.
|
||||
This command must always be answered with "readyok" and can be sent also when the engine is calculating
|
||||
in which case the engine should also immediately answer with "readyok" without stopping the search.
|
||||
|
||||
$
|
||||
* setoption name <id> [value <x>]
|
||||
this is sent to the engine when the user wants to change the internal parameters
|
||||
of the engine. For the "button" type no value is needed.
|
||||
|
|
Loading…
Reference in New Issue