uci_parser/
messages.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7use std::{fmt, str::FromStr, time::Duration};
8
9use crate::{parse_uci_command, UciParseError};
10
11#[cfg(feature = "types")]
12use crate::UciMove;
13
14/// A command sent from a GUI to an engine.
15#[derive(Debug, PartialEq, Eq, Hash, Clone)]
16pub enum UciCommand {
17    /// Check if the engine supports the UCI protocol.
18    ///
19    /// # Command structure:
20    /// ```text
21    /// uci
22    /// ```
23    ///
24    /// # Protocol Description
25    ///
26    /// Tell engine to use the uci (universal chess interface).
27    /// This will be sent once as a first command after program boot to tell the
28    /// engine to switch to uci mode.
29    ///
30    /// After receiving the uci command the engine must identify itself with the `id`
31    /// command and send the `option` commands to tell the GUI which engine settings
32    /// the engine supports if any.
33    ///
34    /// After that the engine should send `uciok` to acknowledge the uci mode.
35    /// If no uciok is sent within a certain time period, the engine task will be
36    /// killed by the GUI.
37    Uci,
38
39    /// Enable/disable printing debug information.
40    ///
41    /// # Command structure:
42    /// ```text
43    /// debug [on | off]
44    /// ```
45    /// where `on = true`
46    ///
47    /// # Protocol Description
48    ///
49    /// Switch the debug mode of the engine on and off.
50    ///
51    /// In debug mode the engine should send additional infos to the GUI.
52    ///   - e.g. with the `info string` command, to help debugging.
53    ///   - e.g. the commands that the engine has received etc.
54    ///
55    /// This mode should be switched off by default and this command can be sent any
56    /// time, also when the engine is thinking.
57    Debug(bool),
58
59    /// Synchronize with the engine.
60    ///
61    /// # Command structure:
62    /// ```text
63    /// isready
64    /// ```
65    ///
66    /// # Protocol Description
67    ///
68    /// This is used to synchronize the engine with the GUI.
69    /// When the GUI has sent a command or multiple commands that can take some time
70    /// to complete, this command can be used to wait for the engine to be ready
71    /// again or to ping the engine to find out if it is still alive. E.g. this
72    /// should be sent after setting the path to the table bases as this can take
73    /// some time.
74    ///
75    /// This command is also required once before the engine is asked to do any
76    /// search to wait for the engine to finish initializing.
77    ///
78    /// This command must always be answered with `readyok` and can be sent also
79    /// when the engine is calculating in which case the engine should also
80    /// immediately answer with `readyok` without stopping the search.
81    IsReady,
82
83    /// Modify an option in the engine.
84    ///
85    /// # Command structure:
86    /// ```text
87    /// setoption name <id> [value <x>]
88    /// ```
89    ///
90    /// # Protocol Description
91    ///
92    /// This is sent to the engine when the user wants to change the internal
93    /// parameters of the engine. For the `button` type no value is needed.
94    ///
95    /// One string will be sent for each parameter and this will only be sent when
96    /// the engine is waiting. The name and value of the option in `<id>` should not
97    /// be case sensitive and can include spaces.
98    ///
99    /// The substrings `value` and `name` should be avoided in `<id>` and `<x>` to
100    /// allow unambiguous parsing, for example do not use `<name> = draw value`.
101    ///
102    /// ## Examples
103    ///
104    /// Here are some strings for the example below:
105    /// * `setoption name Nullmove value true\n`
106    /// * `setoption name Selectivity value 3\n`
107    /// * `setoption name Style value Risky\n`
108    /// * `setoption name Clear Hash\n`
109    /// * `setoption name NalimovPath value c:\chess\tb\4;c:\chess\tb\5\n`
110    SetOption {
111        /// The name of the engine parameter to modify.
112        name: String,
113
114        /// If provided, the new value for the specified parameter option.
115        value: Option<String>,
116    },
117
118    /// Register with this engine.
119    ///
120    /// # Command structure:
121    /// ```text
122    /// registration [name <name> code <code> | later]
123    /// ```
124    ///
125    /// If called with `later`, then the `name` and `code` parameters will be `None`.
126    /// Otherwise, they are likely to both be `Some`, though they are parsed
127    /// independently, and it is up to you(r engine) to determine whether your
128    /// registration process requires both values.
129    ///
130    /// # Protocol Description
131    ///
132    /// This is the command to try to register an engine or to tell the engine that
133    /// registration will be done later. This command should always be sent if the
134    /// engine has sent `registration error` at program startup.
135    ///
136    /// The following tokens are allowed:
137    /// * `later` - The user doesn't want to register the engine now.
138    /// * `name <x>` - The engine should be registered with the name `<x>`
139    /// * `code <y>` - The engine should be registered with the code `<y>`
140    ///
141    /// ## Example:
142    ///    `register later`
143    ///    `register name Stefan MK code 4359874324`
144    Register {
145        /// If provided, the name to use during registration.
146        name: Option<String>,
147
148        /// If provided, the code to use during registration.
149        code: Option<String>,
150    },
151
152    /// Tell the engine that the following commands are to take place on a new game.
153    ///
154    /// # Command structure:
155    /// ```text
156    /// ucinewgame
157    /// ```
158    ///
159    /// # Protocol Description
160    ///
161    /// This is sent to the engine when the next search (started with `position`
162    /// and `go`) will be from a different game. This can be a new game the engine
163    /// should play or a new game it should analyze but also the next position from
164    /// a test suite with positions only.
165    ///
166    /// If the GUI hasn't sent a `ucinewgame` before the first `position` command,
167    /// the engine shouldn't expect any further ucinewgame commands as the GUI is
168    /// probably not supporting the ucinewgame command. So the engine should not rely
169    /// on this command even though all new GUIs should support it.
170    ///
171    /// As the engine's reaction to `ucinewgame` can take some time the GUI should
172    /// always send `isready` after `ucinewgame` to wait for the engine to finish
173    /// its operation.
174    UciNewGame,
175
176    /// Set up the internal position for the engine.
177    ///
178    /// # Command structure:
179    /// ```text
180    /// position [fen <string> | startpos] [moves <move_1> [<move_2> ...]]
181    /// ```
182    ///
183    /// If called with `startpos`, then the `fen` field will be `None`.
184    ///
185    /// # Protocol Description
186    ///
187    /// Set up the position described in FEN string on the internal board and
188    /// play the moves on the internal chess board.
189    ///
190    /// If the game was played from the start position, the string `startpos` will
191    /// be sent.
192    ///
193    /// Note: no `new` command is needed. However, if this position is from a
194    /// different game than the last position sent to the engine, the GUI should have
195    /// sent a `ucinewgame` in between.
196    Position {
197        /// If provided, the FEN string of the position to start with.
198        ///
199        /// If `startpos` was provided, this will be `None`.
200        fen: Option<String>,
201
202        /// A list of moves to apply to the provided/default position.
203        #[cfg(feature = "types")]
204        moves: Vec<UciMove>,
205        #[cfg(not(feature = "types"))]
206        moves: Vec<String>,
207    },
208
209    /// Start a search on the engine.
210    ///
211    /// # Command structure:
212    /// ```text
213    /// go [searchmoves <move_1> [<move_2> ...]] [ponder] [wtime <x>] [btime <x>] [winc <x>] [binc <x>] [movestogo <x>] [depth <x>] [nodes <x>] [mate <x>] [movetime <x>] [infinite]
214    /// ```
215    /// If no parameters are received, this should will be treated as `go infinite`
216    /// and the `infinite` field will be set to `true`.
217    ///
218    /// # Protocol Description
219    ///
220    /// Start calculating on the current position set up with the `position` command.
221    ///
222    /// There are a number of commands that can follow this command, all will be sent
223    /// in the same string. If one command is not sent its value should be
224    /// interpreted as it would not influence the search.
225    ///
226    /// ## Arguments
227    ///
228    /// ```text
229    /// searchmoves <move_1> [<move_2> ... <move_i>]
230    /// ```
231    /// Restrict search to this moves only
232    ///
233    /// Example: After `position startpos` and `go infinite searchmoves e2e4 d2d4`
234    /// the engine should only search the two moves `e2e4` and `d2d4` in the initial
235    /// position.
236    ///
237    /// ```text
238    /// ponder
239    /// ```
240    /// Start searching in pondering mode.
241    ///
242    /// Do not exit the search in ponder mode, even if it's mate!
243    ///
244    /// This means that the last move sent in in the position string is the ponder
245    /// move. The engine can do what it wants to do, but after a `ponderhit` command
246    /// it should execute the suggested move to ponder on. This means that the ponder
247    /// move sent by the GUI can be interpreted as a recommendation about which move
248    /// to ponder. However, if the engine decides to ponder on a different move, it
249    /// should not display any mainlines as they are likely to be misinterpreted by
250    /// the GUI because the GUI expects the engine to ponder on the suggested move.
251    ///
252    /// ```text
253    /// wtime <x>
254    /// ```
255    /// White has `x` milliseconds left on the clock.
256    ///
257    /// ```text
258    /// btime <x>
259    /// ```
260    /// Black has `x` milliseconds left on the clock.
261    ///
262    /// ```text
263    /// winc <x>
264    /// ```
265    /// White increment per move in milliseconds if `x > 0`.
266    ///
267    /// ```text
268    /// binc <x>
269    /// ```
270    /// Black increment per move in milliseconds if `x > 0`.
271    ///
272    /// ```text
273    /// movestogo <x>
274    /// ```
275    /// There are `x` moves to the next time control.
276    ///
277    /// This will only be sent if `x > 0`.
278    ///
279    /// If you don't get this and get the `wtime` and `btime`, it's sudden death.
280    ///
281    /// ```text
282    /// depth <x>
283    /// ```
284    /// Search `x` plies only.
285    ///
286    /// ```text
287    /// nodes <x>
288    /// ```
289    /// Search `x` nodes only.
290    ///
291    /// ```text
292    /// mate <x>
293    /// ```
294    /// Search for a mate in `x` moves.
295    ///
296    /// ```text
297    /// movetime <x>
298    /// ```
299    /// Search exactly `x` milliseconds.
300    ///
301    /// ```text
302    /// infinite
303    /// ```
304    /// Search until the `stop` command. Do not exit the search without being told
305    /// so in this mode!
306    Go(UciSearchOptions),
307
308    /// Tell the engine to stop searching.
309    ///
310    /// # Command structure:
311    /// ```text
312    /// stop
313    /// ```
314    ///
315    /// # Protocol Description
316    ///
317    /// Stop calculating as soon as possible.
318    ///
319    /// Don't forget the `bestmove` and possibly the `ponder` token when finishing
320    /// the search.
321    Stop,
322
323    /// Tell the engine that the opponent played the pondered move.
324    ///
325    /// # Command structure:
326    /// ```text
327    /// ponderhit
328    /// ```
329    ///
330    /// # Protocol Description
331    ///
332    /// The user has played the expected move. This will be sent if the engine was
333    /// told to ponder on the same move the user has played. The engine should
334    /// continue searching but switch from pondering to normal search.
335    PonderHit,
336
337    /// Tell the engine to quit as soon as possible.
338    ///
339    /// # Command structure:
340    /// ```text
341    /// quit
342    /// ```
343    ///
344    /// This does not necessarily need to immediately exit, but should make sure it
345    /// performs any necessary clean-up before exiting.
346    ///
347    /// It is likely that you want this command to be a special case in your
348    /// engine's event handler.
349    ///
350    /// # Protocol Description
351    ///
352    /// Quit the program as soon as possible.
353    Quit,
354
355    /// Run a benchmark suite.
356    ///
357    /// ```text
358    /// bench <x>
359    /// ```
360    /// Not part of the UCI protocol, but [very common among engines](https://official-stockfish.github.io/docs/stockfish-wiki/UCI-&-Commands.html#bench).
361    ///
362    /// Used to make the engine execute a benchmark on a pre-set suite of positions.
363    #[cfg(feature = "parse-bench")]
364    Bench(UciSearchOptions),
365}
366
367impl UciCommand {
368    /// Attempt to parse `input` into a valid [`UciCommand`].
369    #[inline(always)]
370    pub fn new(input: &str) -> Result<Self, UciParseError> {
371        parse_uci_command(input)
372    }
373}
374
375impl FromStr for UciCommand {
376    type Err = UciParseError;
377    /// Alias for [`UciCommand::new`].
378    #[inline(always)]
379    fn from_str(s: &str) -> Result<Self, Self::Err> {
380        Self::new(s)
381    }
382}
383
384impl fmt::Display for UciCommand {
385    /// Formats this [`UciCommand`] using the given formatter.
386    ///
387    /// The formatted string will almost always be identical to the source it was
388    /// parsed from, with the following exceptions:
389    /// 1. Parameters to commands like [`UciCommand::Go`] will have a fixed order,
390    ///     regardless of how they were originally supplied.
391    /// 2. Leading/trailing/excessive whitespace is parsed out.
392    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
393        use UciCommand::*;
394        match self {
395            Uci => write!(f, "uci"),
396
397            Debug(status) => {
398                write!(f, "debug {}", if *status { "on" } else { "off" })
399            }
400
401            IsReady => write!(f, "isready"),
402
403            SetOption { name, value } => {
404                if let Some(value) = value {
405                    write!(f, "setoption name {name} value {value}")
406                } else {
407                    write!(f, "setoption name {name}")
408                }
409            }
410
411            Register { name, code } => match (name, code) {
412                (Some(name), Some(code)) => write!(f, "register name {name} code {code}"),
413                (Some(name), None) => write!(f, "register name {name}"),
414                (None, Some(code)) => write!(f, "register code {code}"),
415                (None, None) => write!(f, "register later"),
416            },
417
418            UciNewGame => write!(f, "ucinewgame"),
419
420            Position { fen, moves } => {
421                if let Some(fen) = fen {
422                    write!(f, "position fen {fen}")?;
423                } else {
424                    write!(f, "position startpos")?;
425                }
426
427                if !moves.is_empty() {
428                    write!(f, " moves")?;
429
430                    for mv in moves {
431                        write!(f, " {mv}")?;
432                    }
433                }
434                Ok(())
435            }
436
437            Go(options) => write!(f, "go{options}"), // The lack of space here is intentional, as the options may be empty
438
439            Stop => write!(f, "stop"),
440
441            PonderHit => write!(f, "ponderhit"),
442
443            Quit => write!(f, "quit"),
444
445            #[cfg(feature = "parse-bench")]
446            Bench(args) => write!(f, "bench{args}"), // As with `go`, the lack of space here is intentional
447        }
448    }
449}
450
451/// Represents the arguments that can be sent to your engine via the `go` command.
452#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
453pub struct UciSearchOptions {
454    /// ```text
455    /// searchmoves <move_1> [<move_2> ... <move_i>]
456    /// ```
457    ///
458    /// Restrict search to this moves only
459    ///
460    /// Example: After `position startpos` and `go infinite searchmoves e2e4 d2d4`
461    /// the engine should only search the two moves `e2e4` and `d2d4` in the initial
462    /// position.
463    #[cfg(feature = "types")]
464    pub searchmoves: Vec<UciMove>,
465
466    #[cfg(not(feature = "types"))]
467    pub searchmoves: Vec<String>,
468
469    /// ```text
470    /// ponder
471    /// ```
472    /// Start searching in pondering mode.
473    ///
474    /// Do not exit the search in ponder mode, even if it's mate!
475    ///
476    /// This means that the last move sent in in the position string is the ponder
477    /// move. The engine can do what it wants to do, but after a `ponderhit` command
478    /// it should execute the suggested move to ponder on. This means that the ponder
479    /// move sent by the GUI can be interpreted as a recommendation about which move
480    /// to ponder. However, if the engine decides to ponder on a different move, it
481    /// should not display any mainlines as they are likely to be misinterpreted by
482    /// the GUI because the GUI expects the engine to ponder on the suggested move.
483    pub ponder: bool,
484
485    /// ```text
486    /// wtime <x>
487    /// ```
488    /// White has `x` milliseconds left on the clock.
489    pub wtime: Option<Duration>,
490
491    /// ```text
492    /// btime <x>
493    /// ```
494    /// Black has `x` milliseconds left on the clock.
495    pub btime: Option<Duration>,
496
497    /// ```text
498    /// winc <x>
499    /// ```
500    /// White increment per move in milliseconds if `x > 0`.
501    pub winc: Option<Duration>,
502
503    /// ```text
504    /// binc <x>
505    /// ```
506    /// Black increment per move in milliseconds if `x > 0`.
507    pub binc: Option<Duration>,
508
509    /// ```text
510    /// movestogo <x>
511    /// ```
512    /// There are `x` moves to the next time control.
513    ///
514    /// This will only be sent if `x > 0`.
515    ///
516    /// If you don't get this and get the `wtime` and `btime`, it's sudden death.
517    pub movestogo: Option<u32>,
518
519    /// ```text
520    /// depth <x>
521    /// ```
522    /// Search `x` plies only.
523    pub depth: Option<u32>,
524
525    /// ```text
526    /// nodes <x>
527    /// ```
528    /// Search `x` nodes only.
529    pub nodes: Option<u32>,
530
531    /// ```text
532    /// mate <x>
533    /// ```
534    /// Search for a mate in `x` moves.
535    pub mate: Option<u32>,
536
537    /// ```text
538    /// movetime <x>
539    /// ```
540    /// Search exactly `x` milliseconds.
541    pub movetime: Option<Duration>,
542
543    /// ```text
544    /// infinite
545    /// ```
546    /// Search until the `stop` command. Do not exit the search without being told
547    /// so in this mode!
548    pub infinite: bool,
549
550    /// ```text
551    /// perft <x>
552    /// ```
553    /// Not part of the UCI protocol, but [very common among engines](https://github.com/official-stockfish/Stockfish/blob/d6043970bd156b1d2ab6cb51e8d5cb0c6a40797c/tests/perft.sh#L17).
554    ///
555    /// Execute a [performance test](https://www.chessprogramming.org/Perft) (perft)
556    /// on the current position at a depth of `x` plies.
557    #[cfg(feature = "parse-go-perft")]
558    pub perft: Option<u32>,
559}
560
561impl fmt::Display for UciSearchOptions {
562    /// Formats the [`UciSearchOptions`] using the given formatter.
563    ///
564    /// This will always format the fields in the order in which they are listed
565    /// [in the protocol definition](https://backscattering.de/chess/uci/#gui-go-tokens).
566    ///
567    /// `go perft` will appear last, if the `parse-go-perft` feature is enabled.
568    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
569        if !self.searchmoves.is_empty() {
570            write!(f, " searchmoves")?;
571            for mv in &self.searchmoves {
572                write!(f, " {mv}")?;
573            }
574        }
575
576        if self.ponder {
577            write!(f, " ponder")?;
578        }
579
580        if let Some(wtime) = self.wtime {
581            write!(f, " wtime {}", wtime.as_millis())?;
582        }
583
584        if let Some(btime) = self.btime {
585            write!(f, " btime {}", btime.as_millis())?;
586        }
587
588        if let Some(winc) = self.winc {
589            write!(f, " winc {}", winc.as_millis())?;
590        }
591
592        if let Some(binc) = self.binc {
593            write!(f, " binc {}", binc.as_millis())?;
594        }
595
596        if let Some(movestogo) = self.movestogo {
597            write!(f, " movestogo {movestogo}")?;
598        }
599
600        if let Some(nodes) = self.nodes {
601            write!(f, " nodes {nodes}")?;
602        }
603
604        if let Some(mate) = self.mate {
605            write!(f, " mate {mate}")?;
606        }
607
608        if let Some(movetime) = self.movetime {
609            write!(f, " movetime {}", movetime.as_millis())?;
610        }
611
612        if self.infinite {
613            write!(f, " infinite")?;
614        }
615
616        #[cfg(feature = "parse-go-perft")]
617        if let Some(perft) = self.perft {
618            write!(f, " perft {perft}")?;
619        }
620
621        Ok(())
622    }
623}
624
625/*************************************************************************************************/
626/*                                 ENGINE TO GUI COMMUNICATION                                   */
627/*************************************************************************************************/
628
629/// # Responses sent from the Engine to the GUI via `stdout`.
630///
631/// These are all the commands the interface gets from the engine.
632#[derive(Debug, Clone, PartialEq, Eq, Hash)]
633pub enum UciResponse<T = String> {
634    /// ```text
635    /// id name <x>
636    /// ```
637    Name(T),
638
639    /// ```text
640    /// id author <x>
641    /// ```
642    Author(T),
643
644    /// ```text
645    /// uciok
646    /// ```
647    UciOk,
648
649    /// ```text
650    /// readyok
651    /// ```
652    ReadyOk,
653
654    /// ```text
655    /// bestmove <move_1> [ponder <move_2>]
656    /// ```
657    ///
658    /// If the `bestmove` field is `None`, this will be printed as
659    /// `bestmove (none) [ponder <ponder>]`.
660    BestMove {
661        bestmove: Option<T>,
662        ponder: Option<T>,
663    },
664
665    /// ```text
666    /// copyprotection [checking | ok | error]
667    /// ```
668    CopyProtection(UciCheckingStatus),
669
670    /// ```text
671    /// registration [checking | ok | error]
672    /// ```
673    Registration(UciCheckingStatus),
674
675    /// ```text
676    /// info [depth <x>] [seldepth <x>] [time <x>] [nodes <x>] [pv <move_1> [<move_2> ... <move_i>]] [score [cp <x> | mate <y>] [lowerbound | upperbound]] [currmove <move>] [currmovenumber <x>] [hashfull <x>] [nps <x>] [tbhits <x>] [sbhits <x>] [cpuload <x>] [string <str>] [refutation <move_1> <move_2> [... <move_i>]] [currline [cpunr] <move_1> [... <move_i>]]
677    /// ```
678    Info(Box<UciInfo>),
679
680    /// ```text
681    /// option name <id> type <t> [default <x>] [min <x>] [max <x>] [var <x>]
682    /// ```
683    Option(UciOption<T>),
684}
685
686impl UciResponse {
687    /// Convenience wrapper for creating a [`UciResponse::Info`] variant without needing to specify a generic parameter.
688    #[must_use]
689    #[inline(always)]
690    pub fn info(info: impl Into<UciInfo>) -> Self {
691        Self::Info(Box::new(info.into()))
692    }
693
694    /// Convenience wrapper for creating a [`UciResponse::UciOk`] variant without needing to specify a generic parameter.
695    #[must_use]
696    #[inline(always)]
697    pub fn uciok() -> Self {
698        Self::UciOk
699    }
700
701    /// Convenience wrapper for creating a [`UciResponse::ReadyOk`] variant without needing to specify a generic parameter.
702    #[must_use]
703    #[inline(always)]
704    pub fn readyok() -> Self {
705        Self::ReadyOk
706    }
707}
708
709impl<T: fmt::Display> UciResponse<T> {
710    /// Convenience wrapper for creating a [`UciResponse::Info`] variant that will display as `info string <s>`.
711    #[must_use]
712    #[inline(always)]
713    pub fn info_string(s: T) -> Self {
714        let info = UciInfo::new().string(s);
715        Self::Info(Box::new(info))
716    }
717}
718
719impl<T: fmt::Display> fmt::Display for UciResponse<T> {
720    /// Responses are formatted to display appropriately according to the UCI specifications.
721    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
722        match self {
723            Self::Name(name) => write!(f, "id name {name}"),
724            Self::Author(author) => write!(f, "id author {author}"),
725            Self::UciOk => write!(f, "uciok"),
726            Self::ReadyOk => write!(f, "readyok"),
727            Self::BestMove { bestmove, ponder } => match (bestmove, ponder) {
728                (Some(b), Some(p)) => write!(f, "bestmove {b} ponder {p}"),
729                (Some(b), None) => write!(f, "bestmove {b}"),
730                (None, Some(p)) => write!(f, "bestmove (none) ponder {p}"),
731                (None, None) => write!(f, "bestmove (none)"),
732            },
733            Self::CopyProtection(status) => write!(f, "copyprotection {status}"),
734            Self::Registration(status) => write!(f, "registration {status}"),
735            Self::Info(info) => write!(f, "info{info}"), // Lack of space here is intentional
736            Self::Option(opt) => write!(f, "option {opt}"),
737        }
738    }
739}
740
741/// Represents the status of the `copyprotection` and `registration` commands.
742#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
743pub enum UciCheckingStatus {
744    /// The engine is checking the status of `copyprotection` or `registration`.
745    Checking,
746
747    /// All is well. Check was successful. No further action needed.
748    Ok,
749
750    /// An error occurred when checking `copyprotection` or `registration`
751    Error,
752}
753
754impl fmt::Display for UciCheckingStatus {
755    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
756        match self {
757            Self::Checking => write!(f, "checking"),
758            Self::Ok => write!(f, "ok"),
759            Self::Error => write!(f, "error"),
760        }
761    }
762}
763
764/// Bounds for the `score` argument of the `info` response.
765#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
766pub enum UciBound {
767    /// The score is just a lowerbound.
768    Lowerbound,
769
770    /// The score is just an upperbound.
771    Upperbound,
772}
773
774impl fmt::Display for UciBound {
775    /// Formats as either `upperbound` or `lowerbound`.
776    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777        match self {
778            Self::Lowerbound => write!(f, "lowerbound"),
779            Self::Upperbound => write!(f, "upperbound"),
780        }
781    }
782}
783
784/// Represents the type of score for the `score` argument of the `info` response.
785#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
786pub enum UciScoreType {
787    /// The score from the engine's point of view in centipawns.
788    Centipawns,
789
790    /// Mate in `<y>` moves (not plies).
791    Mate,
792}
793
794impl fmt::Display for UciScoreType {
795    /// Formats as either `cp` or `mate`.
796    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
797        match self {
798            Self::Centipawns => write!(f, "cp"),
799            Self::Mate => write!(f, "mate"),
800        }
801    }
802}
803
804/// A "score" in the game of chess.
805///
806/// Usually in the form of [centipawns](https://www.chessprogramming.org/Centipawns) or "mate in `n` moves"
807/// and may optionally be an upper/lower bound.
808#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
809pub struct UciScore {
810    /// The score value, which is either a centipawn value or moves-to-mate,
811    /// depending on the value of `score_type`.
812    pub score: i32,
813
814    /// Either `cp` or `mate`.
815    pub score_type: UciScoreType,
816
817    /// Either `lowerbound` or `upperbound`.
818    pub bound: Option<UciBound>,
819}
820
821impl UciScore {
822    /// Construct a new [`UciScore`] with the provided `score`, `score_type`, and
823    /// `bound`.
824    #[must_use]
825    #[inline(always)]
826    pub const fn new(score: i32, score_type: UciScoreType, bound: Option<UciBound>) -> Self {
827        Self {
828            score,
829            score_type,
830            bound,
831        }
832    }
833
834    /// Construct a new [`UciScore`] with the provided `score` and `score_type`
835    #[must_use]
836    #[inline(always)]
837    pub const fn new_unbounded(score: i32, score_type: UciScoreType) -> Self {
838        Self::new(score, score_type, None)
839    }
840
841    /// Construct a new [`UciScore`] with `score_type` [`UciScoreType::Centipawns`].
842    #[must_use]
843    #[inline(always)]
844    pub const fn cp(score: i32) -> Self {
845        Self::new_unbounded(score, UciScoreType::Centipawns)
846    }
847
848    /// Construct a new [`UciScore`] with `score_type` [`UciScoreType::Mate`].
849    #[must_use]
850    #[inline(always)]
851    pub const fn mate(moves_to_mate: i32) -> Self {
852        Self::new_unbounded(moves_to_mate, UciScoreType::Mate)
853    }
854
855    /// Consumes `self` and appends the provided [`UciBound`] onto `self`.
856    #[must_use]
857    #[inline(always)]
858    pub const fn with_bound(mut self, bound: UciBound) -> Self {
859        self.bound = Some(bound);
860        self
861    }
862}
863
864impl fmt::Display for UciScore {
865    /// Formats as `<cp <x> | mate <y>> [lowerbound | upperbound]`
866    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
867        if let Some(bound) = &self.bound {
868            write!(f, "{} {} {bound}", self.score_type, self.score)
869        } else {
870            write!(f, "{} {}", self.score_type, self.score)
871        }
872    }
873}
874
875/// Represents all information that can be sent with the `info` command.
876#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
877pub struct UciInfo {
878    /// ```text
879    /// depth <x>
880    /// ```
881    /// Search depth (in plies).
882    pub depth: Option<String>,
883
884    /// ```text
885    /// seldepth <x>
886    /// ```
887    /// Selective search depth (in plies),
888    ///
889    /// If the engine sends `seldepth` there must also be a `depth` present in the
890    /// same string.
891    pub seldepth: Option<String>,
892
893    /// ```text
894    /// time <x>
895    /// ```
896    /// The time searched (in ms).
897    /// This should be sent together with the `pv`.
898    pub time: Option<String>,
899
900    /// ```text
901    /// nodes <x>
902    /// ```
903    /// `<x>` nodes searched.
904    /// The engine should send this info regularly.
905    pub nodes: Option<String>,
906
907    /// ```text
908    /// pv <move_1> [<move_2> ... <move_i>]
909    /// ```
910    /// The best line found.
911    pub pv: Vec<String>,
912
913    /// ```text
914    /// multipv <num>
915    /// ```
916    /// This for the multi pv mode.
917    ///
918    /// For the best move/pv add `multipv 1` in the string when you send the pv.
919    ///
920    /// In *k*-best mode always send all k variants in k strings together.
921    pub multipv: Option<String>,
922
923    /// ```text
924    /// score [cp <x> | mate <y> | lowerbound | upperbound]
925    /// ```
926    ///
927    ///   - `cp <x>` - The score from the engine's point of view in centipawns.
928    ///   - `mate <y>` - Mate in `y` moves, not plies.
929    ///
930    /// If the engine is getting mated, use negative values for `y`.
931    ///
932    ///   - `lowerbound` - The score is just a lower bound.
933    ///   - `upperbound` - The score is just an upper bound.
934    pub score: Option<UciScore>,
935
936    /// ```text
937    /// currmove <move>
938    /// ```
939    /// Currently searching this move
940    pub currmove: Option<String>,
941
942    /// ```text
943    /// Currmovenumber <x>
944    /// ```
945    /// Currently searching move number `x`, for the first move `x` should be `1` not
946    /// `0`.
947    pub currmovenumber: Option<String>,
948
949    /// ```text
950    /// hashfull <x>
951    /// ```
952    /// The hash is `x` permill full.
953    ///
954    /// The engine should send this info regularly.
955    pub hashfull: Option<String>,
956
957    /// ```text
958    /// nps <x>
959    /// ```
960    /// `x` nodes per second searched.
961    ///
962    /// The engine should send this info regularly.
963    pub nps: Option<String>,
964
965    /// ```text
966    /// tbhits <x>
967    /// ```
968    /// `x` positions where found in the endgame table bases.
969    pub tbhits: Option<String>,
970
971    /// ```text
972    /// sbhits <x>
973    /// ```
974    /// `x` positions where found in the shredder endgame databases.
975    pub sbhits: Option<String>,
976
977    /// ```text
978    /// cpuload x
979    /// ```
980    /// The cpu usage of the engine is `x` permill.
981    pub cpuload: Option<String>,
982
983    /// ```text
984    /// string <str>
985    /// ```
986    /// Any string `str` which will be displayed be the engine.
987    ///
988    /// If there is a string command the rest of the line will be interpreted as
989    /// `str`.
990    pub string: Option<String>,
991
992    /// ```text
993    /// refutation <move_1> <move_2> ... <move_i>
994    /// ```
995    /// Move `<move_1>` is refuted by the line `<move_2> ... <move_1>`.
996    /// `i` can be any number `>= 1`.
997    ///
998    /// Example: after move `d1h5` is searched, the engine can send
999    /// `info refutation d1h5 g6h5` if `g6h5` is the best answer after
1000    /// `d1h5` or if `g6h5` refutes the move `d1h5`.
1001    ///
1002    /// If there is no refutation for `d1h5` found, the engine should just send
1003    /// `info refutation d1h5`.
1004    ///
1005    /// The engine should only send this if the option `UCI_ShowRefutations` is set
1006    /// to `true`.
1007    pub refutation: Vec<String>,
1008
1009    /// ```text
1010    /// currline <cpnunr> <move_1> [<move_2> ... <move_i>]
1011    /// ```
1012    /// This is the current line the engine is calculating. `cpunr` is the number of
1013    /// the cpu if the engine is running on more than one cpu. `cpunr = 1,2,3...`.
1014    ///
1015    /// if the engine is just using one cpu, `cpunr` can be omitted.
1016    ///
1017    /// If `cpunr` is greater than `1`, always send all *k* lines in *k* strings
1018    /// together.
1019    ///
1020    /// The engine should only send this if the option `UCI_ShowCurrLine` is set to
1021    /// `true`.
1022    pub currline: Vec<String>,
1023}
1024
1025impl UciInfo {
1026    /// Creates a new, empty, [`UciInfo`] struct.
1027    #[must_use]
1028    #[inline(always)]
1029    pub fn new() -> Self {
1030        Self::default()
1031    }
1032
1033    /// Consumes `self` and adds the provided `depth` value.
1034    #[must_use]
1035    #[inline(always)]
1036    pub fn depth(mut self, depth: impl fmt::Display) -> Self {
1037        self.depth = Some(depth.to_string());
1038        self
1039    }
1040
1041    /// Consumes `self` and adds the provided `seldepth` value.
1042    #[must_use]
1043    #[inline(always)]
1044    pub fn seldepth(mut self, seldepth: impl fmt::Display) -> Self {
1045        self.seldepth = Some(seldepth.to_string());
1046        self
1047    }
1048
1049    /// Consumes `self` and adds the provided `time` value.
1050    #[must_use]
1051    #[inline(always)]
1052    pub fn time(mut self, time: impl fmt::Display) -> Self {
1053        self.time = Some(time.to_string());
1054        self
1055    }
1056
1057    /// Consumes `self` and adds the provided `nodes` value.
1058    #[must_use]
1059    #[inline(always)]
1060    pub fn nodes(mut self, nodes: impl fmt::Display) -> Self {
1061        self.nodes = Some(nodes.to_string());
1062        self
1063    }
1064
1065    /// Consumes `self` and adds the provided `multipv` value.
1066    #[must_use]
1067    #[inline(always)]
1068    pub fn multipv(mut self, multipv: impl fmt::Display) -> Self {
1069        self.multipv = Some(multipv.to_string());
1070        self
1071    }
1072
1073    /// Consumes `self` and adds the provided `score` value.
1074    #[must_use]
1075    #[inline(always)]
1076    pub fn score(mut self, score: impl Into<UciScore>) -> Self {
1077        self.score = Some(score.into());
1078        self
1079    }
1080
1081    /// Consumes `self` and adds the provided `currmove` value.
1082    #[must_use]
1083    #[inline(always)]
1084    pub fn currmove(mut self, currmove: impl fmt::Display) -> Self {
1085        self.currmove = Some(currmove.to_string());
1086        self
1087    }
1088
1089    /// Consumes `self` and adds the provided `currmovenumber` value.
1090    #[must_use]
1091    #[inline(always)]
1092    pub fn currmovenumber(mut self, currmovenumber: impl fmt::Display) -> Self {
1093        self.currmovenumber = Some(currmovenumber.to_string());
1094        self
1095    }
1096
1097    /// Consumes `self` and adds the provided `hashfull` value.
1098    #[must_use]
1099    #[inline(always)]
1100    pub fn hashfull(mut self, hashfull: impl fmt::Display) -> Self {
1101        self.hashfull = Some(hashfull.to_string());
1102        self
1103    }
1104
1105    /// Consumes `self` and adds the provided `nps` value.
1106    #[must_use]
1107    #[inline(always)]
1108    pub fn nps(mut self, nps: impl fmt::Display) -> Self {
1109        self.nps = Some(nps.to_string());
1110        self
1111    }
1112
1113    /// Consumes `self` and adds the provided `tbhits` value.
1114    #[must_use]
1115    #[inline(always)]
1116    pub fn tbhits(mut self, tbhits: impl fmt::Display) -> Self {
1117        self.tbhits = Some(tbhits.to_string());
1118        self
1119    }
1120
1121    /// Consumes `self` and adds the provided `sbhits` value.
1122    #[must_use]
1123    #[inline(always)]
1124    pub fn sbhits(mut self, sbhits: impl fmt::Display) -> Self {
1125        self.sbhits = Some(sbhits.to_string());
1126        self
1127    }
1128
1129    /// Consumes `self` and adds the provided `cpuload` value.
1130    #[must_use]
1131    #[inline(always)]
1132    pub fn cpuload(mut self, cpuload: impl fmt::Display) -> Self {
1133        self.cpuload = Some(cpuload.to_string());
1134        self
1135    }
1136
1137    /// Consumes `self` and adds the provided `string` value.
1138    #[must_use]
1139    #[inline(always)]
1140    pub fn string(mut self, string: impl fmt::Display) -> Self {
1141        self.string = Some(string.to_string());
1142        self
1143    }
1144
1145    /// Consumes `self` and adds the provided `pv` value.
1146    #[must_use]
1147    #[inline(always)]
1148    pub fn pv<T: fmt::Display>(mut self, pv: impl IntoIterator<Item = T>) -> Self {
1149        self.pv = pv.into_iter().map(|x| x.to_string()).collect();
1150        self
1151    }
1152
1153    /// Consumes `self` and adds the provided `refutation` value.
1154    #[must_use]
1155    #[inline(always)]
1156    pub fn refutation<T: fmt::Display>(mut self, refutation: impl IntoIterator<Item = T>) -> Self {
1157        self.refutation = refutation.into_iter().map(|x| x.to_string()).collect();
1158        self
1159    }
1160
1161    /// Consumes `self` and adds the provided `currline` value.
1162    #[must_use]
1163    #[inline(always)]
1164    pub fn currline<T: fmt::Display>(mut self, currline: impl IntoIterator<Item = T>) -> Self {
1165        self.currline = currline.into_iter().map(|x| x.to_string()).collect();
1166        self
1167    }
1168}
1169
1170impl fmt::Display for UciInfo {
1171    /// An info command will only display data that it has.
1172    ///
1173    /// Any `None` fields are not displayed.
1174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1175        if let Some(x) = &self.depth {
1176            write!(f, " depth {x}")?;
1177        }
1178        if let Some(x) = &self.seldepth {
1179            write!(f, " seldepth {x}")?
1180        }
1181        if let Some(x) = &self.time {
1182            write!(f, " time {x}")?;
1183        }
1184        if let Some(x) = &self.nodes {
1185            write!(f, " nodes {x}")?;
1186        }
1187        if let Some(x) = &self.multipv {
1188            write!(f, " multipv {x}")?;
1189        }
1190        if let Some(x) = &self.score {
1191            write!(f, " score {x}")?;
1192        }
1193        if let Some(x) = &self.currmove {
1194            write!(f, " currmove {x}")?;
1195        }
1196        if let Some(x) = &self.currmovenumber {
1197            write!(f, " currmovenumber {x}")?;
1198        }
1199        if let Some(x) = &self.hashfull {
1200            write!(f, " hashfull {x}")?;
1201        }
1202        if let Some(x) = &self.nps {
1203            write!(f, " nps {x}")?;
1204        }
1205        if let Some(x) = &self.tbhits {
1206            write!(f, " tbhits {x}")?;
1207        }
1208        if let Some(x) = &self.sbhits {
1209            write!(f, " sbhits {x}")?;
1210        }
1211        if let Some(x) = &self.cpuload {
1212            write!(f, " cpuload {x}")?;
1213        }
1214        if let Some(x) = &self.string {
1215            write!(f, " string {x}")?;
1216        }
1217        if !self.refutation.is_empty() {
1218            write!(f, " refutation {}", self.refutation.join(" "))?;
1219        }
1220        if !self.currline.is_empty() {
1221            write!(f, " currline {}", self.currline.join(" "))?;
1222        }
1223        if !self.pv.is_empty() {
1224            write!(f, " pv {}", self.pv.join(" "))?;
1225        }
1226        Ok(())
1227    }
1228}
1229
1230/// Represents a UCI-compatible option that can be modified for your Engine.
1231#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1232pub struct UciOption<T = String, INT = i32> {
1233    /// Name of the option.
1234    pub name: T,
1235
1236    /// What type of option it is.
1237    pub opt_type: UciOptionType<INT>,
1238}
1239
1240impl<T, INT> UciOption<T, INT> {
1241    /// Create a new [`UciOption`] with the provided name and type.
1242    #[must_use]
1243    #[inline(always)]
1244    pub fn new(name: impl Into<T>, opt_type: UciOptionType<INT>) -> Self {
1245        Self {
1246            name: name.into(),
1247            opt_type,
1248        }
1249    }
1250
1251    /// Create a new [`UciOption`] of type [`UciOptionType::Check`].
1252    #[must_use]
1253    #[inline(always)]
1254    pub fn check(name: impl Into<T>, default: impl Into<bool>) -> Self {
1255        Self::new(
1256            name,
1257            UciOptionType::Check {
1258                default: default.into(),
1259            },
1260        )
1261    }
1262
1263    /// Create a new [`UciOption`] of type [`UciOptionType::Spin`].
1264    #[must_use]
1265    #[inline(always)]
1266    pub fn spin(
1267        name: impl Into<T>,
1268        default: impl Into<INT>,
1269        min: impl Into<INT>,
1270        max: impl Into<INT>,
1271    ) -> Self {
1272        Self::new(
1273            name,
1274            UciOptionType::Spin {
1275                default: default.into(),
1276                min: min.into(),
1277                max: max.into(),
1278            },
1279        )
1280    }
1281
1282    /// Create a new [`UciOption`] of type [`UciOptionType::Combo`].
1283    #[must_use]
1284    #[inline(always)]
1285    pub fn combo<S: fmt::Display>(
1286        name: impl Into<T>,
1287        default: S,
1288        vars: impl IntoIterator<Item = S>,
1289    ) -> Self {
1290        Self::new(
1291            name,
1292            UciOptionType::Combo {
1293                default: default.to_string(),
1294                vars: vars.into_iter().map(|s| s.to_string()).collect(),
1295            },
1296        )
1297    }
1298
1299    /// Create a new [`UciOption`] of type [`UciOptionType::Button`].
1300    #[must_use]
1301    #[inline(always)]
1302    pub fn button(name: impl Into<T>) -> Self {
1303        Self::new(name, UciOptionType::Button)
1304    }
1305
1306    /// Create a new [`UciOption`] of type [`UciOptionType::String`].
1307    #[must_use]
1308    #[inline(always)]
1309    pub fn string(name: impl Into<T>, default: impl fmt::Display) -> Self {
1310        Self::new(
1311            name,
1312            UciOptionType::String {
1313                default: default.to_string(),
1314            },
1315        )
1316    }
1317}
1318
1319impl<T: fmt::Display> fmt::Display for UciOption<T> {
1320    /// An option is displayed as `name <name> type <type>`.
1321    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1322        write!(f, "name {} type {}", self.name, self.opt_type)
1323    }
1324}
1325
1326/// Represents the type of UCI-compatible options your engine can expose to the GUI.
1327#[derive(Clone, PartialEq, Eq, Debug, Hash)]
1328pub enum UciOptionType<INT = i32> {
1329    ///```text
1330    /// check
1331    /// ```
1332    /// A checkbox that can either be `true` or `false`.
1333    Check { default: bool },
1334
1335    ///```text
1336    /// spin
1337    /// ```
1338    /// A spin wheel that can be an integer in a certain range.
1339    Spin { default: INT, min: INT, max: INT },
1340
1341    ///```text
1342    /// combo
1343    /// ```
1344    /// A combo box that can have different predefined strings as a value.
1345    Combo { default: String, vars: Vec<String> },
1346
1347    ///```text
1348    /// button
1349    /// ```
1350    /// A button that can be pressed to send a command to the engine.
1351    Button,
1352
1353    ///```text
1354    /// string
1355    /// ```
1356    /// A text field that has a string as a value
1357    ///
1358    /// An empty string has the value `<empty>`
1359    String { default: String },
1360}
1361
1362impl<T: fmt::Display> fmt::Display for UciOptionType<T> {
1363    /// Option types are displayed like [these examples](https://backscattering.de/chess/uci/#engine-option-examples).
1364    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1365        match self {
1366            UciOptionType::Check { default } => write!(f, "check default {default}"),
1367
1368            UciOptionType::Spin { default, min, max } => {
1369                write!(f, "spin default {default} min {min} max {max}")
1370            }
1371
1372            UciOptionType::Combo { default, vars } => {
1373                write!(f, "combo default {default}")?;
1374                for var in vars {
1375                    write!(f, " var {var}")?;
1376                }
1377                Ok(())
1378            }
1379
1380            UciOptionType::Button => write!(f, "button"),
1381
1382            UciOptionType::String { default } => write!(f, "string default {default}"),
1383        }
1384    }
1385}