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}