Create executable over stockfish
This commit is contained in:
parent
74cd37666e
commit
c125ce3dfc
|
@ -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"
|
||||||
|
|
||||||
|
|
56
src/lib.rs
56
src/lib.rs
|
@ -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}"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()});
|
||||||
|
}
|
|
@ -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!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue