engine: correctly manage some go subcommands
This commit is contained in:
parent
5771930e97
commit
8310e0c1e7
|
@ -196,7 +196,7 @@ pub fn apply_into(board: &mut Board, m: &Move) {
|
||||||
clear_square(board, &m.0)
|
clear_square(board, &m.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(board: &Board) {
|
pub fn draw(board: &Board, f: &mut dyn std::io::Write) {
|
||||||
for r in (0..8).rev() {
|
for r in (0..8).rev() {
|
||||||
let mut rank = String::with_capacity(8);
|
let mut rank = String::with_capacity(8);
|
||||||
for f in 0..8 {
|
for f in 0..8 {
|
||||||
|
@ -212,9 +212,9 @@ pub fn draw(board: &Board) {
|
||||||
let piece = if is_color(s, SQ_WH) { piece.to_ascii_uppercase() } else { piece };
|
let piece = if is_color(s, SQ_WH) { piece.to_ascii_uppercase() } else { piece };
|
||||||
rank.push(piece);
|
rank.push(piece);
|
||||||
}
|
}
|
||||||
println!("{} {}", r + 1, rank);
|
writeln!(f, "{} {}", r + 1, rank).unwrap();
|
||||||
}
|
}
|
||||||
println!(" abcdefgh");
|
writeln!(f, " abcdefgh").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -16,7 +16,7 @@ pub fn start_game(player_color: u8) {
|
||||||
let mut b = board::new();
|
let mut b = board::new();
|
||||||
let mut turn = board::SQ_WH;
|
let mut turn = board::SQ_WH;
|
||||||
loop {
|
loop {
|
||||||
board::draw(&b);
|
board::draw(&b, &mut io::stdout());
|
||||||
println!("");
|
println!("");
|
||||||
let m = if turn == player_color {
|
let m = if turn == player_color {
|
||||||
println!("Player turn.");
|
println!("Player turn.");
|
||||||
|
|
186
src/engine.rs
186
src/engine.rs
|
@ -1,6 +1,6 @@
|
||||||
//! Vatu engine.
|
//! Vatu engine.
|
||||||
|
|
||||||
use std::sync::mpsc;
|
use std::sync::{Arc, atomic, mpsc};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time;
|
use std::time;
|
||||||
|
|
||||||
|
@ -11,35 +11,68 @@ use crate::notation;
|
||||||
use crate::rules;
|
use crate::rules;
|
||||||
use crate::uci;
|
use crate::uci;
|
||||||
|
|
||||||
|
/// Analysis engine.
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
board: board::Board, // Board to analyse.
|
/// Current game state, starting point of further analysis.
|
||||||
color: u8, // Color to analyse.
|
state: GameState,
|
||||||
castling: u8, // Castling state.
|
/// Communication mode.
|
||||||
en_passant: Option<board::Pos>, // En passant state.
|
|
||||||
halfmove: i32, // Current half moves.
|
|
||||||
fullmove: i32, // Current full moves.
|
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
|
/// If true, the engine is currently listening to incoming cmds.
|
||||||
listening: bool,
|
listening: bool,
|
||||||
|
/// Shared flag to notify workers if they should keep working.
|
||||||
|
working: Arc<atomic::AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Mode {
|
/// Representation of a game state that can cloned to analysis workers.
|
||||||
// No mode, sit here and do nothing.
|
///
|
||||||
|
/// It does not include various parameters such as clocks so that they
|
||||||
|
/// can be passed separately using `WorkArgs`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct GameState {
|
||||||
|
board: board::Board,
|
||||||
|
color: u8,
|
||||||
|
castling: u8,
|
||||||
|
en_passant: Option<board::Pos>,
|
||||||
|
halfmove: i32,
|
||||||
|
fullmove: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Engine communication mode.
|
||||||
|
enum Mode {
|
||||||
|
/// No mode, sit here and do nothing.
|
||||||
No,
|
No,
|
||||||
// UCI mode: listen to Cmds, send Uci::Cmd::Engine commands.
|
/// UCI mode: listen to Cmds, send Uci::Cmd::Engine commands.
|
||||||
Uci(mpsc::Sender<uci::Cmd>, mpsc::Receiver<Cmd>),
|
///
|
||||||
|
/// First value is the Uci command sender to report results.
|
||||||
|
/// Second value is the receiver for all engine commands, whether
|
||||||
|
/// it's from the Uci controller or analysis workers. Third is the
|
||||||
|
/// sender that is passed to receive outer Uci and workers cmds.
|
||||||
|
Uci(mpsc::Sender<uci::Cmd>, mpsc::Receiver<Cmd>, mpsc::Sender<Cmd>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Engine commands.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
// Commands that can be received by the engine.
|
// Commands that can be received by the engine.
|
||||||
UciChannel(mpsc::Sender<Cmd>), // Provide a sender to UCI to start receiving commands.
|
UciChannel(mpsc::Sender<Cmd>), // Provide a sender to UCI to start receiving commands.
|
||||||
UciPosition(Vec<uci::PositionArgs>), // UCI "position" command.
|
UciPosition(Vec<uci::PositionArgs>), // UCI "position" command.
|
||||||
UciGo(Vec<uci::GoArgs>), // UCI "go" command.
|
UciGo(Vec<uci::GoArgs>), // UCI "go" command.
|
||||||
|
Stop, // Stop working ASAP.
|
||||||
|
TmpBestMove(Option<board::Move>), // Send best move found by analysis worker (TEMPORARY).
|
||||||
|
|
||||||
// Commands that can be sent by the engine.
|
// Commands that can be sent by the engine.
|
||||||
BestMove(Option<board::Move>),
|
BestMove(Option<board::Move>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct WorkArgs {
|
||||||
|
move_time: i32,
|
||||||
|
white_time: i32,
|
||||||
|
black_time: i32,
|
||||||
|
white_inc: i32,
|
||||||
|
black_inc: i32,
|
||||||
|
}
|
||||||
|
|
||||||
pub const CASTLING_WH_K: u8 = 0b00000001;
|
pub const CASTLING_WH_K: u8 = 0b00000001;
|
||||||
pub const CASTLING_WH_Q: u8 = 0b00000010;
|
pub const CASTLING_WH_Q: u8 = 0b00000010;
|
||||||
pub const CASTLING_BL_K: u8 = 0b00000100;
|
pub const CASTLING_BL_K: u8 = 0b00000100;
|
||||||
|
@ -50,14 +83,17 @@ pub const CASTLING_MASK: u8 = 0b00001111;
|
||||||
impl Engine {
|
impl Engine {
|
||||||
pub fn new() -> Engine {
|
pub fn new() -> Engine {
|
||||||
Engine {
|
Engine {
|
||||||
|
state: GameState {
|
||||||
board: board::new_empty(),
|
board: board::new_empty(),
|
||||||
color: board::SQ_WH,
|
color: board::SQ_WH,
|
||||||
castling: CASTLING_MASK,
|
castling: CASTLING_MASK,
|
||||||
en_passant: None,
|
en_passant: None,
|
||||||
halfmove: 0,
|
halfmove: 0,
|
||||||
fullmove: 1,
|
fullmove: 1,
|
||||||
|
},
|
||||||
mode: Mode::No,
|
mode: Mode::No,
|
||||||
listening: false,
|
listening: false,
|
||||||
|
working: Arc::new(atomic::AtomicBool::new(false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,9 +105,9 @@ impl Engine {
|
||||||
self.listening = true;
|
self.listening = true;
|
||||||
while self.listening {
|
while self.listening {
|
||||||
match &self.mode {
|
match &self.mode {
|
||||||
Mode::Uci(_, rx) => {
|
Mode::Uci(_, rx, _) => {
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
Ok(c) => self.handle_uci_command(&c),
|
Ok(c) => self.handle_command(&c),
|
||||||
Err(e) => eprintln!("Engine recv failure: {}", e),
|
Err(e) => eprintln!("Engine recv failure: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,9 +116,22 @@ impl Engine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle UCI commands passed as engine Cmds.
|
||||||
|
fn handle_command(&mut self, cmd: &Cmd) {
|
||||||
|
match cmd {
|
||||||
|
// UCI commands.
|
||||||
|
Cmd::UciPosition(args) => self.uci_position(&args),
|
||||||
|
Cmd::UciGo(args) => self.uci_go(&args),
|
||||||
|
Cmd::Stop => self.stop(),
|
||||||
|
// Workers commands.
|
||||||
|
Cmd::TmpBestMove(m) => self.reply(Cmd::BestMove(*m)),
|
||||||
|
_ => eprintln!("Not an engine input command: {:?}", cmd),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn reply(&mut self, cmd: Cmd) {
|
fn reply(&mut self, cmd: Cmd) {
|
||||||
match &self.mode {
|
match &self.mode {
|
||||||
Mode::Uci(tx, _) => {
|
Mode::Uci(tx, _, _) => {
|
||||||
tx.send(uci::Cmd::Engine(cmd)).unwrap();
|
tx.send(uci::Cmd::Engine(cmd)).unwrap();
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -93,6 +142,7 @@ impl Engine {
|
||||||
///
|
///
|
||||||
/// For speed purposes, it assumes values are always valid.
|
/// For speed purposes, it assumes values are always valid.
|
||||||
fn apply_fen(&mut self, fen: ¬ation::Fen) {
|
fn apply_fen(&mut self, fen: ¬ation::Fen) {
|
||||||
|
eprintln!("Applying FEN {:?}", fen);
|
||||||
self.set_fen_placement(&fen.placement);
|
self.set_fen_placement(&fen.placement);
|
||||||
self.set_fen_color(&fen.color);
|
self.set_fen_color(&fen.color);
|
||||||
self.set_fen_castling(&fen.castling);
|
self.set_fen_castling(&fen.castling);
|
||||||
|
@ -102,13 +152,13 @@ impl Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_fen_placement(&mut self, placement: &str) {
|
fn set_fen_placement(&mut self, placement: &str) {
|
||||||
self.board = board::new_from_fen(placement);
|
self.state.board = board::new_from_fen(placement);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_fen_color(&mut self, color: &str) {
|
fn set_fen_color(&mut self, color: &str) {
|
||||||
match color.chars().next().unwrap() {
|
match color.chars().next().unwrap() {
|
||||||
'w' => self.color = board::SQ_WH,
|
'w' => self.state.color = board::SQ_WH,
|
||||||
'b' => self.color = board::SQ_BL,
|
'b' => self.state.color = board::SQ_BL,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,28 +166,28 @@ impl Engine {
|
||||||
fn set_fen_castling(&mut self, castling: &str) {
|
fn set_fen_castling(&mut self, castling: &str) {
|
||||||
for c in castling.chars() {
|
for c in castling.chars() {
|
||||||
match c {
|
match c {
|
||||||
'K' => self.castling |= CASTLING_WH_K,
|
'K' => self.state.castling |= CASTLING_WH_K,
|
||||||
'Q' => self.castling |= CASTLING_WH_Q,
|
'Q' => self.state.castling |= CASTLING_WH_Q,
|
||||||
'k' => self.castling |= CASTLING_BL_K,
|
'k' => self.state.castling |= CASTLING_BL_K,
|
||||||
'q' => self.castling |= CASTLING_BL_Q,
|
'q' => self.state.castling |= CASTLING_BL_Q,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_fen_en_passant(&mut self, en_passant: &str) {
|
fn set_fen_en_passant(&mut self, en_passant: &str) {
|
||||||
self.en_passant = match en_passant {
|
self.state.en_passant = match en_passant {
|
||||||
"-" => None,
|
"-" => None,
|
||||||
p => Some(board::pos(p)),
|
p => Some(board::pos(p)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_fen_halfmove(&mut self, halfmove: &str) {
|
fn set_fen_halfmove(&mut self, halfmove: &str) {
|
||||||
self.halfmove = halfmove.parse::<i32>().ok().unwrap();
|
self.state.halfmove = halfmove.parse::<i32>().ok().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_fen_fullmove(&mut self, fullmove: &str) {
|
fn set_fen_fullmove(&mut self, fullmove: &str) {
|
||||||
self.fullmove = fullmove.parse::<i32>().ok().unwrap();
|
self.state.fullmove = fullmove.parse::<i32>().ok().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_moves(&mut self, moves: &Vec<board::Move>) {
|
fn apply_moves(&mut self, moves: &Vec<board::Move>) {
|
||||||
|
@ -145,20 +195,25 @@ impl Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_move(&mut self, m: &board::Move) {
|
fn apply_move(&mut self, m: &board::Move) {
|
||||||
board::apply_into(&mut self.board, m);
|
board::apply_into(&mut self.state.board, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start working on board, returning the best move found.
|
/// Start working on board, returning the best move found.
|
||||||
///
|
///
|
||||||
/// Stop working after `movetime` ms, or go on forever if it's -1.
|
/// Stop working after `movetime` ms, or go on forever if it's -1.
|
||||||
pub fn work(&mut self, movetime: i32) -> Option<board::Move> {
|
fn work(&mut self, args: &WorkArgs) {
|
||||||
// Stupid engine! Return a random move.
|
self.working.store(true, atomic::Ordering::Relaxed);
|
||||||
let moves = rules::get_player_legal_moves(&self.board, self.color);
|
let state = self.state.clone();
|
||||||
let mut rng = rand::thread_rng();
|
let args = args.clone();
|
||||||
let best_move = moves.iter().choose(&mut rng).and_then(|m| Some(*m));
|
let working = self.working.clone();
|
||||||
// board::draw(&self.board);
|
let tx = match &self.mode { Mode::Uci(_, _, tx) => tx.clone(), _ => return };
|
||||||
thread::sleep(time::Duration::from_millis(movetime as u64));
|
thread::spawn(move || {
|
||||||
best_move
|
analyze(&state, &args, working, tx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop(&mut self) {
|
||||||
|
self.working.store(false, atomic::Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,20 +223,11 @@ impl Engine {
|
||||||
pub fn setup_uci(&mut self, uci_s: mpsc::Sender<uci::Cmd>) {
|
pub fn setup_uci(&mut self, uci_s: mpsc::Sender<uci::Cmd>) {
|
||||||
// Create a channel to receive commands from Uci.
|
// Create a channel to receive commands from Uci.
|
||||||
let (engine_s, engine_r) = mpsc::channel();
|
let (engine_s, engine_r) = mpsc::channel();
|
||||||
uci_s.send(uci::Cmd::Engine(Cmd::UciChannel(engine_s))).unwrap();
|
uci_s.send(uci::Cmd::Engine(Cmd::UciChannel(engine_s.clone()))).unwrap();
|
||||||
self.mode = Mode::Uci(uci_s, engine_r);
|
self.mode = Mode::Uci(uci_s, engine_r, engine_s);
|
||||||
self.listen();
|
self.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle UCI commands passed as engine Cmds.
|
|
||||||
fn handle_uci_command(&mut self, cmd: &Cmd) {
|
|
||||||
match cmd {
|
|
||||||
Cmd::UciPosition(args) => self.uci_position(&args),
|
|
||||||
Cmd::UciGo(args) => self.uci_go(&args),
|
|
||||||
_ => eprintln!("Not an UCI command: {:?}", cmd),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update board state from a "position" command's args.
|
/// Update board state from a "position" command's args.
|
||||||
fn uci_position(&mut self, p_args: &Vec<uci::PositionArgs>) {
|
fn uci_position(&mut self, p_args: &Vec<uci::PositionArgs>) {
|
||||||
for arg in p_args {
|
for arg in p_args {
|
||||||
|
@ -195,6 +241,11 @@ impl Engine {
|
||||||
},
|
},
|
||||||
uci::PositionArgs::Moves(moves) => {
|
uci::PositionArgs::Moves(moves) => {
|
||||||
self.apply_moves(&moves);
|
self.apply_moves(&moves);
|
||||||
|
self.state.color = if moves.len() % 2 == 0 {
|
||||||
|
board::SQ_WH
|
||||||
|
} else {
|
||||||
|
board::SQ_BL
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,15 +253,52 @@ impl Engine {
|
||||||
|
|
||||||
/// Start working using parameters passed with a "go" command.
|
/// Start working using parameters passed with a "go" command.
|
||||||
fn uci_go(&mut self, g_args: &Vec<uci::GoArgs>) {
|
fn uci_go(&mut self, g_args: &Vec<uci::GoArgs>) {
|
||||||
let mut movetime = -1;
|
let mut args = WorkArgs {
|
||||||
|
move_time: -1,
|
||||||
|
white_time: -1,
|
||||||
|
black_time: -1,
|
||||||
|
white_inc: -1,
|
||||||
|
black_inc: -1,
|
||||||
|
};
|
||||||
for arg in g_args {
|
for arg in g_args {
|
||||||
match arg {
|
match arg {
|
||||||
uci::GoArgs::MoveTime(ms) => movetime = *ms,
|
uci::GoArgs::MoveTime(ms) => args.move_time = *ms,
|
||||||
uci::GoArgs::Infinite => movetime = -1,
|
uci::GoArgs::Infinite => {}
|
||||||
|
uci::GoArgs::WTime(ms) => args.white_time = *ms,
|
||||||
|
uci::GoArgs::BTime(ms) => args.black_time = *ms,
|
||||||
|
uci::GoArgs::WInc(ms) => args.white_inc = *ms,
|
||||||
|
uci::GoArgs::BInc(ms) => args.black_inc = *ms,
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let best_move = self.work(movetime);
|
self.work(&args);
|
||||||
self.reply(Cmd::BestMove(best_move));
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze(
|
||||||
|
state: &GameState,
|
||||||
|
_args: &WorkArgs,
|
||||||
|
wip: Arc<atomic::AtomicBool>,
|
||||||
|
tx: mpsc::Sender<Cmd>,
|
||||||
|
) {
|
||||||
|
if !wip.load(atomic::Ordering::Relaxed) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stupid engine! Return a random move.
|
||||||
|
let moves = rules::get_player_legal_moves(&state.board, state.color);
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let best_move = moves.iter().choose(&mut rng).and_then(|m| Some(*m));
|
||||||
|
thread::sleep(time::Duration::from_millis(1000u64));
|
||||||
|
tx.send(Cmd::TmpBestMove(best_move)).unwrap();
|
||||||
|
|
||||||
|
// thread::sleep(time::Duration::from_secs(1));
|
||||||
|
// for _ in 0..4 {
|
||||||
|
// let board = board.clone();
|
||||||
|
// let wip = wip.clone();
|
||||||
|
// thread::spawn(move || {
|
||||||
|
// analyze(&board, wip);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
11
src/rules.rs
11
src/rules.rs
|
@ -52,7 +52,8 @@ fn get_pawn_moves(board: &Board, at: &Pos, piece: u8) -> Vec<Move> {
|
||||||
return moves
|
return moves
|
||||||
}
|
}
|
||||||
let forward: Pos = (f, forward_r);
|
let forward: Pos = (f, forward_r);
|
||||||
if is_empty(board, &forward) {
|
// If forward square is empty (and we are not jumping over an occupied square), add it.
|
||||||
|
if is_empty(board, &forward) && (i == 1 || is_empty(board, &(f, forward_r - 1))) {
|
||||||
moves.push((*at, forward))
|
moves.push((*at, forward))
|
||||||
}
|
}
|
||||||
// Check diagonals for pieces to attack.
|
// Check diagonals for pieces to attack.
|
||||||
|
@ -240,14 +241,18 @@ mod tests {
|
||||||
assert!(moves.contains( &(pos("e2"), pos("e4")) ));
|
assert!(moves.contains( &(pos("e2"), pos("e4")) ));
|
||||||
|
|
||||||
// Check that a pawn cannot move forward if a piece is blocking its path.
|
// Check that a pawn cannot move forward if a piece is blocking its path.
|
||||||
// 1. black pawn 2 square forward:
|
// 1. black pawn 2 square forward; only 1 square forward available from start pos.
|
||||||
set_square(&mut b, &pos("e4"), SQ_BL_P);
|
set_square(&mut b, &pos("e4"), SQ_BL_P);
|
||||||
let moves = get_piece_moves(&b, &pos("e2"));
|
let moves = get_piece_moves(&b, &pos("e2"));
|
||||||
assert!(moves.len() == 1 && moves.contains( &(pos("e2"), pos("e3")) ));
|
assert!(moves.len() == 1 && moves.contains( &(pos("e2"), pos("e3")) ));
|
||||||
// 2. black pawn 1 square forward:
|
// 2. black pawn 1 square forward; no square available.
|
||||||
set_square(&mut b, &pos("e3"), SQ_BL_P);
|
set_square(&mut b, &pos("e3"), SQ_BL_P);
|
||||||
let moves = get_piece_moves(&b, &pos("e2"));
|
let moves = get_piece_moves(&b, &pos("e2"));
|
||||||
assert_eq!(moves.len(), 0);
|
assert_eq!(moves.len(), 0);
|
||||||
|
// 3. remove the e4 black pawn; the white pawn should not be able to jump above e3 pawn.
|
||||||
|
clear_square(&mut b, &pos("e4"));
|
||||||
|
let moves = get_piece_moves(&b, &pos("e2"));
|
||||||
|
assert_eq!(moves.len(), 0);
|
||||||
|
|
||||||
// Check that a pawn can take a piece diagonally.
|
// Check that a pawn can take a piece diagonally.
|
||||||
set_square(&mut b, &pos("f3"), SQ_BL_P);
|
set_square(&mut b, &pos("f3"), SQ_BL_P);
|
||||||
|
|
55
src/uci.rs
55
src/uci.rs
|
@ -63,6 +63,16 @@ pub enum PositionArgs {
|
||||||
/// Arguments for the go remote commands.
|
/// Arguments for the go remote commands.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum GoArgs {
|
pub enum GoArgs {
|
||||||
|
SearchMoves(Vec<board::Move>),
|
||||||
|
Ponder,
|
||||||
|
WTime(i32),
|
||||||
|
BTime(i32),
|
||||||
|
WInc(i32),
|
||||||
|
BInc(i32),
|
||||||
|
MovesToGo(i32),
|
||||||
|
Depth(i32),
|
||||||
|
Nodes(i32),
|
||||||
|
Mate(i32),
|
||||||
MoveTime(i32),
|
MoveTime(i32),
|
||||||
Infinite,
|
Infinite,
|
||||||
}
|
}
|
||||||
|
@ -165,7 +175,6 @@ impl Uci {
|
||||||
},
|
},
|
||||||
UciCmd::IsReady => if self.state == State::Ready { self.send_ready() },
|
UciCmd::IsReady => if self.state == State::Ready { self.send_ready() },
|
||||||
UciCmd::UciNewGame => if self.state == State::Ready { /* Nothing to do. */ },
|
UciCmd::UciNewGame => if self.state == State::Ready { /* Nothing to do. */ },
|
||||||
UciCmd::Stop => if self.state == State::Ready { /* Nothing to do. */ },
|
|
||||||
UciCmd::Position(args) => if self.state == State::Ready {
|
UciCmd::Position(args) => if self.state == State::Ready {
|
||||||
let args = engine::Cmd::UciPosition(args.to_vec());
|
let args = engine::Cmd::UciPosition(args.to_vec());
|
||||||
self.engine_in.as_ref().unwrap().send(args).unwrap();
|
self.engine_in.as_ref().unwrap().send(args).unwrap();
|
||||||
|
@ -173,7 +182,11 @@ impl Uci {
|
||||||
UciCmd::Go(args) => if self.state == State::Ready {
|
UciCmd::Go(args) => if self.state == State::Ready {
|
||||||
let args = engine::Cmd::UciGo(args.to_vec());
|
let args = engine::Cmd::UciGo(args.to_vec());
|
||||||
self.engine_in.as_ref().unwrap().send(args).unwrap();
|
self.engine_in.as_ref().unwrap().send(args).unwrap();
|
||||||
|
self.state = State::Working;
|
||||||
}
|
}
|
||||||
|
UciCmd::Stop => if self.state == State::Working {
|
||||||
|
self.engine_in.as_ref().unwrap().send(engine::Cmd::Stop).unwrap();
|
||||||
|
},
|
||||||
UciCmd::Quit => return false,
|
UciCmd::Quit => return false,
|
||||||
UciCmd::Unknown(c) => { self.log(format!("Unknown command: {}", c)); }
|
UciCmd::Unknown(c) => { self.log(format!("Unknown command: {}", c)); }
|
||||||
}
|
}
|
||||||
|
@ -188,6 +201,7 @@ impl Uci {
|
||||||
self.engine_in = Some(s.to_owned());
|
self.engine_in = Some(s.to_owned());
|
||||||
}
|
}
|
||||||
engine::Cmd::BestMove(m) => {
|
engine::Cmd::BestMove(m) => {
|
||||||
|
self.state = State::Ready;
|
||||||
self.send_bestmove(m);
|
self.send_bestmove(m);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -287,13 +301,44 @@ fn parse_go_command(fields: &[&str]) -> UciCmd {
|
||||||
let mut subcommands = vec!();
|
let mut subcommands = vec!();
|
||||||
while i < num_fields {
|
while i < num_fields {
|
||||||
match fields[i] {
|
match fields[i] {
|
||||||
|
"infinite" => subcommands.push(GoArgs::Infinite),
|
||||||
"movetime" => {
|
"movetime" => {
|
||||||
i += 1;
|
i += 1;
|
||||||
let ms = fields[i].parse::<i32>().unwrap();
|
subcommands.push(GoArgs::MoveTime(fields[i].parse::<i32>().unwrap()));
|
||||||
subcommands.push(GoArgs::MoveTime(ms));
|
|
||||||
}
|
}
|
||||||
"infinite" => subcommands.push(GoArgs::Infinite),
|
"wtime" => {
|
||||||
f => return UciCmd::Unknown(format!("Unknown go subcommand: {}", f)),
|
i += 1;
|
||||||
|
subcommands.push(GoArgs::WTime(fields[i].parse::<i32>().unwrap()));
|
||||||
|
},
|
||||||
|
"btime" => {
|
||||||
|
i += 1;
|
||||||
|
subcommands.push(GoArgs::BTime(fields[i].parse::<i32>().unwrap()));
|
||||||
|
}
|
||||||
|
"winc" => {
|
||||||
|
i += 1;
|
||||||
|
subcommands.push(GoArgs::WInc(fields[i].parse::<i32>().unwrap()));
|
||||||
|
}
|
||||||
|
"binc" => {
|
||||||
|
i += 1;
|
||||||
|
subcommands.push(GoArgs::BInc(fields[i].parse::<i32>().unwrap()));
|
||||||
|
}
|
||||||
|
"movestogo" => {
|
||||||
|
i += 1;
|
||||||
|
subcommands.push(GoArgs::MovesToGo(fields[i].parse::<i32>().unwrap()));
|
||||||
|
}
|
||||||
|
"depth" => {
|
||||||
|
i += 1;
|
||||||
|
subcommands.push(GoArgs::Depth(fields[i].parse::<i32>().unwrap()));
|
||||||
|
}
|
||||||
|
"nodes" => {
|
||||||
|
i += 1;
|
||||||
|
subcommands.push(GoArgs::Nodes(fields[i].parse::<i32>().unwrap()));
|
||||||
|
}
|
||||||
|
"mate" => {
|
||||||
|
i += 1;
|
||||||
|
subcommands.push(GoArgs::Mate(fields[i].parse::<i32>().unwrap()));
|
||||||
|
}
|
||||||
|
f => eprintln!("Unknown go subcommand: {}", f),
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue