From 5751215ffa8f48a2b109c10f6c6a4dcb9debc3e2 Mon Sep 17 00:00:00 2001 From: dogeystamp Date: Sat, 26 Oct 2024 18:25:07 -0400 Subject: [PATCH] feat: uci engine interface --- Cargo.toml | 2 + src/bin/engine.rs | 133 ++++++++++++++++++++++++++++++++++++++++++++++ src/movegen.rs | 2 +- 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/bin/engine.rs diff --git a/Cargo.toml b/Cargo.toml index 723e31f..954df3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" license = "GPL-3.0-or-later" +default-run = "engine" + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/src/bin/engine.rs b/src/bin/engine.rs new file mode 100644 index 0000000..189b764 --- /dev/null +++ b/src/bin/engine.rs @@ -0,0 +1,133 @@ +/* + +This file is part of chess_inator. + +chess_inator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. + +chess_inator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with chess_inator. If not, see https://www.gnu.org/licenses/. + +Copyright © 2024 dogeystamp +*/ + +//! Main UCI engine binary. + +use chess_inator::fen::FromFen; +use chess_inator::movegen::{FromUCIAlgebraic, Move, MoveGen, MoveGenType, ToUCIAlgebraic}; +use chess_inator::Board; +use std::io; + +/// UCI protocol says to ignore any unknown words. +/// +/// This macro exists to avoid copy-pasting this explanation everywhere. +macro_rules! ignore { + () => { + continue + }; +} + +/// UCI engine metadata query. +fn cmd_uci() -> String { + let str = "id name chess_inator\n\ + id author dogeystamp\n\ + uciok"; + str.into() +} + +/// Parse the `moves` after setting an initial position. +fn cmd_position_moves(mut tokens: std::str::SplitWhitespace<'_>, mut board: Board) -> Board { + while let Some(token) = tokens.next() { + match token { + "moves" => { + for mv in tokens.by_ref() { + let mv = Move::from_uci_algebraic(mv).unwrap(); + let _ = mv.make(&mut board); + } + } + _ => ignore!(), + } + } + + board +} + +/// Sets the position. +fn cmd_position(mut tokens: std::str::SplitWhitespace<'_>) -> Board { + while let Some(token) = tokens.next() { + match token { + "fen" => { + let mut fen = String::with_capacity(64); + // fen is 6 whitespace-delimited fields + for i in 0..6 { + fen.push_str(tokens.next().expect("FEN missing fields")); + if i < 5 { + fen.push(' ') + } + } + + let board = Board::from_fen(&fen) + .unwrap_or_else(|e| panic!("failed to parse fen '{fen}': {e:?}")); + let board = cmd_position_moves(tokens, board); + + return board; + } + "startpos" => { + let board = Board::starting_pos(); + let board = cmd_position_moves(tokens, board); + + return board; + } + _ => ignore!(), + } + } + + panic!("position command was empty") +} + +/// Play the game. +fn cmd_go(mut _tokens: std::str::SplitWhitespace<'_>, board: &mut Board) { + let mvs: Vec<_> = board.gen_moves(MoveGenType::Legal).into_iter().collect(); + let chosen = mvs.first(); + match chosen { + Some(mv) => println!("bestmove {}", mv.to_uci_algebraic()), + None => println!("bestmove 0000"), + } +} + +fn main() { + let stdin = io::stdin(); + + let mut board = Board::starting_pos(); + + loop { + let mut line = String::new(); + stdin.read_line(&mut line).unwrap(); + let mut tokens = line.split_whitespace(); + while let Some(token) = tokens.next() { + match token { + "uci" => { + println!("{}", cmd_uci()); + } + "isready" => { + println!("readyok"); + } + "ucinewgame" => { + board = Board::starting_pos(); + } + "quit" => { + return; + } + "position" => { + board = cmd_position(tokens); + } + "go" => { + cmd_go(tokens, &mut board); + } + _ => ignore!(), + } + + break; + } + } +} diff --git a/src/movegen.rs b/src/movegen.rs index 8ccc042..5cc22d2 100644 --- a/src/movegen.rs +++ b/src/movegen.rs @@ -168,7 +168,7 @@ pub struct Move { impl Move { /// Apply move to a position. - fn make(self, pos: &mut Board) -> AntiMove { + pub fn make(self, pos: &mut Board) -> AntiMove { let mut anti_move = AntiMove { dest: self.dest, src: self.src,