1use 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#[derive(Copy, Clone, Eq, PartialEq, Debug)]
20pub enum CommunicationDirection {
21 GuiToEngine,
23
24 EngineToGui,
26}
27
28pub trait Serializable: Display {
29 fn serialize(&self) -> String;
30}
31
32#[derive(Clone, Eq, PartialEq, Debug, Hash)]
34pub enum UciMessage {
35 Uci,
37
38 Debug(bool),
41
42 IsReady,
44
45 Register {
47 later: bool,
49
50 name: Option<String>,
52
53 code: Option<String>,
55 },
56
57 Position {
59 startpos: bool,
62
63 fen: Option<UciFen>,
66
67 #[cfg(not(feature = "chess"))]
69 moves: Vec<UciMove>,
70
71 #[cfg(feature = "chess")]
73 moves: Vec<ChessMove>,
74 },
75
76 SetOption {
78 name: String,
80
81 value: Option<String>,
83 },
84
85 UciNewGame,
87
88 Stop,
90
91 PonderHit,
93
94 Quit,
96
97 Go {
99 time_control: Option<UciTimeControl>,
101
102 search_control: Option<UciSearchControl>,
104 },
105
106 Id {
110 name: Option<String>,
112
113 author: Option<String>,
115 },
116
117 UciOk,
119
120 ReadyOk,
122
123 BestMove {
125 #[cfg(not(feature = "chess"))]
127 best_move: UciMove,
128
129 #[cfg(feature = "chess")]
131 best_move: ChessMove,
132
133 #[cfg(not(feature = "chess"))]
135 ponder: Option<UciMove>,
136
137 #[cfg(feature = "chess")]
139 ponder: Option<ChessMove>,
140 },
141
142 CopyProtection(ProtectionState),
144
145 Registration(ProtectionState),
147
148 Option(UciOptionConfig),
150
151 Info(Vec<UciInfoAttribute>),
153
154 Unknown(String, Option<PestError<Rule>>)
156}
157
158impl UciMessage {
159 pub fn register_later() -> UciMessage {
161 UciMessage::Register {
162 later: true,
163 name: None,
164 code: None,
165 }
166 }
167
168 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 pub fn go() -> UciMessage {
179 UciMessage::Go {
180 search_control: None,
181 time_control: None,
182 }
183 }
184
185 pub fn go_ponder() -> UciMessage {
187 UciMessage::Go {
188 search_control: None,
189 time_control: Some(UciTimeControl::Ponder),
190 }
191 }
192
193 pub fn go_infinite() -> UciMessage {
195 UciMessage::Go {
196 search_control: None,
197 time_control: Some(UciTimeControl::Infinite)
198 }
199 }
200
201 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 pub fn id_name(name: &str) -> UciMessage {
212 UciMessage::Id {
213 name: Some(name.to_string()),
214 author: None,
215 }
216 }
217
218 pub fn id_author(author: &str) -> UciMessage {
220 UciMessage::Id {
221 name: None,
222 author: Some(author.to_string()),
223 }
224 }
225
226 #[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 #[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 #[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 #[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 pub fn info_string(s: String) -> UciMessage {
264 UciMessage::Info(vec![UciInfoAttribute::String(s)])
265 }
266
267 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 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 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 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 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 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#[derive(Clone, Eq, PartialEq, Debug, Hash)]
527pub enum UciTimeControl {
528 Ponder,
530
531 Infinite,
533
534 TimeLeft {
536 white_time: Option<Duration>,
538
539 black_time: Option<Duration>,
541
542 white_increment: Option<Duration>,
544
545 black_increment: Option<Duration>,
547
548 moves_to_go: Option<u8>,
550 },
551
552 MoveTime(Duration)
554}
555
556impl UciTimeControl {
557 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#[derive(Clone, Eq, PartialEq, Debug, Hash)]
571pub struct UciSearchControl {
572 #[cfg(not(feature = "chess"))]
574 pub search_moves: Vec<UciMove>,
575
576 #[cfg(feature = "chess")]
578 pub search_moves: Vec<ChessMove>,
579
580 pub mate: Option<u8>,
582
583 pub depth: Option<u8>,
585
586 pub nodes: Option<u64>,
588}
589
590impl UciSearchControl {
591 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 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 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 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 fn default() -> Self {
630 UciSearchControl {
631 search_moves: vec![],
632 mate: None,
633 depth: None,
634 nodes: None,
635 }
636 }
637}
638
639#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
641pub enum ProtectionState {
642 Checking,
644
645 Ok,
647
648 Error,
650}
651
652#[derive(Clone, Eq, PartialEq, Debug, Hash)]
654pub enum UciOptionConfig {
655 Check {
657 name: String,
659
660 default: Option<bool>,
662 },
663
664 Spin {
666 name: String,
668
669 default: Option<i64>,
671
672 min: Option<i64>,
674
675 max: Option<i64>,
677 },
678
679 Combo {
681 name: String,
683
684 default: Option<String>,
686
687 var: Vec<String>,
689 },
690
691 Button {
693 name: String
695 },
696
697 String {
699 name: String,
701
702 default: Option<String>,
704 },
705}
706
707impl UciOptionConfig {
708 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 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 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 }
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#[derive(Clone, Eq, PartialEq, Debug, Hash)]
796pub enum UciInfoAttribute {
797 Depth(u8),
799
800 SelDepth(u8),
802
803 Time(Duration),
805
806 Nodes(u64),
808
809 #[cfg(not(feature = "chess"))]
811 Pv(Vec<UciMove>),
812
813 #[cfg(feature = "chess")]
815 Pv(Vec<ChessMove>),
816
817 MultiPv(u16),
819
820 Score {
822 cp: Option<i32>,
824
825 mate: Option<i8>,
827
828 lower_bound: Option<bool>,
830
831 upper_bound: Option<bool>,
833 },
834
835 #[cfg(not(feature = "chess"))]
837 CurrMove(UciMove),
838
839 #[cfg(feature = "chess")]
841 CurrMove(ChessMove),
842
843 CurrMoveNum(u16),
845
846 HashFull(u16),
848
849 Nps(u64),
851
852 TbHits(u64),
854
855 SbHits(u64),
858
859 CpuLoad(u16),
861
862 String(String),
864
865 #[cfg(not(feature = "chess"))]
867 Refutation(Vec<UciMove>),
868
869 #[cfg(feature = "chess")]
871 Refutation(Vec<ChessMove>),
872
873 CurrLine {
875 cpu_nr: Option<u16>,
877
878 #[cfg(not(feature = "chess"))]
880 line: Vec<UciMove>,
881
882 #[cfg(feature = "chess")]
884 line: Vec<ChessMove>,
885 },
886
887 Any(String, String),
889}
890
891impl UciInfoAttribute {
892 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 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 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 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#[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 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 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#[cfg(not(feature = "chess"))]
1067#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
1068pub struct UciSquare {
1069 pub file: char,
1071
1072 pub rank: u8,
1074}
1075
1076#[cfg(not(feature = "chess"))]
1077impl UciSquare {
1078 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 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 fn default() -> Self {
1099 UciSquare {
1100 file: '\0',
1101 rank: 0,
1102 }
1103 }
1104}
1105
1106#[cfg(not(feature = "chess"))]
1108#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
1109pub struct UciMove {
1110 pub from: UciSquare,
1112
1113 pub to: UciSquare,
1115
1116 pub promotion: Option<UciPiece>,
1118}
1119
1120#[cfg(not(feature = "chess"))]
1121impl UciMove {
1122 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 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)]
1152pub struct UciFen(pub String);
1154
1155impl UciFen {
1156 #[inline]
1158 pub fn as_str(&self) -> &str {
1159 self.0.as_str()
1160 }
1161}
1162
1163impl From<&str> for UciFen {
1164 fn from(s: &str) -> Self {
1167 UciFen(s.to_string())
1168 }
1169}
1170
1171impl Display for UciFen {
1172 fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
1174 write!(f, "{}", self.0)
1175 }
1176}
1177
1178
1179pub type MessageList = Vec<UciMessage>;
1181
1182#[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 #[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 #[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}