236 lines
7.4 KiB
Rust
236 lines
7.4 KiB
Rust
use itertools::join;
|
|
use std::fmt;
|
|
use std::{str::FromStr, time::Duration};
|
|
|
|
use chess::{Action, Board, ChessMove};
|
|
|
|
#[derive(Clone, Default, Debug)]
|
|
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, Debug)]
|
|
pub enum EngineCommand {
|
|
Id {
|
|
id: Id,
|
|
},
|
|
UciOk,
|
|
ReadyOk,
|
|
BestMove {
|
|
best_move: Option<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"),
|
|
["(none)"] => Ok(EngineCommand::BestMove { best_move: None, ponder: None }),
|
|
[chessmove] => Ok(EngineCommand::BestMove {
|
|
best_move: Some(
|
|
ChessMove::from_str(chessmove).expect(&format!("chessmove {:?} is invalid", chessmove))),
|
|
ponder: None,
|
|
}),
|
|
[_, "ponder"] => Err("Empty ponder in bestmove command"),
|
|
[chessmove, "ponder", chess_ponder] => Ok(EngineCommand::BestMove {
|
|
best_move: Some(ChessMove::from_str(chessmove).expect(&format!("chessmove {:?} is invalid", chessmove))),
|
|
ponder: Some(
|
|
ChessMove::from_str(chess_ponder).expect(&format!("ponder {:?} is invalid", chessmove))),
|
|
}),
|
|
_ => 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)
|
|
}
|
|
}
|
|
}
|
|
}
|