This commit is contained in:
Baptiste Fouques 2023-01-30 15:56:01 +01:00
parent b48f52c41c
commit 69cbe46f1d
3 changed files with 317 additions and 150 deletions

View File

@ -3,15 +3,16 @@
/// Manage UCI messages and link into chess component /// Manage UCI messages and link into chess component
pub mod uci_command; pub mod uci_command;
use crate::uci_command::*;
use log::{info, warn}; use log::{info, warn};
use std::io::*; use std::io::*;
use std::time::Duration;
use std::result::Result; 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. /// 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 { pub enum UciOption {
Ponder{value: bool}, Ponder { value: bool },
UCIElo{value: Option<u32>}, UCIElo { value: Option<u32> },
} }
#[derive(Clone, Copy)] #[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 { pub enum GameOption {
WhiteTotalTime{value: Option<Duration>}, WhiteTotalTime { value: Option<Duration> },
BlackTotalTime{value: Option<Duration>}, BlackTotalTime { value: Option<Duration> },
WhiteIncrement{value: Option<Duration>}, WhiteIncrement { value: Option<Duration> },
BlackIncrement{value: Option<Duration>}, BlackIncrement { value: Option<Duration> },
WhitePlayer{value: Player}, WhitePlayer { value: Player },
BlackPlayer{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 /// Create new game manager
/// ///
/// Requires line by line input and output streams to communicate with uci engine /// 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> { 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)), source: source.map(|x| BufReader::new(x)),
destination, destination,
id: Id::default(), id: Id::default(),
@ -78,25 +81,49 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
initial: Board::default(), initial: Board::default(),
game: Game::new(), game: Game::new(),
time: [DEFAULT_TIME; 2], 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 { pub fn game_option(&mut self, option: GameOption) -> GameOption {
let old_value: GameOption; let old_value: GameOption;
match option { match option {
GameOption::WhiteTotalTime{value} => {old_value = GameOption::WhiteTotalTime { value: self.time[Color::White.to_index()].0 }; GameOption::WhiteTotalTime { value } => {
self.time[Color::White.to_index()].0 = value}, old_value = GameOption::WhiteTotalTime {
GameOption::BlackTotalTime{value} => {old_value = GameOption::BlackTotalTime { value: self.time[Color::Black.to_index()].0 }; value: self.time[Color::White.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()].0 = value
self.time[Color::White.to_index()].1 = value}, }
GameOption::BlackIncrement{value} => {old_value = GameOption::BlackIncrement { value: self.time[Color::Black.to_index()].1 }; GameOption::BlackTotalTime { value } => {
self.time[Color::Black.to_index()].1 = value}, old_value = GameOption::BlackTotalTime {
GameOption::WhitePlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player[Color::White.to_index()] }; value: self.time[Color::Black.to_index()].0,
self.player[Color::White.to_index()] = value}, };
GameOption::BlackPlayer{value} => {old_value = GameOption::WhitePlayer { value: self.player[Color::Black.to_index()] }; self.time[Color::Black.to_index()].0 = value
self.player[Color::Black.to_index()] = 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 old_value
@ -105,7 +132,7 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
/// Launch uci engine initialisation /// Launch uci engine initialisation
/// ///
/// Retrieve data from uci engine (until uciok command from engine) /// Retrieve data from uci engine (until uciok command from engine)
pub fn init(&mut self){ pub fn init(&mut self) {
self.push(GuiCommand::Uci); self.push(GuiCommand::Uci);
// Consume commands until uciok messages // 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")); /// uci.make_move(ChessMove::from_str("e2e4").expect("error converting e2e4"));
/// ``` /// ```
pub fn make_move(&mut self, chess_move: ChessMove) -> bool { pub fn make_move(&mut self, chess_move: ChessMove) -> bool {
if self.game.make_move(chess_move) { if self.game.make_move(chess_move) {
self.push(GuiCommand::Position { position: Some(self.initial), moves: self.game.actions().to_vec() }); self.push(GuiCommand::Position {
position: Some(self.initial),
moves: self.game.actions().to_vec(),
});
true true
} else { } else {
false false
} }
} }
fn terminate(&mut self, reason: &str) { fn terminate(&mut self, reason: &str) {
self.uciok = false; self.uciok = false;
info!("UCI termination: {}", reason); info!("UCI termination: {}", reason);
@ -165,13 +193,17 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
self.id.author() self.id.author()
} }
fn update(&mut self, id: &Id){ fn update(&mut self, id: &Id) {
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);
} }
fn update_from_str(&mut self, name: Option<&str>, author: Option<&str>){ 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);
} }
@ -180,17 +212,20 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
/// if `EngineCommand::Id`: update name or authorship of the engine /// if `EngineCommand::Id`: update name or authorship of the engine
/// if `EngineCommand::UciOk`: end engine initialization phase /// if `EngineCommand::UciOk`: end engine initialization phase
pub fn exec(&mut self, command: &str) -> Result<EngineCommand, &'static str> { pub fn exec(&mut self, command: &str) -> Result<EngineCommand, &'static str> {
let uci_command = parse (&mut command.to_string()); let uci_command = parse(&mut command.to_string());
match uci_command.clone() { match uci_command.clone() {
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(EngineCommand::Opt { options: _ }) => {}
Ok(EngineCommand::Info { infos: _ }) | Ok(EngineCommand::Info { infos: _ })
Ok(EngineCommand::BestMove { best_move: _, ponder: _ }) | | Ok(EngineCommand::BestMove {
Ok(EngineCommand::ReadyOk) best_move: _,
=> {}, ponder: _,
Ok(EngineCommand::Registration)| })
Ok(EngineCommand::CopyProtection)=> {unimplemented!("command not implemented")}, | Ok(EngineCommand::ReadyOk) => {}
Ok(EngineCommand::Registration) | Ok(EngineCommand::CopyProtection) => {
unimplemented!("command not implemented")
}
Err(_) => warn!("Not a command"), Err(_) => warn!("Not a command"),
}; };
uci_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 /// Retrieve a line from the uci engine input stream and parse it
pub fn pull(&mut self) -> Option<EngineCommand> { pub fn pull(&mut self) -> Option<EngineCommand> {
if let Some(source) = &mut self.source { if let Some(source) = &mut self.source {
let mut command = String::new(); let mut command = String::new();
match source.read_line(&mut command) { 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(_) => { Ok(_) => {
info!("← {}", command); info!("← {}", command);
self.exec(&command).ok() 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 { } else {
info!("❌ No connected uci engine"); info!("❌ No connected uci engine");
@ -220,9 +260,18 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
if let Some(source) = &mut self.source { if let Some(source) = &mut self.source {
let mut data = String::new(); let mut data = String::new();
match source.read_line(&mut data) { match source.read_line(&mut data) {
Ok(0) => {self.terminate("Chess engine closed connection."); None}, Ok(0) => {
Ok(_) => {info!("↜ {}", data); Some(data.clone())}, self.terminate("Chess engine closed connection.");
Err(reason) => {warn!("Unable to read from engine: {reason}"); None}, None
}
Ok(_) => {
info!("↜ {}", data);
Some(data.clone())
}
Err(reason) => {
warn!("Unable to read from engine: {reason}");
None
}
} }
} else { } else {
info!("❌ No connected uci engine"); info!("❌ No connected uci engine");
@ -234,7 +283,7 @@ impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
pub fn push(&mut self, command: GuiCommand) { pub fn push(&mut self, command: GuiCommand) {
if let Some(destination) = &mut self.destination { if let Some(destination) = &mut self.destination {
let command_str = command.to_string(); 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) if n == command_str.len() => info!("→ gui: {command_str}"),
Ok(n) => warn!("⚠ gui: {command_str} truncated at {n}"), Ok(n) => warn!("⚠ gui: {command_str} truncated at {n}"),
Err(reason) => warn!("Unable to send command {command_str}: {reason}"), 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 /// 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 { if let Some(destination) = &mut self.destination {
match destination.write(data.as_bytes()) { match destination.write(data.as_bytes()) {
Ok(n) if n == data.len() => info!("↝ raw: {data}"), 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> { impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
fn uciok(&mut self){ fn uciok(&mut self) {
self.uciok = true; 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> { pub fn human_play(&mut self, chess_move: ChessMove) -> Result<ChessMove, &'static str> {
match self.player[self.side_to_move().to_index()] { match self.player[self.side_to_move().to_index()] {
Player::Human{..} => { Player::Human { .. } => {
if self.make_move(chess_move) { if self.make_move(chess_move) {
Ok(chess_move) Ok(chess_move)
} else { } else {
Err("Invalid move for human player") 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> { pub fn go(&mut self) -> Result<(), &'static str> {
match self.player[self.side_to_move().to_index()] { match self.player[self.side_to_move().to_index()] {
Player::Human{..} => Err("Not a machine to play for current color."), Player::Human { .. } => Err("Not a machine to play for current color."),
Player::Machine {elo} => Player::Machine { elo } => {
{
if self.is_uciok() { if self.is_uciok() {
if let Some(elo) = elo { if let Some(elo) = elo {
self.push(GuiCommand::SetOption {option: Opt::UCILimitStrength{value: true}}); self.push(GuiCommand::SetOption {
self.push(GuiCommand::SetOption {option: Opt::UCIElo{value: elo}}); option: Opt::UCILimitStrength { value: true },
});
self.push(GuiCommand::SetOption {
option: Opt::UCIElo { value: elo },
});
} }
let (wtime, wincr) = self.time[Color::White.to_index()]; let (wtime, wincr) = self.time[Color::White.to_index()];
let (btime, bincr) = self.time[Color::Black.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(()) Ok(())
} else { } 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}; use chess::{Color, Piece, Square};
impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> { impl<Fi: Read, Fo: Write> UciEngine<Fi, Fo> {
pub fn piece_on(&self, square: Square) -> Option<Piece> { pub fn piece_on(&self, square: Square) -> Option<Piece> {
self.game.current_position().piece_on(square) self.game.current_position().piece_on(square)
} }

View File

@ -1,20 +1,31 @@
use itertools::join; use itertools::join;
use std::{time::Duration, str::FromStr};
use std::fmt; use std::fmt;
use std::{str::FromStr, time::Duration};
use chess::{ChessMove, Board, Action}; use chess::{Action, Board, ChessMove};
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Id {name: Option<String>, author: Option<String>} pub struct Id {
name: Option<String>,
author: Option<String>,
}
impl Id { impl Id {
pub fn update(&mut self, id: &Id){ pub fn update(&mut self, id: &Id) {
if let Some(name) = &id.name {self.name = Some(name.clone());} if let Some(name) = &id.name {
if let Some(author) = &id.author {self.author = Some(author.clone());} 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>){ 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(name) = name {
if let Some(author) = author {self.author = Some(author.to_string());} self.name = Some(name.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> {
@ -28,10 +39,10 @@ impl Id {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Info {} pub struct Info {}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Opt{ pub enum Opt {
UCILimitStrength{value: bool}, UCILimitStrength { value: bool },
UCIElo{value: u32}, UCIElo { value: u32 },
SlowMover{value: u32}, SlowMover { value: u32 },
} }
impl Opt { impl Opt {
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
@ -43,7 +54,7 @@ impl Opt {
} }
} }
impl fmt::Display for 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 { match self {
Opt::UCILimitStrength { value } => write!(f, "{value}"), Opt::UCILimitStrength { value } => write!(f, "{value}"),
Opt::UCIElo { value } => write!(f, "{value}"), Opt::UCIElo { value } => write!(f, "{value}"),
@ -54,91 +65,160 @@ impl fmt::Display for Opt {
#[derive(Clone)] #[derive(Clone)]
pub enum EngineCommand { pub enum EngineCommand {
Id{id: Id}, Id {
id: Id,
},
UciOk, UciOk,
ReadyOk, ReadyOk,
BestMove{best_move: ChessMove, ponder: Option<ChessMove>}, BestMove {
best_move: ChessMove,
ponder: Option<ChessMove>,
},
CopyProtection, // unimplemented CopyProtection, // unimplemented
Registration, // unimplemented Registration, // unimplemented
Info{infos: Vec<Info>}, Info {
Opt{options: Vec<Opt>}, infos: Vec<Info>,
},
Opt {
options: Vec<Opt>,
},
} }
#[derive(Debug)] #[derive(Debug)]
pub enum GuiCommand { pub enum GuiCommand {
Uci, Uci,
Debug{mode: bool}, Debug {
mode: bool,
},
IsReady, IsReady,
SetOption{option: Opt}, SetOption {
option: Opt,
},
Register, // unimplemented Register, // unimplemented
UciNewGame, UciNewGame,
Position{position: Option<Board>, moves: Vec<Action>}, Position {
Go{wtime: Option<Duration>, wincr: Option<Duration>, position: Option<Board>,
btime: Option<Duration>, bincr: Option<Duration>}, moves: Vec<Action>,
},
Go {
wtime: Option<Duration>,
wincr: Option<Duration>,
btime: Option<Duration>,
bincr: Option<Duration>,
},
Stop, Stop,
PonderHit, PonderHit,
Quit, 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(); let mut message_iter = message.split_whitespace();
match message_iter.next() { match message_iter.next() {
None => Err("No command provided"), None => Err("No command provided"),
Some("id") => Some("id") => match message_iter.collect::<Vec<&str>>().as_slice() {
match message_iter.collect::<Vec<&str>>().as_slice() { [] => Err("Empty id command"),
[] => Err("Empty id command"), ["name"] => Err("Empty id name command"),
["name"] => Err("Empty id name command"), ["author"] => Err("Empty id author command"),
["author"] => Err("Empty id author command"), ["name", tail @ ..] => Ok(EngineCommand::Id {
["name", tail @ ..] => Ok(EngineCommand::Id{id: { let mut id = Id::default(); id.update_from_str(Some(&join(tail, " ")), None); id}}), id: {
["author", tail @ ..] => Ok(EngineCommand::Id{id: { let mut id = Id::default(); id.update_from_str(None, Some(&join(tail, " "))); id}}), let mut id = Id::default();
_ => Err("Invalid id subcommand") 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("uciok") => Ok(EngineCommand::UciOk),
Some("readyok") => unimplemented!(), Some("readyok") => unimplemented!(),
Some("bestmove") => Some("bestmove") => match message_iter.collect::<Vec<&str>>().as_slice() {
match message_iter.collect::<Vec<&str>>().as_slice() { [] => Err("Empty bestmove command"),
[] => Err("Empty bestmove command"), [chessmove] => Ok(EngineCommand::BestMove {
[chessmove] => Ok(EngineCommand::BestMove { best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"), ponder: None }), best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"),
[_, "ponder"] => Err("Empty ponder in bestmove command"), ponder: None,
[chessmove, "ponder", chess_ponder] => Ok(EngineCommand::BestMove { }),
best_move: ChessMove::from_str(chessmove).expect("chessmove is invalid"), [_, "ponder"] => Err("Empty ponder in bestmove command"),
ponder: Some(ChessMove::from_str(chess_ponder).expect("chessmove ponder is invalid")) }), [chessmove, "ponder", chess_ponder] => Ok(EngineCommand::BestMove {
_ => Err("Invalid chessmove subcommand") 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("copyprotection") => unimplemented!(),
Some("registration") => unimplemented!(), Some("registration") => unimplemented!(),
Some("info") => Ok(EngineCommand::Info { infos: Vec::new() }), //todo!("parse info lines") 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"), Some(_) => Err("Unknown command provided"),
} }
} }
impl fmt::Display for GuiCommand { 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 { match self {
GuiCommand::Uci => writeln!(f, "uci"), GuiCommand::Uci => writeln!(f, "uci"),
GuiCommand::UciNewGame => writeln!(f, "ucinewgame"), GuiCommand::UciNewGame => writeln!(f, "ucinewgame"),
GuiCommand::Position { position, moves } => { GuiCommand::Position { position, moves } => {
writeln!(f, "position {} moves {}", writeln!(
match position { None => "startpos".to_string(), f,
Some(board) if *board == Board::default() => "startpos".to_string(), "position {} moves {}",
Some(board) =>"fen ".to_string() + &board.to_string()}, match position {
join(moves.iter().map(|x| if let Action::MakeMove(chessmove) = x None => "startpos".to_string(),
{" ".to_string() + &chessmove.to_string()} else {"".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 } => { 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 } => { GuiCommand::Go {
let wtime = if let Some(wtime) = wtime {format!(" wtime {}", wtime.as_millis())} else {"".to_string()}; wtime,
let btime = if let Some(btime) = btime {format!(" btime {}", btime.as_millis())} else {"".to_string()}; wincr,
let wincr = if let Some(wincr) = wincr {format!(" wincr {}", wincr.as_millis())} else {"".to_string()}; btime,
let bincr = if let Some(bincr) = bincr {format!(" bincr {}", bincr.as_millis())} else {"".to_string()}; 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) writeln!(f, "go{}{}{}{}", wtime, btime, wincr, bincr)
} }
@ -146,9 +226,9 @@ impl fmt::Display for GuiCommand {
GuiCommand::PonderHit => writeln!(f, "ponderhit"), GuiCommand::PonderHit => writeln!(f, "ponderhit"),
GuiCommand::Quit => writeln!(f, "quit"), GuiCommand::Quit => writeln!(f, "quit"),
GuiCommand::Register|GuiCommand::Debug { .. }|GuiCommand::IsReady GuiCommand::Register | GuiCommand::Debug { .. } | GuiCommand::IsReady => {
=> unimplemented!("{:?}", self), unimplemented!("{:?}", self)
}
} }
} }
} }

View File

@ -2,13 +2,13 @@ extern crate chess;
extern crate chess_uci; extern crate chess_uci;
use chess::ChessMove; use chess::ChessMove;
use chess_uci::*;
use chess_uci::uci_command::EngineCommand; use chess_uci::uci_command::EngineCommand;
use chess_uci::*;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::str::FromStr; use std::str::FromStr;
pub fn main(){ pub fn main() {
env_logger::init(); env_logger::init();
println!("Launching hardcoded stockfish program (should be in PATH)"); println!("Launching hardcoded stockfish program (should be in PATH)");
@ -17,37 +17,58 @@ pub fn main(){
let process = match Command::new("stockfish") let process = match Command::new("stockfish")
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() { .spawn()
Err(why) => panic!("couldn't spawn stockfish: {}", why), {
Ok(process) => process, 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)); 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::WhitePlayer {
uci.game_option(GameOption::BlackPlayer { value: Player::Machine { elo: Some(1500) } }); value: Player::Human { elo: None },
});
uci.game_option(GameOption::BlackPlayer {
value: Player::Machine { elo: Some(1500) },
});
uci.init(); uci.init();
println!("Engine: {} \nby: {}", println!(
if let Some(name) = uci.name() {name} else {"Not defined".to_string()}, "Engine: {} \nby: {}",
if let Some(author) = uci.author() {author} else {"Not defined".to_string()}); if let Some(name) = uci.name() {
uci.push(uci_command::GuiCommand::SetOption { option: uci_command::Opt::SlowMover { value: 11 }}); 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.new_game();
uci.push_raw("d\n"); 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.push_raw("d\n");
uci.go().expect("can not make engine move"); uci.go().expect("can not make engine move");
loop { loop {
match uci.pull() { match uci.pull() {
Some(EngineCommand::BestMove { best_move, .. }) => { Some(EngineCommand::BestMove { best_move, .. }) => {
uci.make_move(best_move); uci.make_move(best_move);
break break;
}, }
Some(EngineCommand::Info { .. }) => continue, Some(EngineCommand::Info { .. }) => continue,
_ => continue, _ => continue,
} }