155 lines
6.0 KiB
Rust
155 lines
6.0 KiB
Rust
use itertools::join;
|
|
use std::{time::Duration, str::FromStr};
|
|
use std::fmt;
|
|
|
|
use chess::{ChessMove, Board, Action};
|
|
|
|
#[derive(Clone, Default)]
|
|
pub struct Id {name: Option<String>, author: Option<String>}
|
|
impl Id {
|
|
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());}
|
|
}
|
|
|
|
pub fn update_from_str(&mut self, name: Option<&str>, author: Option<&str>){
|
|
if let Some(name) = name {self.name = Some(name.to_string());}
|
|
if let Some(author) = author {self.author = Some(author.to_string());}
|
|
}
|
|
|
|
pub fn name(&self) -> Option<String> {
|
|
self.name.clone()
|
|
}
|
|
|
|
pub fn author(&self) -> Option<String> {
|
|
self.author.clone()
|
|
}
|
|
}
|
|
#[derive(Debug, Clone)]
|
|
pub struct Info {}
|
|
#[derive(Debug, Clone)]
|
|
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)]
|
|
pub enum EngineCommand {
|
|
Id{id: Id},
|
|
UciOk,
|
|
ReadyOk,
|
|
BestMove{best_move: ChessMove, ponder: Option<ChessMove>},
|
|
CopyProtection, // unimplemented
|
|
Registration, // unimplemented
|
|
Info{infos: Vec<Info>},
|
|
Opt{options: Vec<Opt>},
|
|
}
|
|
|
|
|
|
#[derive(Debug)]
|
|
pub enum GuiCommand {
|
|
Uci,
|
|
Debug{mode: bool},
|
|
IsReady,
|
|
SetOption{option: Opt},
|
|
Register, // unimplemented
|
|
UciNewGame,
|
|
Position{position: Option<Board>, moves: Vec<Action>},
|
|
Go{wtime: Option<Duration>, wincr: Option<Duration>,
|
|
btime: Option<Duration>, bincr: Option<Duration>},
|
|
Stop,
|
|
PonderHit,
|
|
Quit,
|
|
}
|
|
|
|
pub fn parse(message: &mut str) -> Result<EngineCommand,&'static str> {
|
|
let mut message_iter = message.split_whitespace();
|
|
|
|
match message_iter.next() {
|
|
None => Err("No command provided"),
|
|
|
|
Some("id") =>
|
|
match message_iter.collect::<Vec<&str>>().as_slice() {
|
|
[] => 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::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") =>
|
|
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") => 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"),
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for GuiCommand {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result{
|
|
match self {
|
|
GuiCommand::Uci => writeln!(f, "uci"),
|
|
GuiCommand::UciNewGame => writeln!(f, "ucinewgame"),
|
|
GuiCommand::Position { position, moves } => {
|
|
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.iter().map(|x| if let Action::MakeMove(chessmove) = x
|
|
{" ".to_string() + &chessmove.to_string()} else {"".to_string()}), "")
|
|
)
|
|
}
|
|
GuiCommand::SetOption { option } => {
|
|
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()};
|
|
writeln!(f, "go{}{}{}{}", wtime, btime, wincr, bincr)
|
|
}
|
|
|
|
GuiCommand::Stop => writeln!(f, "stop"),
|
|
GuiCommand::PonderHit => writeln!(f, "ponderhit"),
|
|
GuiCommand::Quit => writeln!(f, "quit"),
|
|
|
|
GuiCommand::Register|GuiCommand::Debug { .. }|GuiCommand::IsReady
|
|
=> unimplemented!("{:?}", self),
|
|
}
|
|
|
|
}
|
|
}
|