uci: handle basic position command
This commit is contained in:
parent
d72042e455
commit
8c0a083cb0
|
@ -95,11 +95,7 @@ pub fn new_empty() -> Board {
|
|||
[SQ_E; 64]
|
||||
}
|
||||
|
||||
pub const FEN_START: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/// Generate a board from a FEN string.
|
||||
///
|
||||
/// This will only parse the first FEN field.
|
||||
/// Generate a board from a FEN placement string.
|
||||
pub fn new_from_fen(fen: &str) -> Board {
|
||||
let mut board = [SQ_E; 64];
|
||||
let mut f = 0;
|
||||
|
@ -216,6 +212,7 @@ pub fn draw(board: &Board) {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::notation;
|
||||
|
||||
#[test]
|
||||
fn test_opposite() {
|
||||
|
@ -235,7 +232,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_new_from_fen() {
|
||||
let b1 = new();
|
||||
let b2 = new_from_fen(FEN_START);
|
||||
let b2 = new_from_fen(notation::FEN_START);
|
||||
assert!(eq(&b1, &b2));
|
||||
}
|
||||
|
||||
|
|
24
src/engine.rs
Normal file
24
src/engine.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
//! Vatu engine.
|
||||
|
||||
use crate::board;
|
||||
use crate::notation;
|
||||
|
||||
pub struct Engine {
|
||||
board: board::Board,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new() -> Engine {
|
||||
Engine {
|
||||
board: board::new_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_fen(&mut self, fen: ¬ation::Fen) {
|
||||
self.set_placement(&fen.placement);
|
||||
}
|
||||
|
||||
fn set_placement(&mut self, placement: &str) {
|
||||
self.board = board::new_from_fen(placement);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
|||
|
||||
pub mod board;
|
||||
pub mod cli;
|
||||
pub mod engine;
|
||||
pub mod notation;
|
||||
pub mod rules;
|
||||
pub mod uci;
|
||||
|
||||
|
|
|
@ -1,17 +1,49 @@
|
|||
//! Functions using various notations.
|
||||
|
||||
use nom::IResult;
|
||||
pub const FEN_START: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
|
||||
|
||||
/// FEN notation for positions, split into fields.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Fen {
|
||||
placement: String,
|
||||
color: String,
|
||||
castling: String,
|
||||
en_passant: String,
|
||||
halfmove: String,
|
||||
fullmove: String,
|
||||
pub placement: String,
|
||||
pub color: String,
|
||||
pub castling: String,
|
||||
pub en_passant: String,
|
||||
pub halfmove: String,
|
||||
pub fullmove: String,
|
||||
}
|
||||
|
||||
fn parse_fen(i: &str) -> IResult<&str, Fen> {
|
||||
|
||||
pub fn parse_fen(i: &str) -> Option<Fen> {
|
||||
let fields: Vec<&str> = i.split_whitespace().collect();
|
||||
parse_fen_fields(fields)
|
||||
}
|
||||
|
||||
pub fn parse_fen_fields(fields: Vec<&str>) -> Option<Fen> {
|
||||
if fields.len() < 6 {
|
||||
return None
|
||||
}
|
||||
Some(Fen {
|
||||
placement: fields[0].to_string(),
|
||||
color: fields[1].to_string(),
|
||||
castling: fields[2].to_string(),
|
||||
en_passant: fields[3].to_string(),
|
||||
halfmove: fields[4].to_string(),
|
||||
fullmove: fields[5].to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_fen() {
|
||||
let fen_start = parse_fen(FEN_START).unwrap();
|
||||
assert_eq!(&fen_start.placement, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR");
|
||||
assert_eq!(&fen_start.color, "w");
|
||||
assert_eq!(&fen_start.castling, "KQkq");
|
||||
assert_eq!(&fen_start.en_passant, "-");
|
||||
assert_eq!(&fen_start.halfmove, "0");
|
||||
assert_eq!(&fen_start.fullmove, "1");
|
||||
}
|
||||
}
|
||||
|
|
79
src/uci.rs
79
src/uci.rs
|
@ -3,12 +3,8 @@
|
|||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use nom::IResult;
|
||||
use nom::branch::alt;
|
||||
use nom::character::is_space;
|
||||
use nom::bytes::complete::{tag, take_while};
|
||||
|
||||
use crate::board;
|
||||
use crate::engine;
|
||||
use crate::notation;
|
||||
|
||||
const VATU_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
const VATU_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
||||
|
@ -16,7 +12,7 @@ const VATU_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
|||
/// Hold some values related to UCI comms.
|
||||
pub struct Uci {
|
||||
state: State,
|
||||
board: board::Board,
|
||||
engine: engine::Engine,
|
||||
logfile: Option<fs::File>,
|
||||
}
|
||||
|
||||
|
@ -34,22 +30,29 @@ pub enum RemoteCmd {
|
|||
IsReady,
|
||||
UciNewGame,
|
||||
Stop,
|
||||
Position(String),
|
||||
Position(PositionArgs),
|
||||
Quit,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
/// Arguments for the position remote command.
|
||||
#[derive(Debug)]
|
||||
pub enum PositionArgs {
|
||||
Startpos,
|
||||
Fen(notation::Fen),
|
||||
}
|
||||
|
||||
impl Uci {
|
||||
fn listen(&mut self) {
|
||||
loop {
|
||||
if let Some(cmd) = self.receive() {
|
||||
match parse_command(&cmd) {
|
||||
Ok((_, cmd)) => {
|
||||
Some(cmd) => {
|
||||
if !self.handle_command(&cmd) {
|
||||
break
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
None => {
|
||||
self.log(format!("Unknown command: {}", cmd))
|
||||
}
|
||||
}
|
||||
|
@ -92,6 +95,7 @@ impl Uci {
|
|||
RemoteCmd::IsReady => if self.state == State::Ready { self.ready() },
|
||||
RemoteCmd::UciNewGame => if self.state == State::Ready { /* Nothing to do. */ },
|
||||
RemoteCmd::Stop => if self.state == State::Ready { /* Nothing to do. */ },
|
||||
RemoteCmd::Position(p) => if self.state == State::Ready { self.position(p) }
|
||||
RemoteCmd::Quit => return false,
|
||||
_ => { self.log(format!("Unknown command: {:?}", cmd)); }
|
||||
}
|
||||
|
@ -106,17 +110,30 @@ impl Uci {
|
|||
self.state = State::Ready;
|
||||
}
|
||||
|
||||
/// Notify interface that it is ready.
|
||||
fn ready(&mut self) {
|
||||
self.send("readyok");
|
||||
self.state = State::Ready;
|
||||
}
|
||||
|
||||
fn position(&mut self, p_args: &PositionArgs) {
|
||||
match p_args {
|
||||
PositionArgs::Fen(fen) => {
|
||||
self.engine.apply_fen(fen);
|
||||
},
|
||||
PositionArgs::Startpos => {
|
||||
let fen = notation::parse_fen(notation::FEN_START).unwrap();
|
||||
self.engine.apply_fen(&fen);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Start UCI I/O.
|
||||
/// Create a new Uci object, ready for I/O.
|
||||
pub fn start(output: Option<&str>) {
|
||||
let mut uci = Uci {
|
||||
state: State::Init,
|
||||
board: board::new_empty(),
|
||||
engine: engine::Engine::new(),
|
||||
logfile: None
|
||||
};
|
||||
if let Some(output) = output {
|
||||
|
@ -128,19 +145,29 @@ pub fn start(output: Option<&str>) {
|
|||
uci.listen();
|
||||
}
|
||||
|
||||
fn take_non_space(i: &str) -> IResult<&str, &str> {
|
||||
take_while(|c| c != ' ')(i)
|
||||
}
|
||||
|
||||
fn parse_command(i: &str) -> IResult<&str, RemoteCmd> {
|
||||
let (i, cmd) = take_non_space(i)?;
|
||||
match cmd {
|
||||
"uci" => Ok((i, RemoteCmd::Uci)),
|
||||
"isready" => Ok((i, RemoteCmd::IsReady)),
|
||||
"ucinewgame" => Ok((i, RemoteCmd::UciNewGame)),
|
||||
"stop" => Ok((i, RemoteCmd::Stop)),
|
||||
"position" => Ok((i, RemoteCmd::Position(i.trim().to_string()))),
|
||||
"quit" => Ok((i, RemoteCmd::Quit)),
|
||||
c => Ok((i, RemoteCmd::Unknown(c.to_string()))),
|
||||
fn parse_command(s: &str) -> Option<RemoteCmd> {
|
||||
let fields: Vec<&str> = s.split_whitespace().collect();
|
||||
match fields[0] {
|
||||
"uci" => Some(RemoteCmd::Uci),
|
||||
"isready" => Some(RemoteCmd::IsReady),
|
||||
"ucinewgame" => Some(RemoteCmd::UciNewGame),
|
||||
"stop" => Some(RemoteCmd::Stop),
|
||||
"position" => {
|
||||
match fields[1] {
|
||||
// Subcommand "fen" is followed by a FEN string.
|
||||
"fen" => {
|
||||
if let Some(fen) = notation::parse_fen_fields(fields[2..8].to_vec()) {
|
||||
Some(RemoteCmd::Position(PositionArgs::Fen(fen)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// Subcommand "startpos" assumes the board is a new game.
|
||||
"startpos" => Some(RemoteCmd::Position(PositionArgs::Startpos)),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
"quit" => Some(RemoteCmd::Quit),
|
||||
c => Some(RemoteCmd::Unknown(c.to_string())),
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue