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]
|
[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 placement string.
|
||||||
|
|
||||||
/// Generate a board from a FEN string.
|
|
||||||
///
|
|
||||||
/// This will only parse the first FEN field.
|
|
||||||
pub fn new_from_fen(fen: &str) -> Board {
|
pub fn new_from_fen(fen: &str) -> Board {
|
||||||
let mut board = [SQ_E; 64];
|
let mut board = [SQ_E; 64];
|
||||||
let mut f = 0;
|
let mut f = 0;
|
||||||
|
@ -216,6 +212,7 @@ pub fn draw(board: &Board) {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::notation;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_opposite() {
|
fn test_opposite() {
|
||||||
|
@ -235,7 +232,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_from_fen() {
|
fn test_new_from_fen() {
|
||||||
let b1 = new();
|
let b1 = new();
|
||||||
let b2 = new_from_fen(FEN_START);
|
let b2 = new_from_fen(notation::FEN_START);
|
||||||
assert!(eq(&b1, &b2));
|
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 board;
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
|
pub mod engine;
|
||||||
|
pub mod notation;
|
||||||
pub mod rules;
|
pub mod rules;
|
||||||
pub mod uci;
|
pub mod uci;
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,49 @@
|
||||||
//! Functions using various notations.
|
//! 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.
|
/// FEN notation for positions, split into fields.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Fen {
|
pub struct Fen {
|
||||||
placement: String,
|
pub placement: String,
|
||||||
color: String,
|
pub color: String,
|
||||||
castling: String,
|
pub castling: String,
|
||||||
en_passant: String,
|
pub en_passant: String,
|
||||||
halfmove: String,
|
pub halfmove: String,
|
||||||
fullmove: 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::fs;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use nom::IResult;
|
use crate::engine;
|
||||||
use nom::branch::alt;
|
use crate::notation;
|
||||||
use nom::character::is_space;
|
|
||||||
use nom::bytes::complete::{tag, take_while};
|
|
||||||
|
|
||||||
use crate::board;
|
|
||||||
|
|
||||||
const VATU_NAME: &str = env!("CARGO_PKG_NAME");
|
const VATU_NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
const VATU_AUTHORS: &str = env!("CARGO_PKG_AUTHORS");
|
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.
|
/// Hold some values related to UCI comms.
|
||||||
pub struct Uci {
|
pub struct Uci {
|
||||||
state: State,
|
state: State,
|
||||||
board: board::Board,
|
engine: engine::Engine,
|
||||||
logfile: Option<fs::File>,
|
logfile: Option<fs::File>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,22 +30,29 @@ pub enum RemoteCmd {
|
||||||
IsReady,
|
IsReady,
|
||||||
UciNewGame,
|
UciNewGame,
|
||||||
Stop,
|
Stop,
|
||||||
Position(String),
|
Position(PositionArgs),
|
||||||
Quit,
|
Quit,
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Arguments for the position remote command.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum PositionArgs {
|
||||||
|
Startpos,
|
||||||
|
Fen(notation::Fen),
|
||||||
|
}
|
||||||
|
|
||||||
impl Uci {
|
impl Uci {
|
||||||
fn listen(&mut self) {
|
fn listen(&mut self) {
|
||||||
loop {
|
loop {
|
||||||
if let Some(cmd) = self.receive() {
|
if let Some(cmd) = self.receive() {
|
||||||
match parse_command(&cmd) {
|
match parse_command(&cmd) {
|
||||||
Ok((_, cmd)) => {
|
Some(cmd) => {
|
||||||
if !self.handle_command(&cmd) {
|
if !self.handle_command(&cmd) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
None => {
|
||||||
self.log(format!("Unknown command: {}", cmd))
|
self.log(format!("Unknown command: {}", cmd))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,6 +95,7 @@ impl Uci {
|
||||||
RemoteCmd::IsReady => if self.state == State::Ready { self.ready() },
|
RemoteCmd::IsReady => if self.state == State::Ready { self.ready() },
|
||||||
RemoteCmd::UciNewGame => if self.state == State::Ready { /* Nothing to do. */ },
|
RemoteCmd::UciNewGame => if self.state == State::Ready { /* Nothing to do. */ },
|
||||||
RemoteCmd::Stop => 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,
|
RemoteCmd::Quit => return false,
|
||||||
_ => { self.log(format!("Unknown command: {:?}", cmd)); }
|
_ => { self.log(format!("Unknown command: {:?}", cmd)); }
|
||||||
}
|
}
|
||||||
|
@ -106,17 +110,30 @@ impl Uci {
|
||||||
self.state = State::Ready;
|
self.state = State::Ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Notify interface that it is ready.
|
||||||
fn ready(&mut self) {
|
fn ready(&mut self) {
|
||||||
self.send("readyok");
|
self.send("readyok");
|
||||||
self.state = State::Ready;
|
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>) {
|
pub fn start(output: Option<&str>) {
|
||||||
let mut uci = Uci {
|
let mut uci = Uci {
|
||||||
state: State::Init,
|
state: State::Init,
|
||||||
board: board::new_empty(),
|
engine: engine::Engine::new(),
|
||||||
logfile: None
|
logfile: None
|
||||||
};
|
};
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
|
@ -128,19 +145,29 @@ pub fn start(output: Option<&str>) {
|
||||||
uci.listen();
|
uci.listen();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_non_space(i: &str) -> IResult<&str, &str> {
|
fn parse_command(s: &str) -> Option<RemoteCmd> {
|
||||||
take_while(|c| c != ' ')(i)
|
let fields: Vec<&str> = s.split_whitespace().collect();
|
||||||
}
|
match fields[0] {
|
||||||
|
"uci" => Some(RemoteCmd::Uci),
|
||||||
fn parse_command(i: &str) -> IResult<&str, RemoteCmd> {
|
"isready" => Some(RemoteCmd::IsReady),
|
||||||
let (i, cmd) = take_non_space(i)?;
|
"ucinewgame" => Some(RemoteCmd::UciNewGame),
|
||||||
match cmd {
|
"stop" => Some(RemoteCmd::Stop),
|
||||||
"uci" => Ok((i, RemoteCmd::Uci)),
|
"position" => {
|
||||||
"isready" => Ok((i, RemoteCmd::IsReady)),
|
match fields[1] {
|
||||||
"ucinewgame" => Ok((i, RemoteCmd::UciNewGame)),
|
// Subcommand "fen" is followed by a FEN string.
|
||||||
"stop" => Ok((i, RemoteCmd::Stop)),
|
"fen" => {
|
||||||
"position" => Ok((i, RemoteCmd::Position(i.trim().to_string()))),
|
if let Some(fen) = notation::parse_fen_fields(fields[2..8].to_vec()) {
|
||||||
"quit" => Ok((i, RemoteCmd::Quit)),
|
Some(RemoteCmd::Position(PositionArgs::Fen(fen)))
|
||||||
c => Ok((i, RemoteCmd::Unknown(c.to_string()))),
|
} 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