Create executable over stockfish

This commit is contained in:
Baptiste Fouques 2023-01-11 17:18:15 +01:00
parent 74cd37666e
commit c125ce3dfc
4 changed files with 99 additions and 19 deletions

View File

@ -3,9 +3,18 @@ name = "chess_uci"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
[lib]
path="src/lib.rs"
[[bin]]
name="chess"
path="src/main.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
chess = "3.2.0" chess = "3.2.0"
env_logger = "0.10.0"
itertools = "0.10.5" itertools = "0.10.5"
log = "0.4.17" log = "0.4.17"

View File

@ -3,25 +3,43 @@
/// Manage UCI messages and link into chess component /// Manage UCI messages and link into chess component
mod uci_command; mod uci_command;
use log::warn; use log::{info, warn};
use std::io::*; use std::io::*;
use crate::uci_command::*; use crate::uci_command::*;
pub struct UciEngine<Fi: Read, Fo: Write> { pub struct UciEngine<Fi: BufRead, Fo: Write> {
source: Fi, source: Fi,
destination: Fo, destination: Fo,
uciok: bool, uciok: bool,
id: Id, id: Id,
} }
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> { impl<Fi: BufRead, Fo: Write> UciEngine<Fi, Fo> {
pub fn new(source: Fi, destination: Fo) -> UciEngine<Fi, Fo> { pub fn new(source: Fi, destination: Fo) -> UciEngine<Fi, Fo> {
UciEngine::<Fi, Fo>{ UciEngine::<Fi, Fo>{
source, destination, source, destination,
id: Id::new(), id: Id::new(),
uciok: false} uciok: false}
} }
pub fn init(&mut self){
self.push(GuiCommand::Uci);
// Consume commands until uciok messages
while !self.uciok {
self.pull();
}
}
pub fn new_game(&mut self) {
unimplemented!();
}
pub fn terminate(&mut self, reason: &str) {
self.uciok = false;
info!("UCI termination: {}", reason);
}
pub fn name(&self) -> Option<String> { pub fn name(&self) -> Option<String> {
self.id.name() self.id.name()
} }
@ -34,7 +52,7 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
if self.is_uciok() {warn!("Engine info should not be updated now (uciok)");} if self.is_uciok() {warn!("Engine info should not be updated now (uciok)");}
self.id.update(id); self.id.update(id);
} }
pub fn update_from_str(&mut self, name: Option<&str>, author: Option<&str>){ 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)");} if self.is_uciok() {warn!("Engine info should not be updated now (uciok)");}
self.id.update_from_str(name, author); self.id.update_from_str(name, author);
@ -84,12 +102,6 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
/// let mut engine = UciEngine::new(io::empty(), io::sink()); /// let mut engine = UciEngine::new(io::empty(), io::sink());
/// assert_eq!(engine.is_uciok(), false); /// 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); /// assert_eq!(engine.is_uciok(), false);
/// ///
/// engine.exec(" uciok"); /// engine.exec(" uciok");
@ -99,12 +111,30 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
match parse (&mut command.to_string()) { match parse (&mut command.to_string()) {
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(_) => {unimplemented!("command not implemented")}, Ok(_) => {unimplemented!("command not implemented")},
Err(_) => warn!("Not a command"), Err(_) => warn!("Not a command"),
} }
} }
pub fn next(&mut self) { pub fn pull(&mut self) {
exec(self.source.read_line()); let mut command = String::new();
match self.source.read_line(&mut command) {
Ok(0) => self.terminate("Chess engine closed connection."),
Ok(_) => {
info!("← {}", command);
self.exec(&command)
},
Err(reason) => warn!("Unable to read from engine: {reason}"),
}
}
pub fn push(&mut self, command: GuiCommand) {
let command_str = command.to_string();
match self.destination.write(&command_str.as_bytes()){
Ok(n) if n == command_str.len() => info!("☒ gui: {command_str} "),
Ok(n) => warn!("⚠ gui: {command_str} truncated at {n}"),
Err(reason) => warn!("Unable to send command {command_str}: {reason}"),
}
} }
} }

29
src/main.rs Normal file
View File

@ -0,0 +1,29 @@
extern crate chess_uci;
use chess_uci::*;
use std::process::{Command, Stdio};
use std::io::BufReader;
pub fn main(){
env_logger::init();
println!("Launching hardcoded stockfish program (should be in PATH)");
// launch chess program
let process = match Command::new("stockfish")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn() {
Err(why) => panic!("couldn't spawn stockfish: {}", why),
Ok(process) => process,
};
let (sf_in,sf_out) = (process.stdin.expect("Program stdin"), process.stdout.expect("Program stdout"));
let mut uci = UciEngine::new(BufReader::new(sf_out), sf_in);
uci.init();
println!("Engine: {} \nby: {}",
if let Some(name) = uci.name() {name} else {"Not defined".to_string()},
if let Some(author) = uci.author() {author} else {"Not defined".to_string()});
}

View File

@ -1,5 +1,8 @@
use itertools::join; use itertools::join;
use std::time::Duration; use std::time::Duration;
use std::fmt;
use chess::ChessMove;
mod uci_command{} mod uci_command{}
@ -18,7 +21,7 @@ impl Id {
if let Some(name) = name {self.name = Some(name.to_string());} if let Some(name) = name {self.name = Some(name.to_string());}
if let Some(author) = author {self.author = Some(author.to_string());} if let Some(author) = author {self.author = Some(author.to_string());}
} }
pub fn name(&self) -> Option<String> { pub fn name(&self) -> Option<String> {
self.name.clone() self.name.clone()
} }
@ -27,7 +30,6 @@ impl Id {
self.author.clone() self.author.clone()
} }
} }
pub struct Move {}
pub struct Info {} pub struct Info {}
pub struct Opt{} pub struct Opt{}
pub struct Position{} pub struct Position{}
@ -36,7 +38,7 @@ pub enum EngineCommand {
Id{id: Id}, Id{id: Id},
UciOk, UciOk,
ReadyOk, ReadyOk,
BestMove{best_move: Move, ponder: Move}, BestMove{best_move: ChessMove, ponder: Option<ChessMove>},
CopyProtection, // unimplemented CopyProtection, // unimplemented
Registration, // unimplemented Registration, // unimplemented
Info{infos: Vec<Info>}, Info{infos: Vec<Info>},
@ -50,9 +52,9 @@ pub enum GuiCommand {
SetOption{option: Opt}, SetOption{option: Opt},
Register, // unimplemented Register, // unimplemented
UciNewGame, UciNewGame,
Position{position: Option<Position>, moves: Vec<Move>}, Position{position: Option<Position>, moves: Vec<ChessMove>},
Go, Go,
SearchMoves{moves: Vec<Move>}, SearchMoves{moves: Vec<ChessMove>},
Ponder, Ponder,
WTime{time: Duration}, WTime{time: Duration},
BTime{time: Duration}, BTime{time: Duration},
@ -90,8 +92,18 @@ pub fn parse(message: &mut String) -> Result<EngineCommand,&'static str> {
Some("copyprotection") => unimplemented!(), Some("copyprotection") => unimplemented!(),
Some("registration") => unimplemented!(), Some("registration") => unimplemented!(),
Some("info") => unimplemented!(), Some("info") => unimplemented!(),
Some("option") => unimplemented!(), Some("option") => Ok(EngineCommand::Opt { options: Vec::new() }), // todo!("Parse options lines")
Some(_) => Err("Unknown command provided"), Some(_) => Err("Unknown command provided"),
} }
} }
impl fmt::Display for GuiCommand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result{
match self {
GuiCommand::Uci => write!(f, "uci\n"),
_ => unimplemented!(),
}
}
}