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"
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
[dependencies]
chess = "3.2.0"
env_logger = "0.10.0"
itertools = "0.10.5"
log = "0.4.17"

View File

@ -3,18 +3,18 @@
/// Manage UCI messages and link into chess component
mod uci_command;
use log::warn;
use log::{info, warn};
use std::io::*;
use crate::uci_command::*;
pub struct UciEngine<Fi: Read, Fo: Write> {
pub struct UciEngine<Fi: BufRead, Fo: Write> {
source: Fi,
destination: Fo,
uciok: bool,
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> {
UciEngine::<Fi, Fo>{
source, destination,
@ -22,6 +22,24 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
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> {
self.id.name()
}
@ -84,12 +102,6 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
/// 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");
@ -99,12 +111,30 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
match parse (&mut command.to_string()) {
Ok(EngineCommand::Id{id}) => self.update(&id),
Ok(EngineCommand::UciOk) => self.uciok(),
Ok(EngineCommand::Opt { options: _ }) => {},
Ok(_) => {unimplemented!("command not implemented")},
Err(_) => warn!("Not a command"),
}
}
pub fn next(&mut self) {
exec(self.source.read_line());
pub fn pull(&mut self) {
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 std::time::Duration;
use std::fmt;
use chess::ChessMove;
mod uci_command{}
@ -27,7 +30,6 @@ impl Id {
self.author.clone()
}
}
pub struct Move {}
pub struct Info {}
pub struct Opt{}
pub struct Position{}
@ -36,7 +38,7 @@ pub enum EngineCommand {
Id{id: Id},
UciOk,
ReadyOk,
BestMove{best_move: Move, ponder: Move},
BestMove{best_move: ChessMove, ponder: Option<ChessMove>},
CopyProtection, // unimplemented
Registration, // unimplemented
Info{infos: Vec<Info>},
@ -50,9 +52,9 @@ pub enum GuiCommand {
SetOption{option: Opt},
Register, // unimplemented
UciNewGame,
Position{position: Option<Position>, moves: Vec<Move>},
Position{position: Option<Position>, moves: Vec<ChessMove>},
Go,
SearchMoves{moves: Vec<Move>},
SearchMoves{moves: Vec<ChessMove>},
Ponder,
WTime{time: Duration},
BTime{time: Duration},
@ -90,8 +92,18 @@ pub fn parse(message: &mut String) -> Result<EngineCommand,&'static str> {
Some("copyprotection") => unimplemented!(),
Some("registration") => unimplemented!(),
Some("info") => unimplemented!(),
Some("option") => unimplemented!(),
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 => write!(f, "uci\n"),
_ => unimplemented!(),
}
}
}