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
|
/// 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> {
|
pub fn new(source: Option<Fi>, destination: Option<Fo>) -> UciEngine<Fi, 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,
|
destination,
|
||||||
id: Id::new(),
|
id: Id::default(),
|
||||||
uciok: false,
|
uciok: false,
|
||||||
initial: Board::default(),
|
initial: Board::default(),
|
||||||
game: Game::new(),
|
game: Game::new(),
|
||||||
|
@ -185,7 +185,12 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
||||||
Ok(EngineCommand::Id{id}) => self.update(&id),
|
Ok(EngineCommand::Id{id}) => self.update(&id),
|
||||||
Ok(EngineCommand::UciOk) => self.uciok(),
|
Ok(EngineCommand::UciOk) => self.uciok(),
|
||||||
Ok(EngineCommand::Opt { options: _ }) => {},
|
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"),
|
Err(_) => warn!("Not a command"),
|
||||||
};
|
};
|
||||||
uci_command
|
uci_command
|
||||||
|
@ -226,10 +231,10 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a Uci Gui command to the engine
|
/// 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 {
|
if let Some(destination) = &mut self.destination {
|
||||||
let command_str = command.to_string();
|
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) if n == command_str.len() => info!("→ gui: {command_str}"),
|
||||||
Ok(n) => warn!("⚠ gui: {command_str} truncated at {n}"),
|
Ok(n) => warn!("⚠ gui: {command_str} truncated at {n}"),
|
||||||
Err(reason) => warn!("Unable to send command {command_str}: {reason}"),
|
Err(reason) => warn!("Unable to send command {command_str}: {reason}"),
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
use itertools::join;
|
use itertools::join;
|
||||||
use std::time::Duration;
|
use std::{time::Duration, str::FromStr};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use chess::{ChessMove, Board, Action};
|
use chess::{ChessMove, Board, Action};
|
||||||
|
|
||||||
mod uci_command{}
|
#[derive(Clone, Default)]
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Id {name: Option<String>, author: Option<String>}
|
pub struct Id {name: Option<String>, author: Option<String>}
|
||||||
impl Id {
|
impl Id {
|
||||||
pub fn new() -> Id {
|
|
||||||
Id{name: None, author: None}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, id: &Id){
|
pub fn update(&mut self, id: &Id){
|
||||||
if let Some(name) = &id.name {self.name = Some(name.clone());}
|
if let Some(name) = &id.name {self.name = Some(name.clone());}
|
||||||
if let Some(author) = &id.author {self.author = Some(author.clone());}
|
if let Some(author) = &id.author {self.author = Some(author.clone());}
|
||||||
|
@ -37,6 +31,25 @@ pub struct Info {}
|
||||||
pub enum Opt{
|
pub enum Opt{
|
||||||
UCILimitStrength{value: bool},
|
UCILimitStrength{value: bool},
|
||||||
UCIElo{value: u32},
|
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)]
|
#[derive(Clone)]
|
||||||
|
@ -63,24 +76,12 @@ pub enum GuiCommand {
|
||||||
Position{position: Option<Board>, moves: Vec<Action>},
|
Position{position: Option<Board>, moves: Vec<Action>},
|
||||||
Go{wtime: Option<Duration>, wincr: Option<Duration>,
|
Go{wtime: Option<Duration>, wincr: Option<Duration>,
|
||||||
btime: Option<Duration>, bincr: 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,
|
Stop,
|
||||||
PonderHit,
|
PonderHit,
|
||||||
Quit,
|
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();
|
let mut message_iter = message.split_whitespace();
|
||||||
|
|
||||||
match message_iter.next() {
|
match message_iter.next() {
|
||||||
|
@ -91,16 +92,25 @@ pub fn parse(message: &mut String) -> Result<EngineCommand,&'static str> {
|
||||||
[] => Err("Empty id command"),
|
[] => Err("Empty id command"),
|
||||||
["name"] => Err("Empty id name command"),
|
["name"] => Err("Empty id name command"),
|
||||||
["author"] => Err("Empty id author 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}}),
|
["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::new(); id.update_from_str(None, Some(&join(tail, " "))); 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")
|
_ => Err("Invalid id subcommand")
|
||||||
},
|
},
|
||||||
Some("uciok") => Ok(EngineCommand::UciOk),
|
Some("uciok") => Ok(EngineCommand::UciOk),
|
||||||
Some("readyok") => unimplemented!(),
|
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("copyprotection") => unimplemented!(),
|
||||||
Some("registration") => 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("option") => Ok(EngineCommand::Opt { options: Vec::new() }), // todo!("Parse options lines")
|
||||||
Some(_) => Err("Unknown command provided"),
|
Some(_) => Err("Unknown command provided"),
|
||||||
}
|
}
|
||||||
|
@ -110,34 +120,35 @@ impl fmt::Display for GuiCommand {
|
||||||
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result{
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result{
|
||||||
match self {
|
match self {
|
||||||
GuiCommand::Uci => write!(f, "uci\n"),
|
GuiCommand::Uci => writeln!(f, "uci"),
|
||||||
GuiCommand::UciNewGame => write!(f, "ucinewgame\n"),
|
GuiCommand::UciNewGame => writeln!(f, "ucinewgame"),
|
||||||
GuiCommand::Position { position, moves } => {
|
GuiCommand::Position { position, moves } => {
|
||||||
write!(f, "position {} moves {}\n",
|
writeln!(f, "position {} moves {}",
|
||||||
match position { None => "startpos".to_string(),
|
match position { None => "startpos".to_string(),
|
||||||
Some(board) if *board == Board::default() => "startpos".to_string(),
|
Some(board) if *board == Board::default() => "startpos".to_string(),
|
||||||
Some(board) =>"fen ".to_string() + &board.to_string()},
|
Some(board) =>"fen ".to_string() + &board.to_string()},
|
||||||
join(moves.into_iter().map(|x| if let Action::MakeMove(chessmove) = x
|
join(moves.iter().map(|x| if let Action::MakeMove(chessmove) = x
|
||||||
{chessmove.to_string()} else {"".to_string()}), "")
|
{" ".to_string() + &chessmove.to_string()} else {"".to_string()}), "")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
GuiCommand::SetOption { option } => {
|
GuiCommand::SetOption { option } => {
|
||||||
match option {
|
writeln!(f, "setoption name {} value {}", option.name(), 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 } => {
|
GuiCommand::Go { wtime, wincr, btime, bincr } => {
|
||||||
let wtime = if let Some(wtime) = wtime {format!(" wtime {}", wtime.as_millis())} else {"".to_string()};
|
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 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 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()};
|
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::ChessMove;
|
||||||
use chess_uci::*;
|
use chess_uci::*;
|
||||||
|
use chess_uci::uci_command::EngineCommand;
|
||||||
|
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
use std::str::FromStr;
|
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 (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));
|
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();
|
uci.init();
|
||||||
|
|
||||||
println!("Engine: {} \nby: {}",
|
println!("Engine: {} \nby: {}",
|
||||||
if let Some(name) = uci.name() {name} else {"Not defined".to_string()},
|
if let Some(name) = uci.name() {name} else {"Not defined".to_string()},
|
||||||
if let Some(author) = uci.author() {author} 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.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("d\n");
|
||||||
|
|
||||||
uci.push_raw("quit\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.
|
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.
|
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
|
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
|
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.
|
in which case the engine should also immediately answer with "readyok" without stopping the search.
|
||||||
|
$
|
||||||
* setoption name <id> [value <x>]
|
* setoption name <id> [value <x>]
|
||||||
this is sent to the engine when the user wants to change the internal parameters
|
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.
|
of the engine. For the "button" type no value is needed.
|
||||||
|
|
Loading…
Reference in New Issue