diff --git a/src/uci.rs b/src/uci.rs index 2ffd859..f11042c 100644 --- a/src/uci.rs +++ b/src/uci.rs @@ -1,25 +1,62 @@ -use std::str::SplitWhitespace; +use std::{ + io::{self, BufRead, Write}, + str::SplitWhitespace, +}; use crate::{game::Game, r#move::Move, search::search}; pub enum Command { Uci, IsReady, - UciNewGame, - Position(String), + Position, Go, - Stop, Quit, } +fn parse_command(parts: &mut SplitWhitespace) -> Result { + match parts.next() { + Some("uci") => Ok(Command::Uci), + Some("isready") => Ok(Command::IsReady), + Some("position") => Ok(Command::Position), + Some("go") => Ok(Command::Go), + Some("quit") => Ok(Command::Quit), + _ => Err("Unrecognised command".to_string()), + } +} + pub enum Response { - Id(String), UciOk, ReadyOk, BestMove(String), Info(String), } +fn write_response(handle_out: &mut impl Write, response: Response) -> Result<(), String> { + let output = match response { + Response::UciOk => "uciok".to_string(), + Response::ReadyOk => "readyok".to_string(), + Response::BestMove(best_move) => format!("bestmove {}", best_move), + Response::Info(info) => info, + }; + + writeln!(handle_out, "{}", output).map_err(|e| e.to_string())?; + handle_out.flush().map_err(|e| e.to_string())?; + Ok(()) +} + +use std::fmt; + +impl fmt::Display for Response { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UciOk => write!(f, "uciok"), + Self::ReadyOk => write!(f, "readyok"), + Self::BestMove(best_move) => write!(f, "bestmove {}", best_move), + Self::Info(info) => write!(f, "{}", info), + } + } +} + struct SearchParameters { movetime: Option, depth: Option, @@ -46,7 +83,7 @@ impl Default for SearchParameters { fn default() -> Self { Self::new() } - } +} pub fn uci_position(mut position: SplitWhitespace) -> Result { let state = position.next().ok_or("Expected startpos or fen")?; @@ -66,7 +103,7 @@ pub fn uci_position(mut position: SplitWhitespace) -> Result { const MAX_DEPTH: u8 = 5; -pub fn uci_go(mut go: SplitWhitespace, game: &mut Game) -> Response { +pub fn uci_go(mut go: SplitWhitespace, game: &mut Game) -> Result { let mut params = SearchParameters::new(); while let Some(subcommand) = go.next() { match subcommand { @@ -75,9 +112,65 @@ pub fn uci_go(mut go: SplitWhitespace, game: &mut Game) -> Response { _ => (), } } - search(game, params.depth.unwrap_or(MAX_DEPTH)); - Response::BestMove(String::from("value")) + + Ok(Move::new(0, 0)) +} + +struct UciGame { + game: Option, +} + +impl UciGame { + const fn new() -> Self { + Self { game: None } + } + + fn set_game_state(&mut self, game: Game) { + self.game = Some(game); + } +} + +impl Default for UciGame { + fn default() -> Self { + Self::new() + } +} + +pub fn uci_loop() -> Result<(), String> { + let stdin = io::stdin(); + let stdout = io::stdout(); + let handle_in = stdin.lock(); + let mut handle_out = stdout.lock(); + let mut uci_game = UciGame::new(); + + for line in handle_in.lines() { + let line_str = line.unwrap_or_else(|_| "quit".to_string()); + let mut parts = line_str.split_whitespace(); + let command = parse_command(&mut parts)?; + let response = match command { + Command::Uci => Response::UciOk, + Command::IsReady => Response::ReadyOk, + Command::Position => { + uci_game.set_game_state(uci_position(parts)?); + Response::Info("Initialized position.".to_string()) + } + Command::Go => { + if let Some(ref mut game) = uci_game.game { + let best_move = uci_go(parts, game)?; + Response::BestMove(best_move.parse_into_str()) + } else { + Response::Info("Going!".to_string()) + } + } + Command::Quit => break, + }; + + write_response(&mut handle_out, response)?; + handle_out.flush().map_err(|e| e.to_string())?; + } + + Ok(()) } #[cfg(test)] @@ -90,8 +183,20 @@ mod tests { fn test_uci_position() -> Result<(), String> { let mut _game = from_fen(FEN[0])?; - // check that the board state is as expected after using uci position and compare - // with a separate fen that created the update instance by itself + Ok(()) + } + + #[test] + fn test_uci_go() -> Result<(), String> { + let mut _game = from_fen(FEN[0])?; + + Ok(()) + } + + #[test] + fn test_uci_loop() -> Result<(), String> { + let mut _game = from_fen(FEN[0])?; + Ok(()) } }