vampirc_uci/
uci.rs

1//! The `uci` module contains the definitions that represent UCI protocol messages.
2//!
3//! Usually, these messages will be obtained by calling the `parse` method of the `parser` module, but you can always
4//! construct them in code and then print them to the standard output to communicate with the GUI.
5
6
7use std::fmt::{Display, Error as FmtError, Formatter, Result as FmtResult};
8#[cfg(not(feature = "chess"))]
9use std::str::FromStr;
10
11#[cfg(feature = "chess")]
12use chess::ChessMove;
13use chrono::Duration;
14use pest::error::Error as PestError;
15
16use crate::parser::Rule;
17
18/// Specifies whether a message is engine- or GUI-bound.
19#[derive(Copy, Clone, Eq, PartialEq, Debug)]
20pub enum CommunicationDirection {
21    /// An engine-bound message.
22    GuiToEngine,
23
24    /// A GUI-bound message.
25    EngineToGui,
26}
27
28pub trait Serializable: Display {
29    fn serialize(&self) -> String;
30}
31
32/// An enumeration type containing representations for all messages supported by the UCI protocol.
33#[derive(Clone, Eq, PartialEq, Debug, Hash)]
34pub enum UciMessage {
35    /// The `uci` engine-bound message.
36    Uci,
37
38    /// The `debug` engine-bound message. Its internal property specifies whether debug mode should be enabled (`true`),
39    /// or disabled (`false`).
40    Debug(bool),
41
42    /// The `isready` engine-bound message.
43    IsReady,
44
45    /// The `register` engine-bound message.
46    Register {
47        /// The `register later` engine-bound message.
48        later: bool,
49
50        /// The name part of the `register <code> <name>` engine-bound message.
51        name: Option<String>,
52
53        /// The code part of the `register <code> <name>` engine-bound message.
54        code: Option<String>,
55    },
56
57    /// The `position` engine-bound message.
58    Position {
59        /// If `true`, it denotes the starting chess position. Generally, if this property is `true`, then the value of
60        /// the `fen` property will be `None`.
61        startpos: bool,
62
63        /// The [FEN format](https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation) representation of a chess
64        /// position.
65        fen: Option<UciFen>,
66
67        /// A list of moves to apply to the position.
68        #[cfg(not(feature = "chess"))]
69        moves: Vec<UciMove>,
70
71        /// A list of moves to apply to the position.
72        #[cfg(feature = "chess")]
73        moves: Vec<ChessMove>,
74    },
75
76    /// The `setoption` engine-bound message.
77    SetOption {
78        /// The name of the option to set.
79        name: String,
80
81        /// The value of the option to set. If the option has no value, this should be `None`.
82        value: Option<String>,
83    },
84
85    /// The `ucinewgame` engine-bound message.
86    UciNewGame,
87
88    /// The `stop` engine-bound message.
89    Stop,
90
91    /// The `ponderhit` engine-bound message.
92    PonderHit,
93
94    /// The `quit` engine-bound message.
95    Quit,
96
97    /// The `go` engine-bound message.
98    Go {
99        /// Time-control-related `go` parameters (sub-commands).
100        time_control: Option<UciTimeControl>,
101
102        /// Search-related `go` parameters (sub-commands).
103        search_control: Option<UciSearchControl>,
104    },
105
106    // From this point on we have client-bound messages
107
108    /// The `id` GUI-bound message.
109    Id {
110        /// The name of the engine, possibly including the version.
111        name: Option<String>,
112
113        /// The name of the author of the engine.
114        author: Option<String>,
115    },
116
117    /// The `uciok` GUI-bound message.
118    UciOk,
119
120    /// The `readyok` GUI-bound message.
121    ReadyOk,
122
123    /// The `bestmove` GUI-bound message.
124    BestMove {
125        /// The move the engine thinks is the best one in the position.
126        #[cfg(not(feature = "chess"))]
127        best_move: UciMove,
128
129        /// The move the engine thinks is the best one in the position.
130        #[cfg(feature = "chess")]
131        best_move: ChessMove,
132
133        /// The move the engine would like to ponder on.
134        #[cfg(not(feature = "chess"))]
135        ponder: Option<UciMove>,
136
137        /// The move the engine would like to ponder on.
138        #[cfg(feature = "chess")]
139        ponder: Option<ChessMove>,
140    },
141
142    /// The `copyprotection` GUI-bound message.
143    CopyProtection(ProtectionState),
144
145    /// The `registration` GUI-bound message.
146    Registration(ProtectionState),
147
148    /// The `option` GUI-bound message.
149    Option(UciOptionConfig),
150
151    /// The `info` GUI-bound message.
152    Info(Vec<UciInfoAttribute>),
153
154    /// Indicating unknown message.
155    Unknown(String, Option<PestError<Rule>>)
156}
157
158impl UciMessage {
159    /// Constructs a `register later` [UciMessage::Register](enum.UciMessage.html#variant.Register)  message.
160    pub fn register_later() -> UciMessage {
161        UciMessage::Register {
162            later: true,
163            name: None,
164            code: None,
165        }
166    }
167
168    /// Constructs a `register <code> <name>` [UciMessage::Register](enum.UciMessage.html#variant.Register) message.
169    pub fn register_code(name: &str, code: &str) -> UciMessage {
170        UciMessage::Register {
171            later: false,
172            name: Some(name.to_string()),
173            code: Some(code.to_string()),
174        }
175    }
176
177    /// Constructs an empty [UciMessage::Register](enum.UciMessage.html#variant.Go) message.
178    pub fn go() -> UciMessage {
179        UciMessage::Go {
180            search_control: None,
181            time_control: None,
182        }
183    }
184
185    /// Construct a `go ponder` [UciMessage::Register](enum.UciMessage.html#variant.Go) message.
186    pub fn go_ponder() -> UciMessage {
187        UciMessage::Go {
188            search_control: None,
189            time_control: Some(UciTimeControl::Ponder),
190        }
191    }
192
193    /// Constructs a `go infinite` [UciMessage::Register](enum.UciMessage.html#variant.Go) message.
194    pub fn go_infinite() -> UciMessage {
195        UciMessage::Go {
196            search_control: None,
197            time_control: Some(UciTimeControl::Infinite)
198        }
199    }
200
201    /// Constructs a `go movetime <milliseconds>` [UciMessage::Register](enum.UciMessage.html#variant.Go) message, with
202    /// `milliseconds` as the argument.
203    pub fn go_movetime(milliseconds: Duration) -> UciMessage {
204        UciMessage::Go {
205            search_control: None,
206            time_control: Some(UciTimeControl::MoveTime(milliseconds)),
207        }
208    }
209
210    /// Constructs an `id <name>` GUI-bound message.
211    pub fn id_name(name: &str) -> UciMessage {
212        UciMessage::Id {
213            name: Some(name.to_string()),
214            author: None,
215        }
216    }
217
218    /// Constructs an `id <name>` GUI-bound message.
219    pub fn id_author(author: &str) -> UciMessage {
220        UciMessage::Id {
221            name: None,
222            author: Some(author.to_string()),
223        }
224    }
225
226    /// Constructs a `bestmove` GUI-bound message without the ponder move.
227    #[cfg(not(feature = "chess"))]
228    pub fn best_move(best_move: UciMove) -> UciMessage {
229        UciMessage::BestMove {
230            best_move,
231            ponder: None,
232        }
233    }
234
235    /// Constructs a `bestmove` GUI-bound message _with_ the ponder move.
236    #[cfg(not(feature = "chess"))]
237    pub fn best_move_with_ponder(best_move: UciMove, ponder: UciMove) -> UciMessage {
238        UciMessage::BestMove {
239            best_move,
240            ponder: Some(ponder),
241        }
242    }
243
244    /// Constructs a `bestmove` GUI-bound message without the ponder move.
245    #[cfg(feature = "chess")]
246    pub fn best_move(best_move: ChessMove) -> UciMessage {
247        UciMessage::BestMove {
248            best_move,
249            ponder: None,
250        }
251    }
252
253    /// Constructs a `bestmove` GUI-bound message _with_ the ponder move.
254    #[cfg(feature = "chess")]
255    pub fn best_move_with_ponder(best_move: ChessMove, ponder: ChessMove) -> UciMessage {
256        UciMessage::BestMove {
257            best_move,
258            ponder: Some(ponder),
259        }
260    }
261
262    /// Constructs an `info string ...` message.
263    pub fn info_string(s: String) -> UciMessage {
264        UciMessage::Info(vec![UciInfoAttribute::String(s)])
265    }
266
267    /// Returns whether the command was meant for the engine or for the GUI.
268    pub fn direction(&self) -> CommunicationDirection {
269        match self {
270            UciMessage::Uci |
271            UciMessage::Debug(..) |
272            UciMessage::IsReady |
273            UciMessage::Register { .. } |
274            UciMessage::Position { .. } |
275            UciMessage::SetOption { .. } |
276            UciMessage::UciNewGame |
277            UciMessage::Stop |
278            UciMessage::PonderHit |
279            UciMessage::Quit |
280            UciMessage::Go { .. } => CommunicationDirection::GuiToEngine,
281            _ => CommunicationDirection::EngineToGui
282        }
283    }
284
285    /// If this `UciMessage` is a `UciMessage::SetOption` and the value of that option is a `bool`, this method returns
286    /// the `bool` value, otherwise it returns `None`.
287    pub fn as_bool(&self) -> Option<bool> {
288        match self {
289            UciMessage::SetOption { value, .. } => {
290                if let Some(val) = value {
291                    let pr = str::parse(val.as_str());
292                    if pr.is_ok() {
293                        return Some(pr.unwrap());
294                    }
295                }
296
297                None
298            }
299            _ => None
300        }
301    }
302
303    /// If this `UciMessage` is a `UciMessage::SetOption` and the value of that option is an integer, this method
304    /// returns the `i32` value of the integer, otherwise it returns `None`.
305    pub fn as_i32(&self) -> Option<i32> {
306        match self {
307            UciMessage::SetOption { value, .. } => {
308                if let Some(val) = value {
309                    let pr = str::parse(val.as_str());
310                    if pr.is_ok() {
311                        return Some(pr.unwrap());
312                    }
313                }
314
315                None
316            }
317            _ => None
318        }
319    }
320
321    /// Return `true` if this `UciMessage` is of variant `UnknownMessage`.
322    pub fn is_unknown(&self) -> bool {
323        match self {
324            UciMessage::Unknown(..) => true,
325            _ => false
326        }
327    }
328}
329
330impl Display for UciMessage {
331    fn fmt(&self, f: &mut Formatter) -> FmtResult {
332        write!(f, "{}", self.serialize())
333    }
334}
335
336impl Serializable for UciMessage {
337    /// Serializes the command into a String.
338    ///
339    /// # Examples
340    /// ```
341    /// use vampirc_uci::{UciMessage, Serializable};
342    ///
343    /// println!("{}", UciMessage::Uci.serialize()); // Should print `uci`.
344    /// ```
345    fn serialize(&self) -> String {
346        match self {
347            UciMessage::Debug(on) => if *on { String::from("debug on") } else { String::from("debug off") },
348            UciMessage::Register { later, name, code } => {
349                if *later {
350                    return String::from("register later");
351                }
352
353                let mut s: String = String::from("register ");
354                if let Some(n) = name {
355                    s += format!("name {}", *n).as_str();
356                    if code.is_some() {
357                        s += " ";
358                    }
359                }
360                if let Some(c) = code {
361                    s += format!("code {}", *c).as_str();
362                }
363
364                s
365            }
366            UciMessage::Position { startpos, fen, moves } => {
367                let mut s = String::from("position ");
368                if *startpos {
369                    s += String::from("startpos").as_str();
370                } else if let Some(uci_fen) = fen {
371                    s += format!("fen {}", uci_fen.as_str()).as_str();
372                }
373
374                if moves.len() > 0 {
375                    s += String::from(" moves").as_str();
376
377                    for m in moves {
378                        s += format!(" {}", *m).as_str();
379                    }
380                }
381
382                s
383            }
384            UciMessage::SetOption { name, value } => {
385                let mut s: String = String::from(format!("setoption name {}", name));
386
387                if let Some(val) = value {
388                    if val.len() == 0 {
389                        s += " value <empty>";
390                    } else {
391                        s += format!(" value {}", *val).as_str();
392                    }
393                } else {
394                    s += " value <empty>";
395                }
396
397                s
398            }
399            UciMessage::Go { time_control, search_control } => {
400                let mut s = String::from("go ");
401
402                if let Some(tc) = time_control {
403                    match tc {
404                        UciTimeControl::Infinite => { s += "infinite "; }
405                        UciTimeControl::Ponder => { s += "ponder "; }
406                        UciTimeControl::MoveTime(duration) => {
407                            s += format!("movetime {} ", duration.num_milliseconds()).as_str();
408                        }
409                        UciTimeControl::TimeLeft { white_time, black_time, white_increment, black_increment, moves_to_go } => {
410                            if let Some(wt) = white_time {
411                                s += format!("wtime {} ", wt.num_milliseconds()).as_str();
412                            }
413
414                            if let Some(bt) = black_time {
415                                s += format!("btime {} ", bt.num_milliseconds()).as_str();
416                            }
417
418                            if let Some(wi) = white_increment {
419                                s += format!("winc {} ", wi.num_milliseconds()).as_str();
420                            }
421
422                            if let Some(bi) = black_increment {
423                                s += format!("binc {} ", bi.num_milliseconds()).as_str();
424                            }
425
426                            if let Some(mtg) = moves_to_go {
427                                s += format!("movestogo {} ", *mtg).as_str();
428                            }
429                        }
430                    }
431                }
432
433                if let Some(sc) = search_control {
434                    if let Some(depth) = sc.depth {
435                        s += format!("depth {} ", depth).as_str();
436                    }
437
438                    if let Some(nodes) = sc.nodes {
439                        s += format!("nodes {} ", nodes).as_str();
440                    }
441
442                    if let Some(mate) = sc.mate {
443                        s += format!("mate {} ", mate).as_str();
444                    }
445
446                    if !sc.search_moves.is_empty() {
447                        s += " searchmoves ";
448                        for m in &sc.search_moves {
449                            s += format!("{} ", m).as_str();
450                        }
451                    }
452                }
453
454                s
455            }
456            UciMessage::Uci => "uci".to_string(),
457            UciMessage::IsReady => "isready".to_string(),
458            UciMessage::UciNewGame => "ucinewgame".to_string(),
459            UciMessage::Stop => "stop".to_string(),
460            UciMessage::PonderHit => "ponderhit".to_string(),
461            UciMessage::Quit => "quit".to_string(),
462
463
464            // GUI-bound from this point on
465
466            UciMessage::Id { name, author } => {
467                let mut s = String::from("id ");
468                if let Some(n) = name {
469                    s += "name ";
470                    s += n;
471                } else if let Some(a) = author {
472                    s += "author ";
473                    s += a;
474                }
475
476                s
477            },
478            UciMessage::UciOk => String::from("uciok"),
479            UciMessage::ReadyOk => String::from("readyok"),
480            UciMessage::BestMove { best_move, ponder } => {
481                let mut s = String::from(format!("bestmove {}", *best_move));
482
483                if let Some(p) = ponder {
484                    s += format!(" ponder {}", *p).as_str();
485                }
486
487                s
488            },
489            UciMessage::CopyProtection(cp_state) | UciMessage::Registration(cp_state) => {
490                let mut s = match self {
491                    UciMessage::CopyProtection(..) => String::from("copyprotection "),
492                    UciMessage::Registration(..) => String::from("registration "),
493                    _ => unreachable!()
494                };
495
496                match cp_state {
497                    ProtectionState::Checking => s += "checking",
498                    ProtectionState::Ok => s += "ok",
499                    ProtectionState::Error => s += "error",
500                }
501
502                s
503            },
504            UciMessage::Option(config) => config.serialize(),
505            UciMessage::Info(info_line) => {
506                let mut s = String::from("info");
507
508                for a in info_line {
509                    s += &format!(" {}", a.serialize());
510                }
511
512                s
513            },
514            UciMessage::Unknown(msg, ..) => {
515                format!("UNKNOWN MESSAGE: {}", msg)
516
517            }
518        }
519    }
520}
521
522
523
524/// This enum represents the possible variants of the `go` UCI message that deal with the chess game's time controls
525/// and the engine's thinking time.
526#[derive(Clone, Eq, PartialEq, Debug, Hash)]
527pub enum UciTimeControl {
528    /// The `go ponder` message.
529    Ponder,
530
531    /// The `go infinite` message.
532    Infinite,
533
534    /// The information about the game's time controls.
535    TimeLeft {
536        /// White's time on the clock, in milliseconds.
537        white_time: Option<Duration>,
538
539        /// Black's time on the clock, in milliseconds.
540        black_time: Option<Duration>,
541
542        /// White's increment per move, in milliseconds.
543        white_increment: Option<Duration>,
544
545        /// Black's increment per move, in milliseconds.
546        black_increment: Option<Duration>,
547
548        /// The number of moves to go to the next time control.
549        moves_to_go: Option<u8>,
550    },
551
552    /// Specifies how much time the engine should think about the move, in milliseconds.
553    MoveTime(Duration)
554}
555
556impl UciTimeControl {
557    /// Returns a `UciTimeControl::TimeLeft` with all members set to `None`.
558    pub fn time_left() -> UciTimeControl {
559        UciTimeControl::TimeLeft {
560            white_time: None,
561            black_time: None,
562            white_increment: None,
563            black_increment: None,
564            moves_to_go: None
565        }
566    }
567}
568
569/// A struct that controls the engine's (non-time-related) search settings.
570#[derive(Clone, Eq, PartialEq, Debug, Hash)]
571pub struct UciSearchControl {
572    /// Limits the search to these moves.
573    #[cfg(not(feature = "chess"))]
574    pub search_moves: Vec<UciMove>,
575
576    /// Limits the search to these moves.
577    #[cfg(feature = "chess")]
578    pub search_moves: Vec<ChessMove>,
579
580    /// Search for mate in this many moves.
581    pub mate: Option<u8>,
582
583    /// Search to this ply depth.
584    pub depth: Option<u8>,
585
586    /// Search no more than this many nodes (positions).
587    pub nodes: Option<u64>,
588}
589
590impl UciSearchControl {
591    /// Creates an `UciSearchControl` with `depth` set to the parameter and everything else set to empty or `None`.
592    pub fn depth(depth: u8) -> UciSearchControl {
593        UciSearchControl {
594            search_moves: vec![],
595            mate: None,
596            depth: Some(depth),
597            nodes: None,
598        }
599    }
600
601    /// Creates an `UciSearchControl` with `mate` set to the parameter and everything else set to empty or `None`.
602    pub fn mate(mate: u8) -> UciSearchControl {
603        UciSearchControl {
604            search_moves: vec![],
605            mate: Some(mate),
606            depth: None,
607            nodes: None,
608        }
609    }
610
611    /// Creates an `UciSearchControl` with `nodes` set to the parameter and everything else set to empty or `None`.
612    pub fn nodes(nodes: u64) -> UciSearchControl {
613        UciSearchControl {
614            search_moves: vec![],
615            mate: None,
616            depth: None,
617            nodes: Some(nodes),
618        }
619    }
620
621    /// Returns `true` if all of the struct's settings are either `None` or empty.
622    pub fn is_empty(&self) -> bool {
623        self.search_moves.is_empty() && self.mate.is_none() && self.depth.is_none() && self.nodes.is_none()
624    }
625}
626
627impl Default for UciSearchControl {
628    /// Creates an empty `UciSearchControl`.
629    fn default() -> Self {
630        UciSearchControl {
631            search_moves: vec![],
632            mate: None,
633            depth: None,
634            nodes: None,
635        }
636    }
637}
638
639/// Represents the copy protection or registration state.
640#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
641pub enum ProtectionState {
642    /// Signifies the engine is checking the copy protection or registration.
643    Checking,
644
645    /// Signifies the copy protection or registration has been validated.
646    Ok,
647
648    /// Signifies error in copy protection or registratin validation.
649    Error,
650}
651
652/// Represents a UCI option definition.
653#[derive(Clone, Eq, PartialEq, Debug, Hash)]
654pub enum UciOptionConfig {
655    /// The option of type `check` (a boolean).
656    Check {
657        /// The name of the option.
658        name: String,
659
660        /// The default value of this `bool` property.
661        default: Option<bool>,
662    },
663
664    /// The option of type `spin` (a signed integer).
665    Spin {
666        /// The name of the option.
667        name: String,
668
669        /// The default value of this integer property.
670        default: Option<i64>,
671
672        /// The minimal value of this integer property.
673        min: Option<i64>,
674
675        /// The maximal value of this integer property.
676        max: Option<i64>,
677    },
678
679    /// The option of type `combo` (a list of strings).
680    Combo {
681        /// The name of the option.
682        name: String,
683
684        /// The default value for this list of strings.
685        default: Option<String>,
686
687        /// The list of acceptable strings.
688        var: Vec<String>,
689    },
690
691    /// The option of type `button` (an action).
692    Button {
693        /// The name of the option.
694        name: String
695    },
696
697    /// The option of type `string` (a string, unsurprisingly).
698    String {
699        /// The name of the option.
700        name: String,
701
702        /// The default value of this string option.
703        default: Option<String>,
704    },
705}
706
707impl UciOptionConfig {
708    /// Returns the name of the option.
709    pub fn get_name(&self) -> &str {
710        match self {
711            UciOptionConfig::Check { name, .. } | UciOptionConfig::Spin { name, .. } | UciOptionConfig::Combo { name, .. } | UciOptionConfig::Button { name } |
712            UciOptionConfig::String { name, .. } => name.as_str()
713        }
714    }
715
716    /// Returns the type string of the option (ie. `"check"`, `"spin"` ...)
717    pub fn get_type_str(&self) -> &'static str {
718        match self {
719            UciOptionConfig::Check { .. } => "check",
720            UciOptionConfig::Spin { .. } => "spin",
721            UciOptionConfig::Combo { .. } => "combo",
722            UciOptionConfig::Button { .. } => "button",
723            UciOptionConfig::String { .. } => "string"
724        }
725    }
726}
727
728impl Serializable for UciOptionConfig {
729    /// Serializes this option config into a full UCI message string.
730    ///
731    /// # Examples
732    ///
733    /// ```
734    /// use vampirc_uci::{UciMessage, UciOptionConfig, Serializable};
735    ///
736    /// let m = UciMessage::Option(UciOptionConfig::Check {
737    ///     name: String::from("Nullmove"),
738    ///     default: Some(true)
739    /// });
740    ///
741    /// assert_eq!(m.serialize(), "option name Nullmove type check default true");
742    /// ```
743    fn serialize(&self) -> String {
744        let mut s = String::from(format!("option name {} type {}", self.get_name(), self.get_type_str()));
745        match self {
746            UciOptionConfig::Check { default, .. } => {
747                if let Some(def) = default {
748                    s += format!(" default {}", *def).as_str();
749                }
750            },
751            UciOptionConfig::Spin { default, min, max, .. } => {
752                if let Some(def) = default {
753                    s += format!(" default {}", *def).as_str();
754                }
755
756                if let Some(m) = min {
757                    s += format!(" min {}", *m).as_str();
758                }
759
760                if let Some(m) = max {
761                    s += format!(" max {}", *m).as_str();
762                }
763            }
764            UciOptionConfig::Combo { default, var, .. } => {
765                if let Some(def) = default {
766                    s += format!(" default {}", *def).as_str();
767                }
768
769                for v in var {
770                    s += format!(" var {}", *v).as_str();
771                }
772            }
773            UciOptionConfig::String { default, .. } => {
774                if let Some(def) = default {
775                    s += format!(" default {}", *def).as_str();
776                }
777            }
778            UciOptionConfig::Button { .. } => {
779                // Do nothing, we're already good
780            }
781        }
782
783        s
784    }
785}
786
787impl Display for UciOptionConfig {
788    fn fmt(&self, f: &mut Formatter) -> FmtResult {
789        write!(f, "{}", self.serialize())
790    }
791}
792
793/// The representation of various info messages. For an info attribute that is not listed in the protocol specification,
794/// the `UciInfoAttribute::Any(name, value)` variant can be used.
795#[derive(Clone, Eq, PartialEq, Debug, Hash)]
796pub enum UciInfoAttribute {
797    /// The `info depth` message.
798    Depth(u8),
799
800    /// The `info seldepth` message.
801    SelDepth(u8),
802
803    /// The `info time` message.
804    Time(Duration),
805
806    /// The `info nodes` message.
807    Nodes(u64),
808
809    /// The `info pv` message (best line move sequence).
810    #[cfg(not(feature = "chess"))]
811    Pv(Vec<UciMove>),
812
813    /// The `info pv` message (best line move sequence).
814    #[cfg(feature = "chess")]
815    Pv(Vec<ChessMove>),
816
817    /// The `info pv ... multipv` message (the pv line number in a multi pv sequence).
818    MultiPv(u16),
819
820    /// The `info score ...` message.
821    Score {
822        /// The score in centipawns.
823        cp: Option<i32>,
824
825        /// Mate coming up in this many moves. Negative value means the engine is getting mated.
826        mate: Option<i8>,
827
828        /// The value sent is the lower bound.
829        lower_bound: Option<bool>,
830
831        /// The value sent is the upper bound.
832        upper_bound: Option<bool>,
833    },
834
835    /// The `info currmove` message (current move).
836    #[cfg(not(feature = "chess"))]
837    CurrMove(UciMove),
838
839    /// The `info currmove` message (current move).
840    #[cfg(feature = "chess")]
841    CurrMove(ChessMove),
842
843    /// The `info currmovenum` message (current move number).
844    CurrMoveNum(u16),
845
846    /// The `info hashfull` message (the occupancy of hashing tables in permills).
847    HashFull(u16),
848
849    /// The `info nps` message (nodes per second).
850    Nps(u64),
851
852    /// The `info tbhits` message (end-game table-base hits).
853    TbHits(u64),
854
855    /// The `info sbhits` message (I guess some Shredder-specific end-game table-base stuff. I dunno, probably best to
856    /// ignore).
857    SbHits(u64),
858
859    /// The `info cpuload` message (CPU load in permills).
860    CpuLoad(u16),
861
862    /// The `info string` message (a string the GUI should display).
863    String(String),
864
865    /// The `info refutation` message (the first move is the move being refuted).
866    #[cfg(not(feature = "chess"))]
867    Refutation(Vec<UciMove>),
868
869    /// The `info refutation` message (the first move is the move being refuted).
870    #[cfg(feature = "chess")]
871    Refutation(Vec<ChessMove>),
872
873    /// The `info currline` message (current line being calculated on a CPU).
874    CurrLine {
875        /// The CPU number calculating this line.
876        cpu_nr: Option<u16>,
877
878        /// The line being calculated.
879        #[cfg(not(feature = "chess"))]
880        line: Vec<UciMove>,
881
882        /// The line being calculated.
883        #[cfg(feature = "chess")]
884        line: Vec<ChessMove>,
885    },
886
887    /// Any other info line in the format `(name, value)`.
888    Any(String, String),
889}
890
891impl UciInfoAttribute {
892    /// Creates a `UciInfoAttribute::Score` with the `cp` attribute set to the value of the parameter and all other
893    /// fields set to `None`.
894    pub fn from_centipawns(cp: i32) -> UciInfoAttribute {
895        UciInfoAttribute::Score {
896            cp: Some(cp),
897            mate: None,
898            lower_bound: None,
899            upper_bound: None,
900        }
901    }
902
903    /// Creates a `UciInfoAttribute::Score` with the `mate` attribute set to the value of the parameter and all other
904    /// fields set to `None`. A negative value indicates it is the engine that is getting mated.
905    pub fn from_mate(mate: i8) -> UciInfoAttribute {
906        UciInfoAttribute::Score {
907            cp: None,
908            mate: Some(mate),
909            lower_bound: None,
910            upper_bound: None,
911        }
912    }
913
914    /// Returns the name of the info attribute.
915    pub fn get_name(&self) -> &str {
916        match self {
917            UciInfoAttribute::Depth(..) => "depth",
918            UciInfoAttribute::SelDepth(..) => "seldepth",
919            UciInfoAttribute::Time(..) => "time",
920            UciInfoAttribute::Nodes(..) => "nodes",
921            UciInfoAttribute::Pv(..) => "pv",
922            UciInfoAttribute::MultiPv(..) => "multipv",
923            UciInfoAttribute::Score { .. } => "score",
924            UciInfoAttribute::CurrMove(..) => "currmove",
925            UciInfoAttribute::CurrMoveNum(..) => "currmovenum",
926            UciInfoAttribute::HashFull(..) => "hashfull",
927            UciInfoAttribute::Nps(..) => "nps",
928            UciInfoAttribute::TbHits(..) => "tbhits",
929            UciInfoAttribute::SbHits(..) => "sbhits",
930            UciInfoAttribute::CpuLoad(..) => "cpuload",
931            UciInfoAttribute::String(..) => "string",
932            UciInfoAttribute::Refutation(..) => "refutation",
933            UciInfoAttribute::CurrLine { .. } => "currline",
934            UciInfoAttribute::Any(name, ..) => name.as_str()
935        }
936    }
937}
938
939impl Serializable for UciInfoAttribute {
940    /// Returns the attribute serialized as a String.
941    fn serialize(&self) -> String {
942        let mut s = format!("{}", self.get_name());
943        match self {
944            UciInfoAttribute::Depth(depth) => s += format!(" {}", *depth).as_str(),
945            UciInfoAttribute::SelDepth(depth) => s += format!(" {}", *depth).as_str(),
946            UciInfoAttribute::Time(time) => s += format!(" {}", time.num_milliseconds()).as_str(),
947            UciInfoAttribute::Nodes(nodes) => s += format!(" {}", *nodes).as_str(),
948            UciInfoAttribute::Pv(moves) | UciInfoAttribute::Refutation(moves) => {
949                if !moves.is_empty() {
950                    for m in moves {
951                        s += format!(" {}", m).as_str();
952                    }
953                }
954            },
955            UciInfoAttribute::MultiPv(num) => s += format!(" {}", *num).as_str(),
956            UciInfoAttribute::Score { cp, mate, lower_bound, upper_bound } => {
957                if let Some(c) = cp {
958                    s += format!(" cp {}", *c).as_str();
959                }
960
961                if let Some(m) = mate {
962                    s += format!(" mate {}", *m).as_str();
963                }
964
965                if lower_bound.is_some() {
966                    s += " lowerbound";
967                } else if upper_bound.is_some() {
968                    s += " upperbound";
969                }
970            },
971            UciInfoAttribute::CurrMove(uci_move) => s += &format!(" {}", *uci_move),
972            UciInfoAttribute::CurrMoveNum(num) => s += &format!(" {}", *num),
973            UciInfoAttribute::HashFull(permill) => s += &format!(" {}", *permill),
974            UciInfoAttribute::Nps(nps) => s += &format!(" {}", *nps),
975            UciInfoAttribute::TbHits(hits) | UciInfoAttribute::SbHits(hits) => s += &format!(" {}", *hits),
976            UciInfoAttribute::CpuLoad(load) => s += &format!(" {}", *load),
977            UciInfoAttribute::String(string) => s += &format!(" {}", string),
978            UciInfoAttribute::CurrLine { cpu_nr, line } => {
979                if let Some(c) = cpu_nr {
980                    s += &format!(" cpunr {}", *c);
981                }
982
983                if !line.is_empty() {
984                    for m in line {
985                        s += &format!(" {}", m);
986                    }
987                }
988            },
989            UciInfoAttribute::Any(_, value) => {
990                s += &format!(" {}", value);
991            }
992        }
993
994        s
995    }
996}
997
998impl Display for UciInfoAttribute {
999    fn fmt(&self, f: &mut Formatter) -> FmtResult {
1000        write!(f, "{}", self.serialize())
1001    }
1002}
1003
1004/// An enum representing the chess piece types.
1005#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
1006#[cfg(not(feature = "chess"))]
1007pub enum UciPiece {
1008    Pawn,
1009    Knight,
1010    Bishop,
1011    Rook,
1012    Queen,
1013    King,
1014}
1015
1016#[cfg(not(feature = "chess"))]
1017impl UciPiece {
1018    /// Returns a character representing a piece in UCI move notation. Used for specifying promotion in moves.
1019    ///
1020    /// `n` – knight
1021    /// `b` - bishop
1022    /// `r` - rook
1023    /// `q` - queen
1024    /// `k` - king
1025    /// `None` - pawn
1026    pub fn as_char(self) -> Option<char> {
1027        match self {
1028            UciPiece::Pawn => None,
1029            UciPiece::Knight => Some('n'),
1030            UciPiece::Bishop => Some('b'),
1031            UciPiece::Rook => Some('r'),
1032            UciPiece::Queen => Some('q'),
1033            UciPiece::King => Some('k')
1034        }
1035    }
1036}
1037
1038#[cfg(not(feature = "chess"))]
1039impl FromStr for UciPiece {
1040    type Err = FmtError;
1041
1042    /// Creates a `UciPiece` from a `&str`, according to these rules:
1043    ///
1044    /// `"n"` - Knight
1045    /// `"p"` - Pawn
1046    /// `"b"` - Bishop
1047    /// `"r"` - Rook
1048    /// `"k"` - King
1049    /// `"q"` - Queen
1050    ///
1051    /// Works with uppercase letters as well.
1052    fn from_str(s: &str) -> Result<UciPiece, FmtError> {
1053        match s.to_ascii_lowercase().as_str() {
1054            "n" => Ok(UciPiece::Knight),
1055            "p" => Ok(UciPiece::Pawn),
1056            "b" => Ok(UciPiece::Bishop),
1057            "r" => Ok(UciPiece::Rook),
1058            "k" => Ok(UciPiece::King),
1059            "q" => Ok(UciPiece::Queen),
1060            _ => Err(FmtError)
1061        }
1062    }
1063}
1064
1065/// A representation of a chessboard square.
1066#[cfg(not(feature = "chess"))]
1067#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
1068pub struct UciSquare {
1069    /// The file. A character in the range of `a..h`.
1070    pub file: char,
1071
1072    /// The rank. A number in the range of `1..8`.
1073    pub rank: u8,
1074}
1075
1076#[cfg(not(feature = "chess"))]
1077impl UciSquare {
1078    /// Create a `UciSquare` from file character and a rank number.
1079    pub fn from(file: char, rank: u8) -> UciSquare {
1080        UciSquare {
1081            file,
1082            rank,
1083        }
1084    }
1085}
1086
1087#[cfg(not(feature = "chess"))]
1088impl Display for UciSquare {
1089    /// Formats the square in the regular notation (as in, `e4`).
1090    fn fmt(&self, f: &mut Formatter) -> FmtResult {
1091        write!(f, "{}{}", self.file, self.rank)
1092    }
1093}
1094
1095#[cfg(not(feature = "chess"))]
1096impl Default for UciSquare {
1097    /// Default square is an invalid square with a file of `\0` and the rank of `0`.
1098    fn default() -> Self {
1099        UciSquare {
1100            file: '\0',
1101            rank: 0,
1102        }
1103    }
1104}
1105
1106/// Representation of a chess move.
1107#[cfg(not(feature = "chess"))]
1108#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
1109pub struct UciMove {
1110    /// The source square.
1111    pub from: UciSquare,
1112
1113    /// The destination square.
1114    pub to: UciSquare,
1115
1116    /// The piece to be promoted to, if any.
1117    pub promotion: Option<UciPiece>,
1118}
1119
1120#[cfg(not(feature = "chess"))]
1121impl UciMove {
1122    /// Create a regular, non-promotion move from the `from` square to the `to` square.
1123    pub fn from_to(from: UciSquare, to: UciSquare) -> UciMove {
1124        UciMove {
1125            from,
1126            to,
1127            promotion: None,
1128        }
1129    }
1130}
1131
1132#[cfg(not(feature = "chess"))]
1133impl Display for UciMove {
1134    /// Formats the move in the UCI move notation.
1135    ///
1136    /// `e2e4` – A move from the square `e2` to the square `e4`.
1137    /// `a2a1q` – A move from the square `a2` to the square `a1` with the pawn promoting to a Queen..
1138    fn fmt(&self, f: &mut Formatter) -> FmtResult {
1139        let mut r = write!(f, "{}{}", self.from, self.to);
1140
1141        if let Some(p) = self.promotion {
1142            if let Some(c) = p.as_char() {
1143                r = write!(f, "{}", c);
1144            }
1145        }
1146
1147        r
1148    }
1149}
1150
1151#[derive(Clone, Eq, PartialEq, Debug, Hash)]
1152/// A representation of the notation in the [FEN notation](https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation).
1153pub struct UciFen(pub String);
1154
1155impl UciFen {
1156    /// Returns the FEN string.
1157    #[inline]
1158    pub fn as_str(&self) -> &str {
1159        self.0.as_str()
1160    }
1161}
1162
1163impl From<&str> for UciFen {
1164    /// Constructs an UciFen object from a `&str` containing a [FEN](https://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation)
1165    /// position. Does not validate the FEN.
1166    fn from(s: &str) -> Self {
1167        UciFen(s.to_string())
1168    }
1169}
1170
1171impl Display for UciFen {
1172    /// Outputs the FEN string.
1173    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
1174        write!(f, "{}", self.0)
1175    }
1176}
1177
1178
1179/// A vector containing several `UciMessage`s.
1180pub type MessageList = Vec<UciMessage>;
1181
1182/// A wrapper that keeps the serialized form in a byte vector. Mostly useful to provide an `AsRef<[u8]>` implementation for
1183/// quick conversion to an array of bytes. Use the `::from(m: UciMessage)` to construct it. It will add the newline
1184/// character `\n` to the serialized message.
1185#[derive(Clone, Eq, PartialEq, Debug, Hash)]
1186pub struct ByteVecUciMessage {
1187    pub message: UciMessage,
1188    pub bytes: Vec<u8>,
1189}
1190
1191impl Display for ByteVecUciMessage {
1192    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
1193        write!(f, "{}", self.message)
1194    }
1195}
1196
1197impl From<UciMessage> for ByteVecUciMessage {
1198    fn from(m: UciMessage) -> Self {
1199        let b = Vec::from((m.serialize() + "\n").as_bytes());
1200        ByteVecUciMessage {
1201            message: m,
1202            bytes: b,
1203        }
1204    }
1205}
1206
1207impl Into<UciMessage> for ByteVecUciMessage {
1208    fn into(self) -> UciMessage {
1209        self.message
1210    }
1211}
1212
1213impl AsRef<UciMessage> for ByteVecUciMessage {
1214    fn as_ref(&self) -> &UciMessage {
1215        &self.message
1216    }
1217}
1218
1219impl AsRef<[u8]> for ByteVecUciMessage {
1220    fn as_ref(&self) -> &[u8] {
1221        self.bytes.as_ref()
1222    }
1223}
1224
1225#[cfg(test)]
1226mod tests {
1227    #[cfg(feature = "chess")]
1228    use chess::Square;
1229
1230    use super::*;
1231
1232    #[test]
1233    fn test_direction_engine_bound() {
1234        assert_eq!(UciMessage::PonderHit.direction(), CommunicationDirection::GuiToEngine);
1235    }
1236
1237    #[test]
1238    fn test_direction_gui_bound() {
1239        assert_eq!(UciMessage::UciOk.direction(), CommunicationDirection::EngineToGui);
1240    }
1241
1242    #[test]
1243    fn test_serialize_id_name() {
1244        assert_eq!(UciMessage::id_name("Vampirc 0.5.0").serialize().as_str(), "id name Vampirc 0.5.0");
1245    }
1246
1247    #[test]
1248    fn test_serialize_id_author() {
1249        assert_eq!(UciMessage::id_author("Matija Kejžar").serialize().as_str(), "id author Matija Kejžar");
1250    }
1251
1252    #[test]
1253    fn test_serialize_uciok() {
1254        assert_eq!(UciMessage::UciOk.serialize().as_str(), "uciok");
1255    }
1256
1257    #[test]
1258    fn test_serialize_readyok() {
1259        assert_eq!(UciMessage::ReadyOk.serialize().as_str(), "readyok");
1260    }
1261
1262    #[cfg(not(feature = "chess"))]
1263    #[test]
1264    fn test_serialize_bestmove() {
1265        assert_eq!(UciMessage::best_move(UciMove::from_to(UciSquare::from('a', 1), UciSquare::from('a', 7))).serialize().as_str(), "bestmove a1a7");
1266    }
1267
1268    #[cfg(feature = "chess")]
1269    #[test]
1270    fn test_serialize_bestmove() {
1271        assert_eq!(UciMessage::best_move(ChessMove::new(Square::A1, Square::A7, None)).serialize().as_str(), "bestmove a1a7");
1272    }
1273
1274    #[cfg(not(feature = "chess"))]
1275    #[test]
1276    fn test_serialize_bestmove_with_options() {
1277        assert_eq!(UciMessage::best_move_with_ponder(UciMove::from_to(UciSquare::from('b', 4), UciSquare::from('a', 5)),
1278                                                     UciMove::from_to(UciSquare::from('b', 4), UciSquare::from('d', 6))).serialize().as_str(), "bestmove b4a5 ponder b4d6");
1279    }
1280
1281    #[cfg(feature = "chess")]
1282    #[test]
1283    fn test_serialize_bestmove_with_options() {
1284        assert_eq!(UciMessage::best_move_with_ponder(
1285            ChessMove::new(Square::B4, Square::A5, None), ChessMove::new(Square::B4, Square::D6, None),
1286        ).serialize().as_str(), "bestmove b4a5 ponder b4d6");
1287    }
1288
1289    #[test]
1290    fn test_serialize_copyprotection() {
1291        assert_eq!(UciMessage::CopyProtection(ProtectionState::Checking).serialize().as_str(), "copyprotection checking");
1292    }
1293
1294    #[test]
1295    fn test_serialize_registration() {
1296        assert_eq!(UciMessage::Registration(ProtectionState::Ok).serialize().as_str(), "registration ok");
1297    }
1298
1299    #[test]
1300    fn test_serialize_check_option() {
1301        let m = UciMessage::Option(UciOptionConfig::Check {
1302            name: "Nullmove".to_string(),
1303            default: Some(false),
1304        });
1305
1306        assert_eq!(m.serialize(), "option name Nullmove type check default false");
1307    }
1308
1309    #[test]
1310    fn test_serialize_spin_option() {
1311        let m = UciMessage::Option(UciOptionConfig::Spin {
1312            name: "Selectivity".to_string(),
1313            default: Some(2),
1314            min: Some(0),
1315            max: Some(4),
1316        });
1317
1318        assert_eq!(m.serialize(), "option name Selectivity type spin default 2 min 0 max 4");
1319    }
1320
1321    #[test]
1322    fn test_serialize_combo_option() {
1323        let m = UciMessage::Option(UciOptionConfig::Combo {
1324            name: "Style".to_string(),
1325            default: Some(String::from("Normal")),
1326            var: vec![String::from("Solid"), String::from("Normal"), String::from("Risky")],
1327        });
1328
1329        assert_eq!(m.serialize(), "option name Style type combo default Normal var Solid var Normal var Risky");
1330    }
1331
1332    #[test]
1333    fn test_serialize_string_option() {
1334        let m = UciMessage::Option(UciOptionConfig::String {
1335            name: "Nalimov Path".to_string(),
1336            default: Some(String::from("c:\\")),
1337        });
1338
1339        assert_eq!(m.serialize(), "option name Nalimov Path type string default c:\\");
1340    }
1341
1342    #[test]
1343    fn test_serialize_button_option() {
1344        let m = UciMessage::Option(UciOptionConfig::Button {
1345            name: "Clear Hash".to_string()
1346        });
1347
1348        assert_eq!(m.serialize(), "option name Clear Hash type button");
1349    }
1350
1351    #[test]
1352    fn test_serialize_info_depth() {
1353        let attributes: Vec<UciInfoAttribute> = vec![
1354            UciInfoAttribute::Depth(24)
1355        ];
1356
1357        let m = UciMessage::Info(attributes);
1358
1359        assert_eq!(m.serialize(), "info depth 24");
1360    }
1361
1362    #[test]
1363    fn test_serialize_info_seldepth() {
1364        let attributes: Vec<UciInfoAttribute> = vec![
1365            UciInfoAttribute::Depth(22),
1366            UciInfoAttribute::SelDepth(17)
1367        ];
1368
1369        let m = UciMessage::Info(attributes);
1370
1371        assert_eq!(m.serialize(), "info depth 22 seldepth 17");
1372    }
1373
1374    // info depth 2 score cp 214 time 1242 nodes 2124 nps 34928 pv e2e4 e7e5 g1f3
1375    #[test]
1376    fn test_serialize_info_pv() {
1377        let attributes: Vec<UciInfoAttribute> = vec![
1378            UciInfoAttribute::Depth(2),
1379            UciInfoAttribute::from_centipawns(214),
1380            UciInfoAttribute::Time(Duration::milliseconds(1242)),
1381            UciInfoAttribute::Nodes(2124),
1382            UciInfoAttribute::Nps(34928),
1383            #[cfg(not(feature = "chess"))]
1384                UciInfoAttribute::Pv(vec![
1385                UciMove::from_to(UciSquare::from('e', 2), UciSquare::from('e', 4)),
1386                UciMove::from_to(UciSquare::from('e', 7), UciSquare::from('e', 5)),
1387                UciMove::from_to(UciSquare::from('g', 1), UciSquare::from('f', 3)),
1388            ]),
1389            #[cfg(feature = "chess")]
1390                UciInfoAttribute::Pv(vec![
1391                ChessMove::new(Square::E2, Square::E4, None),
1392                ChessMove::new(Square::E7, Square::E5, None),
1393                ChessMove::new(Square::G1, Square::F3, None),
1394            ]),
1395        ];
1396
1397        let m = UciMessage::Info(attributes);
1398
1399        assert_eq!(m.serialize(), "info depth 2 score cp 214 time 1242 nodes 2124 nps 34928 pv e2e4 e7e5 g1f3");
1400    }
1401
1402    // info depth 5 seldepth 5 multipv 1 score cp -5 nodes 1540 nps 54 tbhits 0 time 28098 pv a8b6 e3b6 b1b6 a5a7 e2e3
1403    #[test]
1404    fn test_serialize_info_multipv() {
1405        let attributes: Vec<UciInfoAttribute> = vec![
1406            UciInfoAttribute::Depth(5),
1407            UciInfoAttribute::SelDepth(5),
1408            UciInfoAttribute::MultiPv(1),
1409            UciInfoAttribute::from_centipawns(-5),
1410            UciInfoAttribute::Nodes(1540),
1411            UciInfoAttribute::Nps(54),
1412            UciInfoAttribute::TbHits(0),
1413            UciInfoAttribute::Time(Duration::milliseconds(28098)),
1414            #[cfg(not(feature = "chess"))]
1415                UciInfoAttribute::Pv(vec![
1416                UciMove::from_to(UciSquare::from('a', 8), UciSquare::from('b', 6)),
1417                UciMove::from_to(UciSquare::from('e', 3), UciSquare::from('b', 6)),
1418                UciMove::from_to(UciSquare::from('b', 1), UciSquare::from('b', 6)),
1419                UciMove::from_to(UciSquare::from('a', 5), UciSquare::from('a', 7)),
1420                UciMove::from_to(UciSquare::from('e', 2), UciSquare::from('e', 3)),
1421            ]),
1422            #[cfg(feature = "chess")]
1423                UciInfoAttribute::Pv(vec![
1424                ChessMove::new(Square::A8, Square::B6, None),
1425                ChessMove::new(Square::E3, Square::B6, None),
1426                ChessMove::new(Square::B1, Square::B6, None),
1427                ChessMove::new(Square::A5, Square::A7, None),
1428                ChessMove::new(Square::E2, Square::E3, None),
1429            ])
1430        ];
1431
1432        let m = UciMessage::Info(attributes);
1433
1434        assert_eq!(m.serialize(), "info depth 5 seldepth 5 multipv 1 score cp -5 nodes 1540 nps 54 tbhits 0 time 28098 pv a8b6 e3b6 b1b6 a5a7 e2e3");
1435    }
1436
1437    #[test]
1438    fn test_serialize_info_score() {
1439        let attributes: Vec<UciInfoAttribute> = vec![
1440            UciInfoAttribute::Score {
1441                cp: Some(817),
1442                mate: None,
1443                upper_bound: Some(true),
1444                lower_bound: None,
1445            }
1446        ];
1447
1448        let m = UciMessage::Info(attributes);
1449
1450        assert_eq!(m.serialize(), "info score cp 817 upperbound");
1451    }
1452
1453    #[test]
1454    fn test_serialize_info_score_mate_in_three() {
1455        let attributes: Vec<UciInfoAttribute> = vec![
1456            UciInfoAttribute::Score {
1457                cp: None,
1458                mate: Some(-3),
1459                upper_bound: None,
1460                lower_bound: None,
1461            }
1462        ];
1463
1464        let m = UciMessage::Info(attributes);
1465
1466        assert_eq!(m.serialize(), "info score mate -3");
1467    }
1468
1469    #[test]
1470    fn test_serialize_info_currmove() {
1471        #[cfg(not(feature = "chess"))]
1472            let attributes: Vec<UciInfoAttribute> = vec![
1473            UciInfoAttribute::CurrMove(UciMove::from_to(
1474                UciSquare::from('a', 5),
1475                UciSquare::from('c', 3),
1476            ))
1477        ];
1478
1479        #[cfg(feature = "chess")]
1480            let attributes: Vec<UciInfoAttribute> = vec![
1481            UciInfoAttribute::CurrMove(ChessMove::new(Square::A5, Square::C3, None))
1482        ];
1483
1484        let m = UciMessage::Info(attributes);
1485
1486        assert_eq!(m.serialize(), "info currmove a5c3");
1487    }
1488
1489    #[test]
1490    fn test_serialize_info_currmovenum() {
1491        #[cfg(not(feature = "chess"))]
1492            let attributes: Vec<UciInfoAttribute> = vec![
1493            UciInfoAttribute::CurrMove(UciMove::from_to(
1494                UciSquare::from('a', 2),
1495                UciSquare::from('f', 2),
1496            )),
1497            UciInfoAttribute::CurrMoveNum(2)
1498        ];
1499
1500        #[cfg(feature = "chess")]
1501            let attributes: Vec<UciInfoAttribute> = vec![
1502            UciInfoAttribute::CurrMove(ChessMove::new(Square::A2, Square::F2, None)),
1503            UciInfoAttribute::CurrMoveNum(2)
1504        ];
1505
1506        let m = UciMessage::Info(attributes);
1507
1508        assert_eq!(m.serialize(), "info currmove a2f2 currmovenum 2");
1509    }
1510
1511    #[test]
1512    fn test_serialize_info_hashfull() {
1513        let attributes: Vec<UciInfoAttribute> = vec![
1514            UciInfoAttribute::HashFull(455)
1515        ];
1516
1517        let m = UciMessage::Info(attributes);
1518
1519        assert_eq!(m.serialize(), "info hashfull 455");
1520    }
1521
1522    #[test]
1523    fn test_serialize_info_nps() {
1524        let attributes: Vec<UciInfoAttribute> = vec![
1525            UciInfoAttribute::Nps(5098)
1526        ];
1527
1528        let m = UciMessage::Info(attributes);
1529
1530        assert_eq!(m.serialize(), "info nps 5098");
1531    }
1532
1533    #[test]
1534    fn test_serialize_info_tbhits_nbhits() {
1535        let attributes: Vec<UciInfoAttribute> = vec![
1536            UciInfoAttribute::TbHits(987),
1537            UciInfoAttribute::SbHits(409),
1538        ];
1539
1540        let m = UciMessage::Info(attributes);
1541
1542        assert_eq!(m.serialize(), "info tbhits 987 sbhits 409");
1543    }
1544
1545    #[test]
1546    fn test_serialize_info_cpuload() {
1547        let attributes: Vec<UciInfoAttribute> = vec![
1548            UciInfoAttribute::CpuLoad(823)
1549        ];
1550
1551        let m = UciMessage::Info(attributes);
1552
1553        assert_eq!(m.serialize(), "info cpuload 823");
1554    }
1555
1556    #[test]
1557    fn test_serialize_info_string() {
1558        let attributes: Vec<UciInfoAttribute> = vec![
1559            UciInfoAttribute::String(String::from("Invalid move: d6e1 - violates chess rules"))
1560        ];
1561
1562        let m = UciMessage::Info(attributes);
1563
1564        assert_eq!(m.serialize(), "info string Invalid move: d6e1 - violates chess rules");
1565    }
1566
1567    #[test]
1568    fn test_serialize_info_refutation() {
1569        #[cfg(not(feature = "chess"))]
1570        let attributes: Vec<UciInfoAttribute> = vec![
1571            UciInfoAttribute::Refutation(vec![
1572                UciMove::from_to(
1573                    UciSquare::from('d', 1),
1574                    UciSquare::from('h', 5),
1575                ),
1576                UciMove::from_to(
1577                    UciSquare::from('g', 6),
1578                    UciSquare::from('h', 5),
1579                )
1580            ])
1581        ];
1582
1583        #[cfg(feature = "chess")]
1584            let attributes: Vec<UciInfoAttribute> = vec![
1585            UciInfoAttribute::Refutation(vec![
1586                ChessMove::new(Square::D1, Square::H5, None),
1587                ChessMove::new(Square::G6, Square::H5, None),
1588            ])
1589        ];
1590
1591        let m = UciMessage::Info(attributes);
1592
1593        assert_eq!(m.serialize(), "info refutation d1h5 g6h5");
1594    }
1595
1596    #[test]
1597    fn test_serialize_info_currline() {
1598        #[cfg(not(feature = "chess"))]
1599            let attributes: Vec<UciInfoAttribute> = vec![
1600            UciInfoAttribute::CurrLine {
1601                cpu_nr: Some(1),
1602                line: vec![
1603                    UciMove::from_to(
1604                        UciSquare::from('d', 1),
1605                        UciSquare::from('h', 5),
1606                    ),
1607                    UciMove::from_to(
1608                        UciSquare::from('g', 6),
1609                        UciSquare::from('h', 5),
1610                    )
1611                ],
1612            }
1613        ];
1614
1615        #[cfg(feature = "chess")]
1616            let attributes: Vec<UciInfoAttribute> = vec![
1617            UciInfoAttribute::CurrLine {
1618                cpu_nr: Some(1),
1619                line: vec![
1620                    ChessMove::new(Square::D1, Square::H5, None),
1621                    ChessMove::new(Square::G6, Square::H5, None),
1622                ],
1623            }
1624        ];
1625
1626        let m = UciMessage::Info(attributes);
1627
1628        assert_eq!(m.serialize(), "info currline cpunr 1 d1h5 g6h5");
1629    }
1630
1631    #[test]
1632    fn test_serialize_info_any() {
1633        let attributes: Vec<UciInfoAttribute> = vec![
1634            UciInfoAttribute::Any(String::from("other"), String::from("Some other message."))
1635        ];
1636
1637        let m = UciMessage::Info(attributes);
1638
1639        assert_eq!(m.serialize(), "info other Some other message.");
1640    }
1641
1642    #[test]
1643    fn test_serialize_none_setoption() {
1644        assert_eq!(UciMessage::SetOption {
1645            name: "Some option".to_string(),
1646            value: None,
1647        }.serialize(), "setoption name Some option value <empty>")
1648    }
1649
1650    #[test]
1651    fn test_serialize_empty_setoption() {
1652        assert_eq!(UciMessage::SetOption {
1653            name: "ABC".to_string(),
1654            value: Some(String::from("")),
1655        }.serialize(), "setoption name ABC value <empty>")
1656    }
1657
1658    #[test]
1659    fn test_is_unknown_false() {
1660        assert_eq!(UciMessage::Uci.is_unknown(), false);
1661    }
1662
1663    #[test]
1664    fn test_is_unknown_true() {
1665        let um = UciMessage::Unknown("Unrecognized Command".to_owned(), None);
1666        assert_eq!(um.is_unknown(), true);
1667    }
1668
1669    #[test]
1670    fn test_byte_vec_message_creation() {
1671        let uok = ByteVecUciMessage::from(UciMessage::UciOk);
1672        assert_eq!(uok.message, UciMessage::UciOk);
1673        assert_eq!(uok.bytes, (UciMessage::UciOk.serialize() + "\n").as_bytes());
1674
1675        let asm: UciMessage = uok.into();
1676        assert_eq!(asm, UciMessage::UciOk);
1677    }
1678
1679    #[test]
1680    fn test_byte_vec_message_as_ref_uci_message() {
1681        let uci = ByteVecUciMessage::from(UciMessage::Uci);
1682        let um: &UciMessage = uci.as_ref();
1683        assert_eq!(*um, UciMessage::Uci);
1684    }
1685
1686    #[test]
1687    fn test_byte_vec_message_as_ref_u8() {
1688        let uci = ByteVecUciMessage::from(UciMessage::UciNewGame);
1689        let um: &[u8] = uci.as_ref();
1690        let uc = Vec::from(um);
1691        assert_eq!(uc, Vec::from((UciMessage::UciNewGame.serialize() + "\n").as_bytes()));
1692    }
1693
1694    #[test]
1695    fn test_empty_go_message() {
1696        let empty_go = UciMessage::go();
1697        assert_eq!(empty_go, UciMessage::Go { time_control: None, search_control: None });
1698    }
1699
1700    #[test]
1701    fn test_negative_duration() {
1702        let time_control = UciTimeControl::TimeLeft {
1703            white_time: Some(Duration::milliseconds(-4061)),
1704            black_time: Some(Duration::milliseconds(56826)),
1705            white_increment: None,
1706            black_increment: None,
1707            moves_to_go: Some(90),
1708        };
1709
1710        let message = UciMessage::Go {
1711            time_control: Some(time_control),
1712            search_control: None,
1713        };
1714
1715        match message {
1716            UciMessage::Go { time_control, search_control: _ } => {
1717                let tc = time_control.unwrap();
1718                match tc {
1719                    UciTimeControl::TimeLeft { white_time, black_time, white_increment: _, black_increment: _, moves_to_go: _ } => {
1720                        let wt = white_time.unwrap();
1721                        assert_eq!(wt, Duration::milliseconds(-4061));
1722                        assert_eq!(wt.num_milliseconds(), -4061);
1723                        assert_eq!(wt.num_seconds(), -4);
1724                        assert_eq!(black_time.unwrap(), Duration::milliseconds(56826));
1725                    }
1726                    _ => unreachable!()
1727                }
1728            },
1729            _ => unreachable!()
1730        }
1731    }
1732}