WIP
This commit is contained in:
parent
c7b9544004
commit
90ee3e416f
7 changed files with 344 additions and 2 deletions
148
chessfriend/src/uci.rs
Normal file
148
chessfriend/src/uci.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Eryn Wells <eryn@erynwells.me>
|
||||
|
||||
use clap::{Error as ClapError, Parser, Subcommand, ValueEnum};
|
||||
use std::{
|
||||
fmt::Display,
|
||||
io::{BufRead, Write},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(multicall = true)]
|
||||
pub struct Uci {
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Command {
|
||||
/// Establish UCI (Universal Chess Interface) as the channel's exchange protocol.
|
||||
Uci,
|
||||
|
||||
/// Toggle debug state on or off.
|
||||
Debug { state: DebugState },
|
||||
|
||||
/// Synchronize the engine with the client. Can also be used as a 'ping'.
|
||||
IsReady,
|
||||
|
||||
/// Stop calculating as soon as possible.
|
||||
Stop,
|
||||
|
||||
/// Stop all processing and quit the program.
|
||||
Quit,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
|
||||
enum DebugState {
|
||||
On,
|
||||
Off,
|
||||
}
|
||||
|
||||
pub enum Response<'a> {
|
||||
/// Declares one aspect of the engine's identity.
|
||||
Id(IdValue<'a>),
|
||||
|
||||
/// Declares that communicating in UCI is acceptable.
|
||||
UciOk,
|
||||
|
||||
/// Declares that the engine is ready to receive commands from the client.
|
||||
ReadyOk,
|
||||
}
|
||||
|
||||
impl Display for Response<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Response::Id(value) => write!(f, "id {value}"),
|
||||
Response::UciOk => write!(f, "uciok"),
|
||||
Response::ReadyOk => write!(f, "readyok"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum IdValue<'a> {
|
||||
Name(&'a str),
|
||||
Author(&'a str),
|
||||
}
|
||||
|
||||
impl Display for IdValue<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
IdValue::Name(name) => write!(f, "name {name}"),
|
||||
IdValue::Author(author) => write!(f, "author {author}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("unable to parse command")]
|
||||
LexError,
|
||||
|
||||
#[error("{0}")]
|
||||
ClapError(#[from] ClapError),
|
||||
}
|
||||
|
||||
impl Uci {
|
||||
/// Respond to a command.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// Returns an error if parsing the command string fails, otherwise returns an array of
|
||||
/// responses.
|
||||
///
|
||||
pub fn respond(line: &str) -> Result<Vec<Response>, Error> {
|
||||
let arguments = shlex::split(line).ok_or(Error::LexError)?;
|
||||
|
||||
let interface = Self::try_parse_from(arguments)?;
|
||||
|
||||
match interface.command {
|
||||
Command::Uci => {
|
||||
const IDENTITIES: [Response; 2] = [
|
||||
Response::Id(IdValue::Name("ChessFriend")),
|
||||
Response::Id(IdValue::Author("Eryn Wells")),
|
||||
];
|
||||
|
||||
let options: Vec<Response> = vec![];
|
||||
|
||||
Ok(IDENTITIES.into_iter().chain(options).collect())
|
||||
}
|
||||
Command::Debug { state: _ } => Ok(vec![]),
|
||||
Command::IsReady => Ok(vec![Response::ReadyOk]),
|
||||
Command::Stop => Ok(vec![]),
|
||||
Command::Quit => Ok(vec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UciInterface {}
|
||||
|
||||
impl UciInterface {
|
||||
pub fn read_until_quit(
|
||||
&self,
|
||||
input: impl BufRead,
|
||||
output: &mut impl Write,
|
||||
) -> Result<(), UciInterfaceError> {
|
||||
for line in input.lines() {
|
||||
let line = line?;
|
||||
|
||||
let responses = Uci::respond(line.as_str())?;
|
||||
|
||||
// TODO: Dispatch command to background processing thread.
|
||||
|
||||
for r in responses {
|
||||
write!(output, "{r}")?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum UciInterfaceError {
|
||||
#[error("io error: {0}")]
|
||||
IoError(#[from] std::io::Error),
|
||||
|
||||
#[error("uci error: {0}")]
|
||||
UciError(#[from] Error),
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue