fmt
This commit is contained in:
parent
b48f52c41c
commit
69cbe46f1d
|
@ -3,15 +3,16 @@
|
|||
/// Manage UCI messages and link into chess component
|
||||
pub mod uci_command;
|
||||
|
||||
use crate::uci_command::*;
|
||||
use log::{info, warn};
|
||||
use std::io::*;
|
||||
use std::time::Duration;
|
||||
use std::result::Result;
|
||||
use crate::uci_command::*;
|
||||
use std::time::Duration;
|
||||
|
||||
use chess::{Game, Board, ChessMove};
|
||||
use chess::{Board, ChessMove, Game};
|
||||
|
||||
const DEFAULT_TIME: (Option<Duration>, Option<Duration>) = (Some(Duration::from_secs(120 * 60)), None);
|
||||
const DEFAULT_TIME: (Option<Duration>, Option<Duration>) =
|
||||
(Some(Duration::from_secs(120 * 60)), None);
|
||||
|
||||
/// Structure used to manage a chess game with a uci engine.
|
||||
///
|
||||
|
@ -49,28 +50,30 @@ pub struct UciEngine<Fi: Read, Fo: Write> {
|
|||
}
|
||||
|
||||
pub enum UciOption {
|
||||
Ponder{value: bool},
|
||||
UCIElo{value: Option<u32>},
|
||||
Ponder { value: bool },
|
||||
UCIElo { value: Option<u32> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Player{Human{elo: Option<u32>}, Machine{elo: Option<u32>}}
|
||||
pub enum Player {
|
||||
Human { elo: Option<u32> },
|
||||
Machine { elo: Option<u32> },
|
||||
}
|
||||
pub enum GameOption {
|
||||
WhiteTotalTime{value: Option<Duration>},
|
||||
BlackTotalTime{value: Option<Duration>},
|
||||
WhiteIncrement{value: Option<Duration>},
|
||||
BlackIncrement{value: Option<Duration>},
|
||||
WhitePlayer{value: Player},
|
||||
BlackPlayer{value: Player},
|
||||
WhiteTotalTime { value: Option<Duration> },
|
||||
BlackTotalTime { value: Option<Duration> },
|
||||
WhiteIncrement { value: Option<Duration> },
|
||||
BlackIncrement { value: Option<Duration> },
|
||||
WhitePlayer { value: Player },
|
||||
BlackPlayer { value: Player },
|
||||
}
|
||||
|
||||
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
||||
|
||||
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
||||
/// Create new game manager
|
||||
///
|
||||
/// Requires line by line input and output streams to communicate with uci engine
|
||||
pub fn new(source: Option<Fi>, destination: Option<Fo>) -> UciEngine<Fi, Fo> {
|
||||
UciEngine::<Fi, Fo>{
|
||||
UciEngine::<Fi, Fo> {
|
||||
source: source.map(|x| BufReader::new(x)),
|
||||
destination,
|
||||
id: Id::default(),
|
||||
|
@ -78,25 +81,49 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
initial: Board::default(),
|
||||
game: Game::new(),
|
||||
time: [DEFAULT_TIME; 2],
|
||||
player: [Player::Human{elo: None}; 2],
|
||||
player: [Player::Human { elo: None }; 2],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn game_option(&mut self, option: GameOption) -> GameOption {
|
||||
let old_value: GameOption;
|
||||
match option {
|
||||
GameOption::WhiteTotalTime{value} => {old_value = GameOption::WhiteTotalTime { value: self.time[Color::White.to_index()].0 };
|
||||
self.time[Color::White.to_index()].0 = value},
|
||||
GameOption::BlackTotalTime{value} => {old_value = GameOption::BlackTotalTime { value: self.time[Color::Black.to_index()].0 };
|
||||
self.time[Color::Black.to_index()].0 = value},
|
||||
GameOption::WhiteIncrement{value} => {old_value = GameOption::WhiteIncrement { value: self.time[Color::White.to_index()].1 };
|
||||
self.time[Color::White.to_index()].1 = value},
|
||||
GameOption::BlackIncrement{value} => {old_value = GameOption::BlackIncrement { value: self.time[Color::Black.to_index()].1 };
|
||||
self.time[Color::Black.to_index()].1 = value},
|
||||
GameOption::WhitePlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player[Color::White.to_index()] };
|
||||
self.player[Color::White.to_index()] = value},
|
||||
GameOption::BlackPlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player[Color::Black.to_index()] };
|
||||
self.player[Color::Black.to_index()] = value},
|
||||
GameOption::WhiteTotalTime { value } => {
|
||||
old_value = GameOption::WhiteTotalTime {
|
||||
value: self.time[Color::White.to_index()].0,
|
||||
};
|
||||
self.time[Color::White.to_index()].0 = value
|
||||
}
|
||||
GameOption::BlackTotalTime { value } => {
|
||||
old_value = GameOption::BlackTotalTime {
|
||||
value: self.time[Color::Black.to_index()].0,
|
||||
};
|
||||
self.time[Color::Black.to_index()].0 = value
|
||||
}
|
||||
GameOption::WhiteIncrement { value } => {
|
||||
old_value = GameOption::WhiteIncrement {
|
||||
value: self.time[Color::White.to_index()].1,
|
||||
};
|
||||
self.time[Color::White.to_index()].1 = value
|
||||
}
|
||||
GameOption::BlackIncrement { value } => {
|
||||
old_value = GameOption::BlackIncrement {
|
||||
value: self.time[Color::Black.to_index()].1,
|
||||
};
|
||||
self.time[Color::Black.to_index()].1 = value
|
||||
}
|
||||
GameOption::WhitePlayer { value } => {
|
||||
old_value = GameOption::WhitePlayer {
|
||||
value: self.player[Color::White.to_index()],
|
||||
};
|
||||
self.player[Color::White.to_index()] = value
|
||||
}
|
||||
GameOption::BlackPlayer { value } => {
|
||||
old_value = GameOption::WhitePlayer {
|
||||
value: self.player[Color::Black.to_index()],
|
||||
};
|
||||
self.player[Color::Black.to_index()] = value
|
||||
}
|
||||
}
|
||||
|
||||
old_value
|
||||
|
@ -105,7 +132,7 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
/// Launch uci engine initialisation
|
||||
///
|
||||
/// Retrieve data from uci engine (until uciok command from engine)
|
||||
pub fn init(&mut self){
|
||||
pub fn init(&mut self) {
|
||||
self.push(GuiCommand::Uci);
|
||||
|
||||
// Consume commands until uciok messages
|
||||
|
@ -140,16 +167,17 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
/// uci.make_move(ChessMove::from_str("e2e4").expect("error converting e2e4"));
|
||||
/// ```
|
||||
pub fn make_move(&mut self, chess_move: ChessMove) -> bool {
|
||||
if self.game.make_move(chess_move) {
|
||||
self.push(GuiCommand::Position { position: Some(self.initial), moves: self.game.actions().to_vec() });
|
||||
if self.game.make_move(chess_move) {
|
||||
self.push(GuiCommand::Position {
|
||||
position: Some(self.initial),
|
||||
moves: self.game.actions().to_vec(),
|
||||
});
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fn terminate(&mut self, reason: &str) {
|
||||
self.uciok = false;
|
||||
info!("UCI termination: {}", reason);
|
||||
|
@ -165,13 +193,17 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
self.id.author()
|
||||
}
|
||||
|
||||
fn update(&mut self, id: &Id){
|
||||
if self.is_uciok() {warn!("Engine info should not be updated now (uciok)");}
|
||||
fn update(&mut self, id: &Id) {
|
||||
if self.is_uciok() {
|
||||
warn!("Engine info should not be updated now (uciok)");
|
||||
}
|
||||
self.id.update(id);
|
||||
}
|
||||
|
||||
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)");}
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -180,17 +212,20 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
/// if `EngineCommand::Id`: update name or authorship of the engine
|
||||
/// if `EngineCommand::UciOk`: end engine initialization phase
|
||||
pub fn exec(&mut self, command: &str) -> Result<EngineCommand, &'static str> {
|
||||
let uci_command = parse (&mut command.to_string());
|
||||
match uci_command.clone() {
|
||||
Ok(EngineCommand::Id{id}) => self.update(&id),
|
||||
let uci_command = parse(&mut command.to_string());
|
||||
match uci_command.clone() {
|
||||
Ok(EngineCommand::Id { id }) => self.update(&id),
|
||||
Ok(EngineCommand::UciOk) => self.uciok(),
|
||||
Ok(EngineCommand::Opt { options: _ }) => {},
|
||||
Ok(EngineCommand::Info { infos: _ }) |
|
||||
Ok(EngineCommand::BestMove { best_move: _, ponder: _ }) |
|
||||
Ok(EngineCommand::ReadyOk)
|
||||
=> {},
|
||||
Ok(EngineCommand::Registration)|
|
||||
Ok(EngineCommand::CopyProtection)=> {unimplemented!("command not implemented")},
|
||||
Ok(EngineCommand::Opt { options: _ }) => {}
|
||||
Ok(EngineCommand::Info { infos: _ })
|
||||
| Ok(EngineCommand::BestMove {
|
||||
best_move: _,
|
||||
ponder: _,
|
||||
})
|
||||
| Ok(EngineCommand::ReadyOk) => {}
|
||||
Ok(EngineCommand::Registration) | Ok(EngineCommand::CopyProtection) => {
|
||||
unimplemented!("command not implemented")
|
||||
}
|
||||
Err(_) => warn!("Not a command"),
|
||||
};
|
||||
uci_command
|
||||
|
@ -198,16 +233,21 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
|
||||
/// Retrieve a line from the uci engine input stream and parse it
|
||||
pub fn pull(&mut self) -> Option<EngineCommand> {
|
||||
|
||||
if let Some(source) = &mut self.source {
|
||||
let mut command = String::new();
|
||||
match source.read_line(&mut command) {
|
||||
Ok(0) => {self.terminate("Chess engine closed connection."); None},
|
||||
Ok(0) => {
|
||||
self.terminate("Chess engine closed connection.");
|
||||
None
|
||||
}
|
||||
Ok(_) => {
|
||||
info!("← {}", command);
|
||||
self.exec(&command).ok()
|
||||
},
|
||||
Err(reason) => {warn!("Unable to read from engine: {reason}"); None},
|
||||
}
|
||||
Err(reason) => {
|
||||
warn!("Unable to read from engine: {reason}");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("❌ No connected uci engine");
|
||||
|
@ -220,9 +260,18 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
if let Some(source) = &mut self.source {
|
||||
let mut data = String::new();
|
||||
match source.read_line(&mut data) {
|
||||
Ok(0) => {self.terminate("Chess engine closed connection."); None},
|
||||
Ok(_) => {info!("↜ {}", data); Some(data.clone())},
|
||||
Err(reason) => {warn!("Unable to read from engine: {reason}"); None},
|
||||
Ok(0) => {
|
||||
self.terminate("Chess engine closed connection.");
|
||||
None
|
||||
}
|
||||
Ok(_) => {
|
||||
info!("↜ {}", data);
|
||||
Some(data.clone())
|
||||
}
|
||||
Err(reason) => {
|
||||
warn!("Unable to read from engine: {reason}");
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info!("❌ No connected uci engine");
|
||||
|
@ -234,7 +283,7 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
pub fn push(&mut self, command: GuiCommand) {
|
||||
if let Some(destination) = &mut self.destination {
|
||||
let command_str = command.to_string();
|
||||
match destination.write(command_str.as_bytes()){
|
||||
match 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}"),
|
||||
|
@ -245,7 +294,7 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
}
|
||||
|
||||
/// Push string (not a game manager integrated command) to the Uci engine output stream
|
||||
pub fn push_raw(&mut self, data: &str){
|
||||
pub fn push_raw(&mut self, data: &str) {
|
||||
if let Some(destination) = &mut self.destination {
|
||||
match destination.write(data.as_bytes()) {
|
||||
Ok(n) if n == data.len() => info!("↝ raw: {data}"),
|
||||
|
@ -258,8 +307,8 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
||||
fn uciok(&mut self){
|
||||
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
||||
fn uciok(&mut self) {
|
||||
self.uciok = true;
|
||||
}
|
||||
|
||||
|
@ -270,42 +319,59 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
|||
|
||||
pub fn human_play(&mut self, chess_move: ChessMove) -> Result<ChessMove, &'static str> {
|
||||
match self.player[self.side_to_move().to_index()] {
|
||||
Player::Human{..} => {
|
||||
Player::Human { .. } => {
|
||||
if self.make_move(chess_move) {
|
||||
Ok(chess_move)
|
||||
} else {
|
||||
Err("Invalid move for human player")
|
||||
}
|
||||
},
|
||||
Player::Machine {..} => Err("Not a human to play for current color.")
|
||||
}
|
||||
Player::Machine { .. } => Err("Not a human to play for current color."),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn go(&mut self) -> Result<(), &'static str> {
|
||||
match self.player[self.side_to_move().to_index()] {
|
||||
Player::Human{..} => Err("Not a machine to play for current color."),
|
||||
Player::Machine {elo} =>
|
||||
{
|
||||
Player::Human { .. } => Err("Not a machine to play for current color."),
|
||||
Player::Machine { elo } => {
|
||||
if self.is_uciok() {
|
||||
if let Some(elo) = elo {
|
||||
self.push(GuiCommand::SetOption {option: Opt::UCILimitStrength{value: true}});
|
||||
self.push(GuiCommand::SetOption {option: Opt::UCIElo{value: elo}});
|
||||
self.push(GuiCommand::SetOption {
|
||||
option: Opt::UCILimitStrength { value: true },
|
||||
});
|
||||
self.push(GuiCommand::SetOption {
|
||||
option: Opt::UCIElo { value: elo },
|
||||
});
|
||||
}
|
||||
let (wtime, wincr) = self.time[Color::White.to_index()];
|
||||
let (btime, bincr) = self.time[Color::Black.to_index()];
|
||||
self.push(GuiCommand::Go{wtime, wincr, btime, bincr});
|
||||
self.push(GuiCommand::Go {
|
||||
wtime,
|
||||
wincr,
|
||||
btime,
|
||||
bincr,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
Err("UCI engine not ready")
|
||||
Err("UCI engine not ready")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn stop(&mut self) -> Result<(), &'static str> {
|
||||
match self.player[self.side_to_move().to_index()] {
|
||||
Player::Human { .. } => Err("Not a machine to play for current color."),
|
||||
Player::Machine { .. } => {
|
||||
self.push(GuiCommand::Stop);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use chess::{Square, Piece, Color};
|
||||
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
||||
use chess::{Color, Piece, Square};
|
||||
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
|
||||
pub fn piece_on(&self, square: Square) -> Option<Piece> {
|
||||
self.game.current_position().piece_on(square)
|
||||
}
|
||||
|
|
|
@ -1,20 +1,31 @@
|
|||
use itertools::join;
|
||||
use std::{time::Duration, str::FromStr};
|
||||
use std::fmt;
|
||||
use std::{str::FromStr, time::Duration};
|
||||
|
||||
use chess::{ChessMove, Board, Action};
|
||||
use chess::{Action, Board, ChessMove};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Id {name: Option<String>, author: Option<String>}
|
||||
pub struct Id {
|
||||
name: Option<String>,
|
||||
author: Option<String>,
|
||||
}
|
||||
impl Id {
|
||||
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(&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 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<String> {
|
||||
|
@ -28,10 +39,10 @@ impl Id {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Info {}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Opt{
|
||||
UCILimitStrength{value: bool},
|
||||
UCIElo{value: u32},
|
||||
SlowMover{value: u32},
|
||||
pub enum Opt {
|
||||
UCILimitStrength { value: bool },
|
||||
UCIElo { value: u32 },
|
||||
SlowMover { value: u32 },
|
||||
}
|
||||
impl Opt {
|
||||
pub fn name(&self) -> &str {
|
||||
|
@ -43,7 +54,7 @@ impl Opt {
|
|||
}
|
||||
}
|
||||
impl fmt::Display for Opt {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Opt::UCILimitStrength { value } => write!(f, "{value}"),
|
||||
Opt::UCIElo { value } => write!(f, "{value}"),
|
||||
|
@ -54,91 +65,160 @@ impl fmt::Display for Opt {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub enum EngineCommand {
|
||||
Id{id: Id},
|
||||
Id {
|
||||
id: Id,
|
||||
},
|
||||
UciOk,
|
||||
ReadyOk,
|
||||
BestMove{best_move: ChessMove, ponder: Option<ChessMove>},
|
||||
BestMove {
|
||||
best_move: ChessMove,
|
||||
ponder: Option<ChessMove>,
|
||||
},
|
||||
CopyProtection, // unimplemented
|
||||
Registration, // unimplemented
|
||||
Info{infos: Vec<Info>},
|
||||
Opt{options: Vec<Opt>},
|
||||
Registration, // unimplemented
|
||||
Info {
|
||||
infos: Vec<Info>,
|
||||
},
|
||||
Opt {
|
||||
options: Vec<Opt>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GuiCommand {
|
||||
Uci,
|
||||
Debug{mode: bool},
|
||||
Debug {
|
||||
mode: bool,
|
||||
},
|
||||
IsReady,
|
||||
SetOption{option: Opt},
|
||||
SetOption {
|
||||
option: Opt,
|
||||
},
|
||||
Register, // unimplemented
|
||||
UciNewGame,
|
||||
Position{position: Option<Board>, moves: Vec<Action>},
|
||||
Go{wtime: Option<Duration>, wincr: Option<Duration>,
|
||||
btime: Option<Duration>, bincr: Option<Duration>},
|
||||
Position {
|
||||
position: Option<Board>,
|
||||
moves: Vec<Action>,
|
||||
},
|
||||
Go {
|
||||
wtime: Option<Duration>,
|
||||
wincr: Option<Duration>,
|
||||
btime: Option<Duration>,
|
||||
bincr: Option<Duration>,
|
||||
},
|
||||
Stop,
|
||||
PonderHit,
|
||||
Quit,
|
||||
}
|
||||
|
||||
pub fn parse(message: &mut str) -> Result<EngineCommand,&'static str> {
|
||||
pub fn parse(message: &mut str) -> Result<EngineCommand, &'static str> {
|
||||
let mut message_iter = message.split_whitespace();
|
||||
|
||||
match message_iter.next() {
|
||||
None => Err("No command provided"),
|
||||
|
||||
Some("id") =>
|
||||
match message_iter.collect::<Vec<&str>>().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::default(); id.update_from_str(Some(&join(tail, " ")), None); id}}),
|
||||
["author", tail @ ..] => Ok(EngineCommand::Id{id: { let mut id = Id::default(); id.update_from_str(None, Some(&join(tail, " "))); id}}),
|
||||
_ => Err("Invalid id subcommand")
|
||||
},
|
||||
Some("id") => match message_iter.collect::<Vec<&str>>().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::default();
|
||||
id.update_from_str(Some(&join(tail, " ")), None);
|
||||
id
|
||||
},
|
||||
}),
|
||||
["author", tail @ ..] => Ok(EngineCommand::Id {
|
||||
id: {
|
||||
let mut id = Id::default();
|
||||
id.update_from_str(None, Some(&join(tail, " ")));
|
||||
id
|
||||
},
|
||||
}),
|
||||
_ => Err("Invalid id subcommand"),
|
||||
},
|
||||
Some("uciok") => Ok(EngineCommand::UciOk),
|
||||
Some("readyok") => unimplemented!(),
|
||||
Some("bestmove") =>
|
||||
match message_iter.collect::<Vec<&str>>().as_slice() {
|
||||
[] => Err("Empty bestmove command"),
|
||||
[chessmove] => Ok(EngineCommand::BestMove { best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"), ponder: None }),
|
||||
[_, "ponder"] => Err("Empty ponder in bestmove command"),
|
||||
[chessmove, "ponder", chess_ponder] => Ok(EngineCommand::BestMove {
|
||||
best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"),
|
||||
ponder: Some(ChessMove::from_str(chess_ponder).expect("chessmove ponder is invalid")) }),
|
||||
_ => Err("Invalid chessmove subcommand")
|
||||
Some("bestmove") => match message_iter.collect::<Vec<&str>>().as_slice() {
|
||||
[] => Err("Empty bestmove command"),
|
||||
[chessmove] => Ok(EngineCommand::BestMove {
|
||||
best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"),
|
||||
ponder: None,
|
||||
}),
|
||||
[_, "ponder"] => Err("Empty ponder in bestmove command"),
|
||||
[chessmove, "ponder", chess_ponder] => Ok(EngineCommand::BestMove {
|
||||
best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"),
|
||||
ponder: Some(
|
||||
ChessMove::from_str(chess_ponder).expect("chessmove ponder is invalid"),
|
||||
),
|
||||
}),
|
||||
_ => Err("Invalid chessmove subcommand"),
|
||||
},
|
||||
Some("copyprotection") => unimplemented!(),
|
||||
Some("registration") => unimplemented!(),
|
||||
Some("info") => Ok(EngineCommand::Info { infos: Vec::new() }), //todo!("parse info lines")
|
||||
Some("option") => Ok(EngineCommand::Opt { options: Vec::new() }), // todo!("Parse options lines")
|
||||
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{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
GuiCommand::Uci => writeln!(f, "uci"),
|
||||
GuiCommand::UciNewGame => writeln!(f, "ucinewgame"),
|
||||
GuiCommand::Position { position, moves } => {
|
||||
writeln!(f, "position {} moves {}",
|
||||
match position { None => "startpos".to_string(),
|
||||
Some(board) if *board == Board::default() => "startpos".to_string(),
|
||||
Some(board) =>"fen ".to_string() + &board.to_string()},
|
||||
join(moves.iter().map(|x| if let Action::MakeMove(chessmove) = x
|
||||
{" ".to_string() + &chessmove.to_string()} else {"".to_string()}), "")
|
||||
writeln!(
|
||||
f,
|
||||
"position {} moves {}",
|
||||
match position {
|
||||
None => "startpos".to_string(),
|
||||
Some(board) if *board == Board::default() => "startpos".to_string(),
|
||||
Some(board) => "fen ".to_string() + &board.to_string(),
|
||||
},
|
||||
join(
|
||||
moves
|
||||
.iter()
|
||||
.map(|x| if let Action::MakeMove(chessmove) = x {
|
||||
" ".to_string() + &chessmove.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}),
|
||||
""
|
||||
)
|
||||
)
|
||||
}
|
||||
GuiCommand::SetOption { option } => {
|
||||
writeln!(f, "setoption name {} value {}", option.name(), option)
|
||||
writeln!(f, "setoption name {} value {}", option.name(), option)
|
||||
}
|
||||
GuiCommand::Go { wtime, wincr, btime, bincr } => {
|
||||
let wtime = if let Some(wtime) = wtime {format!(" wtime {}", wtime.as_millis())} else {"".to_string()};
|
||||
let btime = if let Some(btime) = btime {format!(" btime {}", btime.as_millis())} else {"".to_string()};
|
||||
let wincr = if let Some(wincr) = wincr {format!(" wincr {}", wincr.as_millis())} else {"".to_string()};
|
||||
let bincr = if let Some(bincr) = bincr {format!(" bincr {}", bincr.as_millis())} else {"".to_string()};
|
||||
GuiCommand::Go {
|
||||
wtime,
|
||||
wincr,
|
||||
btime,
|
||||
bincr,
|
||||
} => {
|
||||
let wtime = if let Some(wtime) = wtime {
|
||||
format!(" wtime {}", wtime.as_millis())
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let btime = if let Some(btime) = btime {
|
||||
format!(" btime {}", btime.as_millis())
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let wincr = if let Some(wincr) = wincr {
|
||||
format!(" wincr {}", wincr.as_millis())
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let bincr = if let Some(bincr) = bincr {
|
||||
format!(" bincr {}", bincr.as_millis())
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
writeln!(f, "go{}{}{}{}", wtime, btime, wincr, bincr)
|
||||
}
|
||||
|
||||
|
@ -146,9 +226,9 @@ impl fmt::Display for GuiCommand {
|
|||
GuiCommand::PonderHit => writeln!(f, "ponderhit"),
|
||||
GuiCommand::Quit => writeln!(f, "quit"),
|
||||
|
||||
GuiCommand::Register|GuiCommand::Debug { .. }|GuiCommand::IsReady
|
||||
=> unimplemented!("{:?}", self),
|
||||
GuiCommand::Register | GuiCommand::Debug { .. } | GuiCommand::IsReady => {
|
||||
unimplemented!("{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ extern crate chess;
|
|||
extern crate chess_uci;
|
||||
|
||||
use chess::ChessMove;
|
||||
use chess_uci::*;
|
||||
use chess_uci::uci_command::EngineCommand;
|
||||
use chess_uci::*;
|
||||
|
||||
use std::process::{Command, Stdio};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn main(){
|
||||
pub fn main() {
|
||||
env_logger::init();
|
||||
|
||||
println!("Launching hardcoded stockfish program (should be in PATH)");
|
||||
|
@ -17,37 +17,58 @@ pub fn main(){
|
|||
let process = match Command::new("stockfish")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.spawn() {
|
||||
Err(why) => panic!("couldn't spawn stockfish: {}", why),
|
||||
Ok(process) => process,
|
||||
};
|
||||
.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 (sf_in, sf_out) = (
|
||||
process.stdin.expect("Program stdin"),
|
||||
process.stdout.expect("Program stdout"),
|
||||
);
|
||||
let mut uci = UciEngine::new(Some(sf_out), Some(sf_in));
|
||||
uci.game_option(GameOption::WhitePlayer { value: Player::Human { elo: None } });
|
||||
uci.game_option(GameOption::BlackPlayer { value: Player::Machine { elo: Some(1500) } });
|
||||
uci.game_option(GameOption::WhitePlayer {
|
||||
value: Player::Human { elo: None },
|
||||
});
|
||||
uci.game_option(GameOption::BlackPlayer {
|
||||
value: Player::Machine { elo: Some(1500) },
|
||||
});
|
||||
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()});
|
||||
uci.push(uci_command::GuiCommand::SetOption { option: uci_command::Opt::SlowMover { value: 11 }});
|
||||
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()
|
||||
}
|
||||
);
|
||||
uci.push(uci_command::GuiCommand::SetOption {
|
||||
option: uci_command::Opt::SlowMover { value: 11 },
|
||||
});
|
||||
|
||||
uci.new_game();
|
||||
uci.push_raw("d\n");
|
||||
|
||||
uci.human_play(ChessMove::from_str("e2e4").expect("error converting e2e4")).expect("can not make human move");
|
||||
uci.human_play(ChessMove::from_str("e2e4").expect("error converting e2e4"))
|
||||
.expect("can not make human move");
|
||||
|
||||
uci.push_raw("d\n");
|
||||
|
||||
uci.go().expect("can not make engine move");
|
||||
|
||||
|
||||
loop {
|
||||
match uci.pull() {
|
||||
Some(EngineCommand::BestMove { best_move, .. }) => {
|
||||
uci.make_move(best_move);
|
||||
break
|
||||
},
|
||||
break;
|
||||
}
|
||||
Some(EngineCommand::Info { .. }) => continue,
|
||||
_ => continue,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue