From 74cd37666ecf3e6e3e39e6f97908dfe3e1967a2e Mon Sep 17 00:00:00 2001 From: Baptiste Fouques Date: Wed, 11 Jan 2023 12:07:28 +0100 Subject: [PATCH] advance on execution --- Cargo.toml | 3 +- src/lib.rs | 195 +++++++++++++++++---------------------------- src/uci_command.rs | 97 ++++++++++++++++++++++ 3 files changed, 173 insertions(+), 122 deletions(-) create mode 100644 src/uci_command.rs diff --git a/Cargo.toml b/Cargo.toml index 6b001d3..cd6c313 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "chess-uci" +name = "chess_uci" version = "0.1.0" edition = "2021" @@ -8,3 +8,4 @@ edition = "2021" [dependencies] chess = "3.2.0" itertools = "0.10.5" +log = "0.4.17" diff --git a/src/lib.rs b/src/lib.rs index 7c1f327..aa84480 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,80 +1,51 @@ #![allow(dead_code)] /// Manage UCI messages and link into chess component -pub mod uci_engine { - use itertools::join; +mod uci_command; - mod chess_move { - pub struct Move {} - } - mod chess_info{ - pub struct Info {} - } - mod chess_option{ - pub struct Opt{} +use log::warn; +use std::io::*; +use crate::uci_command::*; + +pub struct UciEngine { + source: Fi, + destination: Fo, + uciok: bool, + id: Id, +} + +impl UciEngine { + pub fn new(source: Fi, destination: Fo) -> UciEngine { + UciEngine::{ + source, destination, + id: Id::new(), + uciok: false} } - pub struct UciEngine { - name: String, - author: String, + pub fn name(&self) -> Option { + self.id.name() } - impl UciEngine { - pub fn new(name: &str, author: &str) -> UciEngine { - UciEngine{name: name.to_string(), author: author.to_string()} - } - - pub fn name(&self) -> String { - self.name.clone() - } - - pub fn author(&self) -> String { - self.author.clone() - } - - pub fn update(&mut self, id: &Id){ - match &id.name { - Some(name) => self.name = name.clone(), - None => {} - } - - match &id.author { - Some(author) => self.author = author.clone(), - None => {} - } - } + pub fn author(&self) -> Option { + self.id.author() } - pub struct Id {name: Option, author: Option} - impl Id { - pub fn new(name: Option<&str>, author: Option<&str>) -> Id { - Id{ - name: if let Some(name) = name {Some(name.to_string())} else {None}, - author: if let Some(author) = author {Some(author.to_string())} else {None} - } - } - pub fn update(&mut self, name: Option<&str>, author: Option<&str>){ - match &name { - Some(name) => self.name = Some(name.to_string()), - None => {} - } - - match &author { - Some(author) => self.author = Some(author.to_string()), - None => {} - } - } + pub fn update(&mut self, id: &Id){ + if self.is_uciok() {warn!("Engine info should not be updated now (uciok)");} + self.id.update(id); + } + + pub fn update_from_str(&mut self, name: Option<&str>, author: Option<&str>){ + if self.is_uciok() {warn!("Engine info should not be updated now (uciok)");} + self.id.update_from_str(name, author); } - pub enum UciCommand { - Id{id: Id}, - UciOk, - ReadyOk, - BestMove{best_move: chess_move::Move, ponder: chess_move::Move}, - CopyProtection, // unimplemented - Registration, // unimplemented - Info{infos: Vec}, - Opt{options: Vec}, + pub fn uciok(&mut self){ + self.uciok = true; + } + + pub fn is_uciok(&self) -> bool { + self.uciok } /// Parse UCI commands @@ -90,68 +61,50 @@ pub mod uci_engine { /// e.g. "id author Stefan MK\n" /// /// ```rust - /// use chess_uci::uci_engine::{parse, UciCommand, Id, UciEngine}; - /// let mut engine = UciEngine::new("Test", "Bat"); + /// use chess_uci::*; + /// use std::io; + /// let mut engine = UciEngine::new(io::empty(), io::sink()); /// - /// engine.exec(&parse(&mut "id name Baptiste".to_string()).expect("simple id name command")); - /// assert_eq!(engine.name(), "Baptiste"); + /// engine.exec("id name Baptiste"); + /// assert_eq!(engine.name().expect(""), "Baptiste"); /// - /// engine.exec(&parse(&mut "id author Monique Jouve".to_string()).expect("simple id author command")); - /// assert_eq!(engine.author(), "Monique Jouve"); + /// engine.exec("id author Monique Jouve"); + /// assert_eq!(engine.author().expect(""), "Monique Jouve"); /// /// ``` - pub fn parse(message: &mut String) -> Result { - let mut message_iter = message.split_whitespace(); - - match message_iter.next() { - None => Err("No command provided"), - - Some("id") => - match message_iter.collect::>().as_slice() { - [] => Err("Empty id command"), - ["name"] => Err("Empty id name command"), - ["author"] => Err("Empty id author command"), - ["name", tail @ ..] => Ok(UciCommand::Id{id: Id::new(Some(&join(tail, " ")), None)}), - ["author", tail @ ..] => Ok(UciCommand::Id{id: Id::new(None, Some(&join(tail, " ")))}), - _ => Err("Invalid id subcommand") - }, - Some("uciok") => unimplemented!(), - Some("readyok") => unimplemented!(), - Some("bestmove") => unimplemented!(), - Some("copyprotection") => unimplemented!(), - Some("registration") => unimplemented!(), - Some("info") => unimplemented!(), - Some("option") => unimplemented!(), - Some(_) => Err("Unknown command provided"), + /// + /// * uciok + /// + /// Must be sent after the id and optional options to tell the GUI that the engine + /// has sent all infos and is ready in uci mode. + /// + /// ```rust + /// use chess_uci::*; + /// use std::io; + /// let mut engine = UciEngine::new(io::empty(), io::sink()); + /// assert_eq!(engine.is_uciok(), false); + /// + /// engine.exec("id name Baptiste"); + /// assert_eq!(engine.name().expect(""), "Baptiste"); + /// assert_eq!(engine.is_uciok(), false); + /// + /// engine.exec("id author Monique Jouve"); + /// assert_eq!(engine.author().expect(""), "Monique Jouve"); + /// assert_eq!(engine.is_uciok(), false); + /// + /// engine.exec(" uciok"); + /// assert_eq!(engine.is_uciok(), true); + /// ``` + pub fn exec(&mut self, command: &str){ + match parse (&mut command.to_string()) { + Ok(EngineCommand::Id{id}) => self.update(&id), + Ok(EngineCommand::UciOk) => self.uciok(), + Ok(_) => {unimplemented!("command not implemented")}, + Err(_) => warn!("Not a command"), } } - impl UciEngine { - pub fn exec(&mut self, command: &UciCommand){ - match command { - UciCommand::Id{id} => self.update(&id), - _ => unimplemented!(), - } - } - } -} - -#[cfg(test)] -mod tests { - use super::uci_engine::*; - - #[test] - fn new_uci() { - let mut engine = UciEngine::new("Test", "Bat"); - assert_eq!(engine.name(), "Test"); - assert_eq!(engine.author(), "Bat"); - let new_id = Id::new(Some("Test 1"), None); - engine.update(&new_id); - assert_eq!(engine.name(), "Test 1"); - assert_eq!(engine.author(), "Bat"); - let new_id = Id::new(Some("Test 2"), Some("Bat 1")); - engine.update(&new_id); - assert_eq!(engine.name(), "Test 2"); - assert_eq!(engine.author(), "Bat 1"); + pub fn next(&mut self) { + exec(self.source.read_line()); } } diff --git a/src/uci_command.rs b/src/uci_command.rs new file mode 100644 index 0000000..a2a84aa --- /dev/null +++ b/src/uci_command.rs @@ -0,0 +1,97 @@ +use itertools::join; +use std::time::Duration; + +mod uci_command{} + +pub struct Id {name: Option, author: Option} +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());} + } + + 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 { + self.name.clone() + } + + pub fn author(&self) -> Option { + self.author.clone() + } +} +pub struct Move {} +pub struct Info {} +pub struct Opt{} +pub struct Position{} + +pub enum EngineCommand { + Id{id: Id}, + UciOk, + ReadyOk, + BestMove{best_move: Move, ponder: Move}, + CopyProtection, // unimplemented + Registration, // unimplemented + Info{infos: Vec}, + Opt{options: Vec}, +} + +pub enum GuiCommand { + Uci, + Debug{mode: bool}, + IsReady, + SetOption{option: Opt}, + Register, // unimplemented + UciNewGame, + Position{position: Option, moves: Vec}, + Go, + SearchMoves{moves: Vec}, + 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 { + let mut message_iter = message.split_whitespace(); + + match message_iter.next() { + None => Err("No command provided"), + + Some("id") => + match message_iter.collect::>().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::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}}), + _ => Err("Invalid id subcommand") + }, + Some("uciok") => Ok(EngineCommand::UciOk), + Some("readyok") => unimplemented!(), + Some("bestmove") => unimplemented!(), + Some("copyprotection") => unimplemented!(), + Some("registration") => unimplemented!(), + Some("info") => unimplemented!(), + Some("option") => unimplemented!(), + Some(_) => Err("Unknown command provided"), + } +} +