#chess-engine #chess #uci #parser

uci-parser

Universal Chess Interface parser

2 unstable releases

new 0.2.0 Oct 17, 2024
0.1.0 Oct 3, 2024

#309 in Game dev

Download history 148/week @ 2024-09-30 12/week @ 2024-10-07 158/week @ 2024-10-14

318 downloads per month

MPL-2.0 license

84KB
1.5K SLoC

A Universal Chess Interface (UCI) Parser

This crate contains types and functions for communicating with chess engines and chess GUIs through the Universal Chess Interface (UCI) protocol.

Overview

The primary function of this crate is to provide well-typed representations for every message/command described in the UCI protocol, along with an easy-to-use API for converting between these well-typed representations and strings.

Examples

The simplest use case is parsing commands like uci, which is as easy as it sounds:

use uci_parser::UciCommand;
let cmd = UciCommand::new("uci").unwrap();
assert_eq!(cmd, UciCommand::Uci);

Commands implement FromStr, so you can call .parse():

use uci_parser::UciCommand;

// Arbitrary whitespace is handled appropriately
let cmd = "setoption    name \n    Threads value \t 16".parse::<UciCommand>();
assert!(cmd.is_ok());

assert_eq!(
    cmd.unwrap(),
    UciCommand::SetOption {
        name: "Threads".to_string(),
        value: Some("16".to_string())
    }
);

Commands that have many optional arguments, like go, implement Default so they can be parsed cleanly:

use std::time::Duration;
use uci_parser::{UciCommand, UciSearchOptions};

let cmd = "go movetime 42".parse::<UciCommand>();
assert!(cmd.is_ok());

assert_eq!(
    cmd.unwrap(),
    UciCommand::Go(UciSearchOptions {
        // Times are provided as Durations
        movetime: Some(Duration::from_millis(42)),
        ..Default::default()
    })
);

Crate Features

How edge cases should be handled is a delicate subject and the correct answer depends on the needs of your engine. Rather than enforce my own opinion on handling those edge cases, I've marked them as crate features.

  • parse-go-perft: Adds support for parsing perft <depth> as an argument to the go command.

  • parse-bench: Adds support for parsing the string bench into UciCommand::Bench.

    • This is not part of the UCI protocol, but is common among engines and very useful for engine development.
    • The arguments to bench are the same as the arguments to go, since both commands involve running searches.
  • parse-position-kiwipete: Adds support to parse kiwipete as a special argument to position (similar to startpos).

    • The "kiwipete" position is useful for debugging engines, as it is a messy position with many possible moves available.
    • If enabled, position kiwipete will be equivalent to parsing position fen r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1, so the fen field of Position will be Some(<kiwipete fen>).
  • validate-promotion-moves: Restricts the grammar when parsing moves in UCI notation to ensure that promotions are valid:

    • By default, moves are parsed with the grammar [a-h][1-8][a-h][1-8][pnbrqk], which will parse e2e4p successfully, even though it doesn't "make sense" in-game. With this feature enabled, the grammar restricts to: [a-h][1-8][a-h][1-8] | [a-h]7[a-h]8[qnrb] | [a-h]2[a-h]1[qnrb]. This means that only moves from the penultimate ranks to the final ranks will be parsed as promotions, and the only valid pieces for a promotion are a Queen, Knight, Rook, or Bishop.
  • clamp-negatives: Clamps negative numbers to 0 when parsing.

    • By default, all numbers are assumed to be positive, and the parser will fail on strings like go wtime -80. This is normally not a problem,
    • All numeric values within the UCI protocol should be positive. That said, there have been instances where some GUIs send negative move times, which could mean any variety of things. If this feature is enabled, all numeric values are clamped to at least 0. That is, if a GUI sent go movetime -42, this crate will parse that as a Duration of 0 milliseconds. It is up to your engine to determine how to respond to these situations.
  • err-on-unused-input: Causes the parser to fail if the input text was not fully consumed during parsing.

    • As per the protocol, unknown tokens encountered before a command are ignored (joho debug on parses to debug on). Unknown tokens encountered while parsing a specific command will generate errors (debug joho on fails). Unknown tokens after a command are, by default, ignored (debug on joho parses to debug on). If this feature is enabled, the parser will fail if all tokens were not consumed during parsing (debug on joho will fail).
  • types: Exposes several well-typed representations of UCI components, such as moves.

    • By default, commands like position startpos moves e2e4 will yield a list of Strings for all parsed moves, leaving you to have to re-parse them in your engine later. If this feature is enabled, any String that is parsed as a move will be converted to UciMove, which contains types representing the files, ranks, squares, and pieces involved in each move.
    • See the types module for more information.

Dependencies

~1.1–1.8MB
~36K SLoC