1#[cfg(feature = "chess")]
8use std::fmt::Error as FmtError;
9use std::str::FromStr;
10
11use chrono::Duration;
12use pest::error::Error;
13use pest::iterators::Pair;
14use pest::Parser;
15
16#[cfg(feature = "chess")]
17use crate::chess::{ChessMove, Piece, Square};
18use crate::uci::ProtectionState;
19use crate::uci::{
20 MessageList, UciFen, UciInfoAttribute, UciMessage, UciSearchControl, UciTimeControl,
21};
22#[cfg(not(feature = "chess"))]
23use crate::uci::{UciMove, UciPiece, UciSquare};
24use crate::UciOptionConfig;
25
26#[derive(Parser)]
27#[grammar = "../res/uci.pest"]
28struct UciParser;
29
30pub fn parse_strict(s: &str) -> Result<MessageList, Error<Rule>> {
49 let mut ml = MessageList::new();
50 do_parse_uci(s, Rule::commands, Some(&mut ml))?;
51
52 Ok(ml)
53}
54
55pub fn parse(s: &str) -> MessageList {
73 let mut ml = MessageList::new();
74 do_parse_uci(s, Rule::commands_ignore_unknown, Some(&mut ml)).unwrap();
75
76 ml
77}
78
79pub fn parse_with_unknown(s: &str) -> MessageList {
91 let mut ml = MessageList::new();
92 let parse_att = do_parse_uci(s, Rule::commands_with_unknown, Some(&mut ml));
93
94 if let Err(e) = parse_att {
95 let m = UciMessage::Unknown(s.trim_end().to_owned(), Some(e));
96 return vec![m];
97 }
98
99 ml
100}
101
102pub fn parse_one(s: &str) -> UciMessage {
121 let r = do_parse_uci(s, Rule::single_message_per_line, None);
122
123 if let Err(e) = r {
124 let m = UciMessage::Unknown(s.trim_end().to_owned(), Some(e));
125 return m;
126 }
127
128 if let Some(m) = r.unwrap() {
129 return m;
130 }
131
132 return UciMessage::Unknown(String::new(), None);
133}
134
135fn do_parse_uci(
136 s: &str,
137 top_rule: Rule,
138 mut ml: Option<&mut MessageList>,
139) -> Result<Option<UciMessage>, Error<Rule>> {
140 let pairs = UciParser::parse(top_rule, s)?;
141
142 let mut single: Option<UciMessage> = None;
143
144 pairs
145 .map(|pair: Pair<_>| {
146 match pair.as_rule() {
147 Rule::uci => UciMessage::Uci,
148 Rule::debug => {
149 for sp in pair.into_inner() {
150 match sp.as_rule() {
151 Rule::switch => {
152 return UciMessage::Debug(
153 sp.as_span().as_str().eq_ignore_ascii_case("on"),
154 );
155 }
156 _ => unreachable!(),
157 }
158 }
159 UciMessage::Debug(false)
160 }
161 Rule::isready => UciMessage::IsReady,
162 Rule::setoption => {
163 let mut name: String = String::default();
164 let mut value: String = String::default();
165
166 for sp in pair.into_inner() {
167 match sp.as_rule() {
168 Rule::option_internal => {
169 for spi in sp.into_inner() {
170 match spi.as_rule() {
171 Rule::option_name => {
172 name = spi.as_span().as_str().trim().to_string();
173 }
174 Rule::option_value => {
175 value = spi.as_span().as_str().to_string();
176 }
177 _ => {}
178 }
179 }
180 }
181 _ => (),
183 }
184 }
185
186 let val = if value != String::default() {
187 Some(value)
188 } else {
189 None
190 };
191 UciMessage::SetOption { name, value: val }
192 }
193 Rule::register => {
194 for sp in pair.into_inner() {
195 match sp.as_rule() {
196 Rule::register_later => {
197 return UciMessage::register_later();
198 }
199 Rule::register_nc => {
200 let mut name: &str = "";
201
202 for spi in sp.into_inner() {
203 match spi.as_rule() {
204 Rule::register_name => {
205 name = spi.as_span().as_str();
206 }
207 Rule::register_code => {
208 return UciMessage::register_code(name, spi.as_str());
209 }
210 _ => (),
211 }
212 }
213 }
214 _ => unreachable!(),
215 }
216 }
217
218 unreachable!()
219 }
220 Rule::ucinewgame => UciMessage::UciNewGame,
221 Rule::stop => UciMessage::Stop,
222 Rule::ponderhit => UciMessage::PonderHit,
223 Rule::quit => UciMessage::Quit,
224 Rule::position => {
225 let mut startpos = false;
226 let mut fen: Option<UciFen> = None;
227 #[cfg(not(feature = "chess"))] let mut moves: Vec<UciMove> = Default::default();
228 #[cfg(feature = "chess")] let mut moves: Vec<ChessMove> = Default::default();
229
230 for sp in pair.into_inner() {
231 match sp.as_rule() {
232 Rule::startpos => {
233 startpos = true;
234 }
235 Rule::fen => fen = Some(UciFen::from(sp.as_span().as_str())),
236 Rule::a_move => {
237 moves.push(parse_a_move(sp));
238 }
239 _ => {}
240 }
241 }
242
243 UciMessage::Position {
244 startpos,
245 fen,
246 moves,
247 }
248 }
249 Rule::go => {
250 let mut time_control: Option<UciTimeControl> = None;
251 let mut tl = false;
252 let mut wtime: Option<i64> = None;
253 let mut btime: Option<i64> = None;
254 let mut winc: Option<i64> = None;
255 let mut binc: Option<i64> = None;
256 let mut moves_to_go: Option<u8> = None;
257
258 let mut search: UciSearchControl = UciSearchControl::default();
259
260 for sp in pair.into_inner() {
261 match sp.as_rule() {
262 Rule::go_empty => {}
263 Rule::go_full => {
264 for sp_full in sp.into_inner() {
265 match sp_full.as_rule() {
266 Rule::go_time => {
267 for spi in sp_full.into_inner() {
268 match spi.as_rule() {
269 Rule::go_ponder => {
270 time_control = Some(UciTimeControl::Ponder);
271 }
272 Rule::go_infinite => {
273 time_control = Some(UciTimeControl::Infinite);
274 }
275 Rule::go_movetime => {
276 time_control = Some(UciTimeControl::MoveTime(
277 Duration::milliseconds(parse_milliseconds(spi)),
278 ));
279 }
280 Rule::go_timeleft => {
281 if !tl {
282 tl = true;
283 }
284
285 for sspi in spi.into_inner() {
286 match sspi.as_rule() {
287 Rule::wtime => {
288 wtime = Some(parse_milliseconds(sspi));
289 }
290 Rule::btime => {
291 btime = Some(parse_milliseconds(sspi));
292 }
293 Rule::winc => {
294 winc = Some(parse_milliseconds(sspi));
295 }
296 Rule::binc => {
297 binc = Some(parse_milliseconds(sspi));
298 }
299 Rule::movestogo => {
300 moves_to_go =
301 Some(parse_u8(sspi, Rule::digits3));
302 }
303 _ => {}
304 };
305 }
306 }
307
308 _ => {}
309 }
310 }
311 }
312 Rule::go_search => {
313 for spi in sp_full.into_inner() {
314 match spi.as_rule() {
315 Rule::depth => {
316 search.depth = Some(parse_u8(spi, Rule::digits3));
317 }
318 Rule::mate => {
319 search.mate = Some(parse_u8(spi, Rule::digits3))
320 }
321 Rule::nodes => {
322 search.nodes = Some(parse_u64(spi, Rule::digits12))
323 }
324 Rule::searchmoves => {
325 for mt in spi.into_inner() {
326 search.search_moves.push(parse_a_move(mt));
327 }
328 }
329 _ => {}
330 }
331 }
332 }
333 _ => unreachable!()
334 }
335 }
336 }
337 _ => unreachable!(),
338 }
339 }
340
341 if tl {
342 time_control = Some(UciTimeControl::TimeLeft {
343 white_time: wtime.map(|millis| Duration::milliseconds(millis)),
344 black_time: btime.map(|millis| Duration::milliseconds(millis)),
345 white_increment: winc.map(|millis| Duration::milliseconds(millis)),
346 black_increment: binc.map(|millis| Duration::milliseconds(millis)),
347 moves_to_go,
348 });
349 }
350
351 let search_control: Option<UciSearchControl>;
352 if search.is_empty() {
353 search_control = None
354 } else {
355 search_control = Some(search);
356 }
357
358 UciMessage::Go {
359 time_control,
360 search_control,
361 }
362 }
363 Rule::id => {
364 for sp in pair.into_inner() {
365 let id_rule: Rule = sp.as_rule();
366 match id_rule {
367 Rule::id_name | Rule::id_author => {
368 return parse_id_text(sp, id_rule);
369 }
370 _ => {}
371 }
372 }
373
374 unreachable!()
375 }
376 Rule::uciok => UciMessage::UciOk,
377 Rule::readyok => UciMessage::ReadyOk,
378 Rule::bestmove => {
379 #[cfg(not(feature = "chess"))] let mut bm: Option<UciMove> = None;
380 #[cfg(not(feature = "chess"))] let mut ponder: Option<UciMove> = None;
381 #[cfg(feature = "chess")] let mut bm: Option<ChessMove> = None;
382 #[cfg(feature = "chess")] let mut ponder: Option<ChessMove> = None;
383 for sp in pair.into_inner() {
384 match sp.as_rule() {
385 Rule::a_move => {
386 bm = Some(parse_a_move(sp));
387 }
388 Rule::bestmove_ponder => {
389 for ssp in sp.into_inner() {
390 match ssp.as_rule() {
391 Rule::a_move => ponder = Some(parse_a_move(ssp)),
392 _ => {}
393 }
394 }
395 }
396 _ => {}
397 }
398 }
399
400 UciMessage::BestMove {
401 best_move: bm.unwrap(),
402 ponder,
403 }
404 }
405 Rule::copyprotection | Rule::registration => {
406 let mut ps: Option<ProtectionState> = None;
407 let pc = pair.clone();
408 for sp in pair.into_inner() {
409 match sp.as_rule() {
410 Rule::protection_checking => ps = Some(ProtectionState::Checking),
411 Rule::protection_ok => ps = Some(ProtectionState::Ok),
412 Rule::protection_error => ps = Some(ProtectionState::Error),
413 _ => {}
414 }
415 }
416
417 if pc.as_rule() == Rule::copyprotection {
418 UciMessage::CopyProtection(ps.unwrap())
419 } else {
420 UciMessage::Registration(ps.unwrap())
421 }
422 }
423 Rule::option => {
424 let mut name: Option<&str> = None;
425 let mut opt_default: Option<&str> = None;
426 let mut opt_min: Option<i64> = None;
427 let mut opt_max: Option<i64> = None;
428 let mut opt_var: Vec<String> = Vec::default();
429 let mut type_pair: Option<Pair<Rule>> = None;
430
431 for sp in pair.into_inner() {
432 match sp.as_rule() {
433 Rule::option_name2 => {
434 name = Some(sp.as_span().as_str());
435 }
436 Rule::option_type => {
437 for spi in sp.into_inner() {
438 match spi.as_rule() {
439 Rule::option_check
440 | Rule::option_spin
441 | Rule::option_combo
442 | Rule::option_string
443 | Rule::option_button => {
444 type_pair = Some(spi);
445 }
446 _ => {}
447 }
448 }
449 }
450 Rule::option_default => {
451 opt_default = Some(sp.as_span().as_str());
452 }
453 Rule::option_min => {
454 opt_min = Some(parse_i64(sp, Rule::i64));
455 }
456 Rule::option_max => {
457 opt_max = Some(parse_i64(sp, Rule::i64));
458 }
459 Rule::option_var => {
460 opt_var.push(String::from(sp.as_span().as_str()));
461 }
462 _ => unreachable!(),
463 }
464 }
465
466 let uoc: UciOptionConfig = match type_pair.unwrap().as_rule() {
467 Rule::option_check => UciOptionConfig::Check {
468 name: String::from(name.unwrap()),
469 default: if let Some(def) = opt_default {
470 match def.to_lowercase().as_str() {
471 "true" => Some(true),
472 "false" => Some(false),
473 _ => None,
474 }
475 } else {
476 None
477 },
478 },
479 Rule::option_spin => UciOptionConfig::Spin {
480 name: String::from(name.unwrap()),
481 default: if let Some(def) = opt_default {
482 if let Ok(def_i64) = str::parse::<i64>(def) {
483 Some(def_i64)
484 } else {
485 None
486 }
487 } else {
488 None
489 },
490 min: if let Some(min1) = opt_min {
491 Some(min1)
492 } else {
493 None
494 },
495 max: if let Some(max1) = opt_max {
496 Some(max1)
497 } else {
498 None
499 },
500 },
501 Rule::option_combo => UciOptionConfig::Combo {
502 name: String::from(name.unwrap()),
503 default: if let Some(def) = opt_default {
504 if def.eq_ignore_ascii_case("<empty>") {
505 Some(String::from(""))
506 } else {
507 Some(String::from(def))
508 }
509 } else {
510 None
511 },
512 var: opt_var,
513 },
514 Rule::option_string => UciOptionConfig::String {
515 name: String::from(name.unwrap()),
516 default: if let Some(def) = opt_default {
517 if def.eq_ignore_ascii_case("<empty>") {
518 Some(String::from(""))
519 } else {
520 Some(String::from(def))
521 }
522 } else {
523 None
524 },
525 },
526 Rule::option_button => UciOptionConfig::Button {
527 name: String::from(name.unwrap()),
528 },
529 _ => unreachable!(),
530 };
531
532 UciMessage::Option(uoc)
533 }
534 Rule::info => {
535 let mut info_attr: Vec<UciInfoAttribute> = vec![];
536
537 for sp in pair.into_inner() {
538 match sp.as_rule() {
539 Rule::info_attribute => {
540 for spi in sp.into_inner() {
541 match spi.as_rule() {
542 Rule::info_depth => {
543 let info_depth = UciInfoAttribute::Depth(parse_u8(
544 spi,
545 Rule::digits3,
546 ));
547 info_attr.push(info_depth);
548 break;
549 }
550 Rule::info_seldepth => {
551 let info_depth = UciInfoAttribute::SelDepth(parse_u8(
552 spi,
553 Rule::digits3,
554 ));
555 info_attr.push(info_depth);
556 break;
557 }
558 Rule::info_time => {
559 let info_time = UciInfoAttribute::Time(Duration::milliseconds(parse_i64(
560 spi,
561 Rule::digits12,
562 )));
563 info_attr.push(info_time);
564 break;
565 }
566 Rule::info_nodes => {
567 let info_nodes = UciInfoAttribute::Nodes(parse_u64(
568 spi,
569 Rule::digits12,
570 ));
571 info_attr.push(info_nodes);
572 break;
573 }
574 Rule::info_currmovenum => {
575 let an_info = UciInfoAttribute::CurrMoveNum(parse_u64(
576 spi,
577 Rule::digits12,
578 )
579 as u16);
580 info_attr.push(an_info);
581 break;
582 }
583 Rule::info_hashfull => {
584 let an_info = UciInfoAttribute::HashFull(parse_u64(
585 spi,
586 Rule::digits12,
587 )
588 as u16);
589 info_attr.push(an_info);
590 break;
591 }
592 Rule::info_nps => {
593 let an_info = UciInfoAttribute::Nps(parse_u64(
594 spi,
595 Rule::digits12,
596 ));
597 info_attr.push(an_info);
598 break;
599 }
600 Rule::info_tbhits => {
601 let an_info = UciInfoAttribute::TbHits(parse_u64(
602 spi,
603 Rule::digits12,
604 ));
605 info_attr.push(an_info);
606 break;
607 }
608 Rule::info_sbhits => {
609 let an_info = UciInfoAttribute::SbHits(parse_u64(
610 spi,
611 Rule::digits12,
612 ));
613 info_attr.push(an_info);
614 break;
615 }
616 Rule::info_cpuload => {
617 let an_info = UciInfoAttribute::CpuLoad(parse_u64(
618 spi,
619 Rule::digits12,
620 )
621 as u16);
622 info_attr.push(an_info);
623 break;
624 }
625 Rule::info_multipv => {
626 let an_info = UciInfoAttribute::MultiPv(parse_u64(
627 spi,
628 Rule::digits12,
629 )
630 as u16);
631 info_attr.push(an_info);
632 break;
633 }
634 Rule::info_pv => {
635 #[cfg(not(feature = "chess"))] let mut mv: Vec<UciMove> = vec![];
636 #[cfg(feature = "chess")] let mut mv: Vec<ChessMove> = vec![];
637 for spii in spi.into_inner() {
638 match spii.as_rule() {
639 Rule::a_move => {
640 let a_move = parse_a_move(spii);
641 mv.push(a_move);
642 }
643 _ => {}
644 }
645 }
646 info_attr.push(UciInfoAttribute::Pv(mv));
647 break;
648 }
649 Rule::info_refutation => {
650 #[cfg(not(feature = "chess"))] let mut mv: Vec<UciMove> = vec![];
651 #[cfg(feature = "chess")] let mut mv: Vec<ChessMove> = vec![];
652 for spii in spi.into_inner() {
653 match spii.as_rule() {
654 Rule::a_move => {
655 let a_move = parse_a_move(spii);
656 mv.push(a_move);
657 }
658 _ => {}
659 }
660 }
661 info_attr.push(UciInfoAttribute::Refutation(mv));
662 break;
663 }
664 Rule::info_currline => {
665 #[cfg(not(feature = "chess"))] let mut mv: Vec<UciMove> = vec![];
666 #[cfg(feature = "chess")] let mut mv: Vec<ChessMove> = vec![];
667 let mut cpu_nr: Option<u16> = None;
668 for spii in spi.into_inner() {
669 match spii.as_rule() {
670 Rule::a_move => {
671 let a_move = parse_a_move(spii);
672 mv.push(a_move);
673 }
674 Rule::info_cpunr => {
675 cpu_nr =
676 Some(parse_u64(spii, Rule::digits3)
677 as u16);
678 }
679 _ => {}
680 }
681 }
682 info_attr.push(UciInfoAttribute::CurrLine {
683 cpu_nr,
684 line: mv,
685 });
686 break;
687 }
688 Rule::info_string => {
689 for spii in spi.into_inner() {
690 match spii.as_rule() {
691 Rule::info_string_string => {
692 let an_info = UciInfoAttribute::String(
693 spii.as_span().as_str().to_owned(),
694 );
695 info_attr.push(an_info);
696 break;
697 }
698 _ => {}
699 }
700 }
701 break;
702 }
703 Rule::info_currmove => {
704 for spii in spi.into_inner() {
705 match spii.as_rule() {
706 Rule::a_move => {
707 let an_info = UciInfoAttribute::CurrMove(
708 parse_a_move(spii),
709 );
710 info_attr.push(an_info);
711 break;
712 }
713 _ => {}
714 }
715 }
716 break;
717 }
718 Rule::info_score => {
719 let mut cp: Option<i32> = None;
720 let mut mate: Option<i8> = None;
721 let mut lb: Option<bool> = None;
722 let mut ub: Option<bool> = None;
723
724 for spii in spi.into_inner() {
725 match spii.as_rule() {
726 Rule::info_cp => cp = Some(parse_i64(spii, Rule::i64) as i32),
727 Rule::info_mate => mate = Some(parse_i64(spii, Rule::i64) as i8),
728 Rule::info_lowerbound => lb = Some(true),
729 Rule::info_upperbound => ub = Some(true),
730 _ => {}
731 }
732 }
733
734 info_attr.push(UciInfoAttribute::Score {
735 cp,
736 mate,
737 lower_bound: lb,
738 upper_bound: ub,
739 });
740 }
741 Rule::info_any => {
742 let mut s: Option<String> = None;
743 let mut t: Option<String> = None;
744
745 for spii in spi.into_inner() {
746 match spii.as_rule() {
747 Rule::token => {
748 t = Some(
749 spii.as_span().as_str().to_owned(),
750 );
751 }
752 Rule::info_string_string => {
753 s = Some(
754 spii.as_span().as_str().to_owned(),
755 );
756 }
757 _ => {}
758 }
759 }
760 let an_info =
761 UciInfoAttribute::Any(t.unwrap(), s.unwrap());
762 info_attr.push(an_info);
763 break;
764 }
765 _ => unreachable!(),
766 }
767 }
768 }
769 _ => unreachable!(),
770 }
771 }
772
773 UciMessage::Info(info_attr)
774 }
775 Rule::something_produced => {
776 UciMessage::Unknown(pair.as_span().as_str().to_string(), None)
777 }
778 Rule::something_produced_nl => {
779 UciMessage::Unknown(pair.as_span().as_str().trim_end().to_string(), None)
780 }
781
782 _ => unreachable!(),
783 }
784 })
785 .for_each(|msg| {
786 if let Some(a_ml) = &mut ml {
787 (*a_ml).push(msg);
788 } else {
789 single = Some(msg);
790 }
791 });
792
793 Ok(single)
794}
795
796fn parse_id_text(id_pair: Pair<Rule>, rule: Rule) -> UciMessage {
797 for sp in id_pair.into_inner() {
798 match sp.as_rule() {
799 Rule::id_text => {
800 let text = sp.as_span().as_str();
801 match rule {
802 Rule::id_name => {
803 return UciMessage::Id {
804 name: Some(String::from(text)),
805 author: None,
806 };
807 }
808 Rule::id_author => {
809 return UciMessage::Id {
810 author: Some(String::from(text)),
811 name: None,
812 };
813 }
814 _ => unreachable!(),
815 }
816 }
817 _ => {}
818 }
819 }
820
821 unreachable!();
822}
823
824#[cfg(not(feature = "chess"))]
825fn parse_square(sq_pair: Pair<Rule>) -> UciSquare {
826 let mut file: char = '\0';
827 let mut rank: u8 = 0;
828
829 match sq_pair.as_rule() {
830 Rule::square => {
831 for sp in sq_pair.into_inner() {
832 match sp.as_rule() {
833 Rule::file => {
834 file = sp.as_span().as_str().chars().into_iter().next().unwrap();
835 }
836 Rule::rank => {
837 rank = str::parse(sp.as_span().as_str()).unwrap();
838 }
839 _ => unreachable!(),
840 }
841 }
842 }
843 _ => unreachable!(),
844 }
845
846 UciSquare::from(file, rank)
847}
848
849#[cfg(feature = "chess")]
850fn parse_square(sq_pair: Pair<Rule>) -> Square {
851 let mut file: char = '\0';
852 let mut rank: u8 = 0;
853
854 match sq_pair.as_rule() {
855 Rule::square => {
856 for sp in sq_pair.into_inner() {
857 match sp.as_rule() {
858 Rule::file => {
859 file = sp.as_span().as_str().chars().into_iter().next().unwrap();
860 }
861 Rule::rank => {
862 rank = str::parse(sp.as_span().as_str()).unwrap();
863 }
864 _ => unreachable!(),
865 }
866 }
867 }
868 _ => unreachable!(),
869 }
870
871 Square::from_str(format!("{}{}", file.to_string(), rank.to_string()).as_str()).unwrap()
872}
873
874fn parse_milliseconds(pair: Pair<Rule>) -> i64 {
875 for sp in pair.into_inner() {
876 match sp.as_rule() {
877 Rule::milliseconds => {
878 return str::parse::<i64>(sp.as_span().as_str()).unwrap();
879 }
880 _ => {}
881 }
882 }
883
884 0
885}
886
887fn parse_u8(pair: Pair<Rule>, rule: Rule) -> u8 {
888 for sp in pair.into_inner() {
889 if sp.as_rule() == rule {
890 return str::parse::<u8>(sp.as_span().as_str()).unwrap();
891 }
892 }
893
894 0
895}
896
897fn parse_u64(pair: Pair<Rule>, rule: Rule) -> u64 {
898 for sp in pair.into_inner() {
899 if sp.as_rule() == rule {
900 return str::parse::<u64>(sp.as_span().as_str()).unwrap();
901 }
902 }
903
904 0
905}
906
907fn parse_i64(pair: Pair<Rule>, rule: Rule) -> i64 {
908 for sp in pair.into_inner() {
909 if sp.as_rule() == rule {
910 return str::parse::<i64>(sp.as_span().as_str()).unwrap();
911 }
912 }
913
914 0
915}
916
917#[cfg(not(feature = "chess"))]
918fn parse_a_move(sp: Pair<Rule>) -> UciMove {
919 let mut from_sq = UciSquare::default();
920 let mut to_sq = UciSquare::default();
921 let mut promotion: Option<UciPiece> = None;
922
923 for move_token in sp.into_inner() {
924 match move_token.as_rule() {
925 Rule::from_sq => {
926 from_sq = parse_square(move_token.into_inner().next().unwrap());
927 }
928 Rule::to_sq => {
929 to_sq = parse_square(move_token.into_inner().next().unwrap());
930 }
931 Rule::promotion => {
932 promotion = Some(UciPiece::from_str(move_token.as_span().as_str()).unwrap());
933 }
934 _ => unreachable!(),
935 }
936 }
937
938 UciMove {
939 from: from_sq,
940 to: to_sq,
941 promotion,
942 }
943}
944
945#[cfg(feature = "chess")]
946fn parse_a_move(sp: Pair<Rule>) -> ChessMove {
947 let mut from_sq = Square::default();
948 let mut to_sq = Square::default();
949 let mut promotion: Option<Piece> = None;
950
951 for move_token in sp.into_inner() {
952 match move_token.as_rule() {
953 Rule::from_sq => {
954 from_sq = parse_square(move_token.into_inner().next().unwrap());
955 }
956 Rule::to_sq => {
957 to_sq = parse_square(move_token.into_inner().next().unwrap());
958 }
959 Rule::promotion => {
960 promotion = Some(piece_from_str(move_token.as_span().as_str()).unwrap());
961 }
962 _ => unreachable!(),
963 }
964 }
965
966 ChessMove::new(from_sq, to_sq, promotion)
967}
968
969#[cfg(feature = "chess")]
970fn piece_from_str(s: &str) -> Result<Piece, FmtError> {
971 match s.to_ascii_lowercase().as_str() {
972 "n" => Ok(Piece::Knight),
973 "p" => Ok(Piece::Pawn),
974 "b" => Ok(Piece::Bishop),
975 "r" => Ok(Piece::Rook),
976 "k" => Ok(Piece::King),
977 "q" => Ok(Piece::Queen),
978 _ => Err(FmtError),
979 }
980}
981
982#[cfg(test)]
983mod tests {
984 use std::io::*;
985
986 use crate::uci::Serializable;
987
988 use super::*;
989
990 #[test]
991 fn test_uci() {
992 let ml = parse_strict("uci\r\nuci\r\n").unwrap();
993 assert_eq!(ml.len(), 2);
994 for mb in ml {
995 assert_eq!(mb, UciMessage::Uci);
997 }
998 }
999
1000 #[test]
1001 fn test_debug_on() {
1002 let ml = parse_strict("debug on\r\n").unwrap();
1003 assert_eq!(ml.len(), 1);
1004 assert_eq!(ml[0], UciMessage::Debug(true));
1005 }
1006
1007 #[test]
1008 fn test_debug_off() {
1009 let ml = parse_strict("debug off\n").unwrap();
1010 assert_eq!(ml.len(), 1);
1011 assert_eq!(ml[0], UciMessage::Debug(false));
1012 }
1013
1014 #[test]
1015 fn test_debugon() {
1016 parse_strict("debugon\r\n").expect_err("Should not parse 'debugon'");
1017 }
1018
1019 #[test]
1020 fn test_debug_wrong_param() {
1021 let ml = parse_strict("debug abc\r\n");
1022 assert_eq!(ml.is_err(), true);
1023 }
1024
1025 #[test]
1026 fn test_debug_cutoff() {
1027 parse_strict("debug ontario\r\n").expect_err("Should not parse");
1028 }
1029
1030 #[test]
1031 fn test_isready() {
1032 let ml = parse_strict(" \tisready \r\n").unwrap();
1033 assert_eq!(ml.len(), 1);
1034 assert_eq!(ml[0], UciMessage::IsReady);
1035 }
1036
1037 #[test]
1038 fn test_set_option_bool() {
1039 let ml = parse_strict("setoption name Nullmove value true\n").unwrap();
1040 assert_eq!(ml.len(), 1);
1041 let so = &ml[0];
1042
1043 match so {
1044 UciMessage::SetOption { name, value } => {
1045 assert_eq!(*name, String::from("Nullmove"));
1046 let val = value.clone();
1047 assert_eq!(val.is_some(), true);
1048 assert_eq!(val.unwrap().as_str(), String::from("true"));
1049 assert_eq!(so.as_bool().unwrap(), true);
1050 }
1051 _ => unreachable!(),
1052 }
1053 }
1054
1055 #[test]
1057 fn test_set_option_int() {
1058 let ml = parse_strict("setoption name Selectivity is awesome value 3\n").unwrap();
1059 assert_eq!(ml.len(), 1);
1060 let so = &ml[0];
1061
1062 match so {
1063 UciMessage::SetOption { name, value } => {
1064 assert_eq!(*name, String::from("Selectivity is awesome"));
1065 let val = value.clone();
1066 assert_eq!(val.is_some(), true);
1067 assert_eq!(val.unwrap().as_str(), String::from("3"));
1068 assert_eq!(so.as_bool().is_none(), true);
1069 assert_eq!(so.as_i32().unwrap(), 3);
1070 }
1071 _ => unreachable!(),
1072 }
1073 }
1074
1075 #[test]
1077 fn test_set_option_button() {
1078 let ml = parse_strict("setoption name Clear Hash\r\n").unwrap();
1079 assert_eq!(ml.len(), 1);
1080 let so = &ml[0];
1081
1082 match so {
1083 UciMessage::SetOption { name, value } => {
1084 assert_eq!(*name, String::from("Clear Hash"));
1085 let val = value.clone();
1086 assert_eq!(val.is_some(), false);
1087 }
1088 _ => unreachable!(),
1089 }
1090 }
1091
1092 #[test]
1093 fn test_set_option_str() {
1094 let ml =
1095 parse_strict("setoption name NalimovPath value c:\\chess\\tb\\4;c:\\chess\\tb\\5\n")
1096 .unwrap();
1097 assert_eq!(ml.len(), 1);
1098 let so = &ml[0];
1099
1100 match so {
1101 UciMessage::SetOption { name, value } => {
1102 assert_eq!(*name, String::from("NalimovPath"));
1103 let val = value.clone();
1104 assert_eq!(val.is_some(), true);
1105 assert_eq!(
1106 val.unwrap().as_str(),
1107 String::from("c:\\chess\\tb\\4;c:\\chess\\tb\\5")
1108 );
1109 assert_eq!(so.as_bool(), None);
1110 }
1111 _ => unreachable!(),
1112 }
1113 }
1114
1115 #[test]
1116 fn test_register_later() {
1117 let ml = parse_strict("REGISTER lateR\r\n").unwrap();
1118 assert_eq!(ml.len(), 1);
1119 assert_eq!(ml[0], UciMessage::register_later());
1120 }
1121
1122 #[test]
1123 fn test_register_name_code() {
1124 let ml = parse_strict("register name Matija Kejžar code 4359874324\n").unwrap();
1125 assert_eq!(ml.len(), 1);
1126 assert_eq!(
1127 ml[0],
1128 UciMessage::register_code("Matija Kejžar", "4359874324")
1129 );
1130 }
1131
1132 #[test]
1133 fn test_register_invalid() {
1134 parse_strict("register name Matija Kejžar\n").expect_err("Parse error expected.");
1135 }
1136
1137 #[test]
1138 fn test_register_invalid2() {
1139 parse_strict("register code XX-344-00LP name Matija Kejžar\n")
1140 .expect_err("Parse error expected.");
1141 }
1142
1143 #[test]
1144 fn test_ucinewgame() {
1145 let ml = parse_strict(" ucinewGAME \r\n").unwrap();
1146 assert_eq!(ml.len(), 1);
1147 assert_eq!(ml[0], UciMessage::UciNewGame);
1148 }
1149
1150 #[test]
1151 fn test_stop() {
1152 let ml = parse_strict("stop\r\n").unwrap();
1153 assert_eq!(ml.len(), 1);
1154 assert_eq!(ml[0], UciMessage::Stop);
1155 }
1156
1157 #[test]
1158 fn test_stop_really_stop() {
1159 parse_strict("stopper\r\n").expect_err("Parse error expected for 'stopper'.");
1160 }
1161
1162 #[test]
1163 fn test_ponderhit() {
1164 let ml = parse_strict("PonderHit \r\n").unwrap();
1165 assert_eq!(ml.len(), 1);
1166 assert_eq!(ml[0], UciMessage::PonderHit);
1167 }
1168
1169 #[test]
1170 fn test_quit() {
1171 let ml = parse_strict("QUIT\r\n").unwrap();
1172 assert_eq!(ml.len(), 1);
1173 assert_eq!(ml[0], UciMessage::Quit);
1174 }
1175
1176 #[test]
1177 fn test_position_startpos() {
1178 let ml = parse_strict("position startpos moves e2e4 e7e5\r\n").unwrap();
1179 assert_eq!(ml.len(), 1);
1180
1181 #[cfg(not(feature = "chess"))]
1182 {
1183 let m1 = UciMove {
1184 from: UciSquare { file: 'e', rank: 2 },
1185 to: UciSquare { file: 'e', rank: 4 },
1186 promotion: None,
1187 };
1188
1189 let m2 = UciMove {
1190 from: UciSquare { file: 'e', rank: 7 },
1191 to: UciSquare { file: 'e', rank: 5 },
1192 promotion: None,
1193 };
1194
1195 let pos = UciMessage::Position {
1196 startpos: true,
1197 fen: None,
1198 moves: vec![m1, m2],
1199 };
1200
1201 assert_eq!(ml[0], pos);
1202 }
1203
1204 #[cfg(feature = "chess")]
1205 {
1206 let m1 = ChessMove::new(Square::E2, Square::E4, None);
1207 let m2 = ChessMove::new(Square::E7, Square::E5, None);
1208
1209 let pos = UciMessage::Position {
1210 startpos: true,
1211 fen: None,
1212 moves: vec![m1, m2],
1213 };
1214
1215 assert_eq!(ml[0], pos);
1216 }
1217 }
1218
1219 #[test]
1220 fn test_position_startpos_as_fen() {
1221 let ml = parse_strict(
1222 "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 moves d2d4\r\n",
1223 )
1224 .unwrap();
1225 assert_eq!(ml.len(), 1);
1226
1227 #[cfg(not(feature = "chess"))]
1228 let m1 = UciMove {
1229 from: UciSquare { file: 'd', rank: 2 },
1230 to: UciSquare { file: 'd', rank: 4 },
1231 promotion: None,
1232 };
1233
1234 #[cfg(feature = "chess")]
1235 let m1 = ChessMove::new(Square::D2, Square::D4, None);
1236
1237 let pos = UciMessage::Position {
1238 startpos: false,
1239 fen: Some(UciFen(String::from(
1240 "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
1241 ))),
1242 moves: vec![m1],
1243 };
1244
1245 assert_eq!(ml[0], pos);
1246 }
1247
1248 #[test]
1250 fn test_position_endgame() {
1251 let ml =
1252 parse_strict("position fen 2k5/6PR/8/8/2b4P/8/6K1/8 w - - 0 53 moves g7g8q c4g8\r\n")
1253 .unwrap();
1254 assert_eq!(ml.len(), 1);
1255
1256 #[cfg(not(feature = "chess"))]
1257 let m1 = UciMove {
1258 from: UciSquare { file: 'g', rank: 7 },
1259 to: UciSquare { file: 'g', rank: 8 },
1260 promotion: Some(UciPiece::Queen),
1261 };
1262
1263 #[cfg(not(feature = "chess"))]
1264 let m2 = UciMove {
1265 from: UciSquare { file: 'c', rank: 4 },
1266 to: UciSquare { file: 'g', rank: 8 },
1267 promotion: None,
1268 };
1269
1270 #[cfg(feature = "chess")]
1271 let m1 = ChessMove::new(Square::G7, Square::G8, Some(Piece::Queen));
1272
1273 #[cfg(feature = "chess")]
1274 let m2 = ChessMove::new(Square::C4, Square::G8, None);
1275
1276 let pos = UciMessage::Position {
1277 startpos: false,
1278 fen: Some(UciFen(String::from("2k5/6PR/8/8/2b4P/8/6K1/8 w - - 0 53"))),
1279 moves: vec![m1, m2],
1280 };
1281
1282 assert_eq!(ml[0], pos);
1283 }
1284
1285 #[test]
1286 fn test_position_incorrect_fen() {
1287 parse_strict("position fen 2k50/6PR/8/8/2b4P/8/6K1/8 w - - 0 53 moves g7g8q c4g8\r\n")
1288 .expect_err("Parse should fail.");
1289 }
1290
1291 #[test]
1292 fn test_position_startpos_no_moves() {
1293 let ml = parse_strict("position startpos\r\n").unwrap();
1294 assert_eq!(ml.len(), 1);
1295
1296 let pos = UciMessage::Position {
1297 startpos: true,
1298 fen: None,
1299 moves: vec![],
1300 };
1301
1302 assert_eq!(ml[0], pos);
1303 }
1304
1305 #[test]
1306 fn test_position_fen_no_moves() {
1307 let ml = parse_strict("position fen 2k5/6PR/8/8/2b4P/8/6K1/8 w - - 0 53\r\n").unwrap();
1308 assert_eq!(ml.len(), 1);
1309
1310 let pos = UciMessage::Position {
1311 startpos: false,
1312 fen: Some(UciFen(String::from(
1313 "2k5/6PR/8/8/2b4P/8/6K1/8 w - - 0 53",
1314 ))),
1315 moves: vec![],
1316 };
1317
1318 assert_eq!(ml[0], pos);
1319 }
1320
1321 #[test]
1322 fn test_go_ponder() {
1323 let ml = parse_strict("go ponder\n").unwrap();
1324 assert_eq!(ml.len(), 1);
1325
1326 assert_eq!(ml[0], UciMessage::go_ponder());
1327 }
1328
1329 #[test]
1330 fn test_go_infinite() {
1331 let ml = parse_strict("go infinite\n").unwrap();
1332 assert_eq!(ml.len(), 1);
1333
1334 assert_eq!(ml[0], UciMessage::go_infinite());
1335 }
1336
1337 #[test]
1338 fn test_go_movetime() {
1339 let ml = parse_strict("go movetime 55055\n").unwrap();
1340 assert_eq!(ml.len(), 1);
1341
1342 assert_eq!(
1343 ml[0],
1344 UciMessage::go_movetime(Duration::milliseconds(55055))
1345 );
1346 }
1347
1348 #[test]
1349 fn test_go_timeleft() {
1350 let ml = parse_strict("go wtime 903000 btime 770908 winc 15000 movestogo 17 binc 10000\n")
1351 .unwrap();
1352 assert_eq!(ml.len(), 1);
1353
1354 let tl = UciTimeControl::TimeLeft {
1355 white_time: Some(Duration::milliseconds(903000)),
1356 black_time: Some(Duration::milliseconds(770908)),
1357 white_increment: Some(Duration::milliseconds(15000)),
1358 black_increment: Some(Duration::milliseconds(10000)),
1359 moves_to_go: Some(17),
1360 };
1361
1362 assert_eq!(
1363 ml[0],
1364 UciMessage::Go {
1365 search_control: None,
1366 time_control: Some(tl),
1367 }
1368 );
1369 }
1370
1371 #[test]
1372 fn test_search_control_depth() {
1373 let ml = parse_strict("go ponder depth 6\n").unwrap();
1374 assert_eq!(ml.len(), 1);
1375
1376 let result = UciMessage::Go {
1377 time_control: Some(UciTimeControl::Ponder),
1378 search_control: Some(UciSearchControl::depth(6)),
1379 };
1380
1381 assert_eq!(ml[0], result);
1382 }
1383
1384 #[test]
1385 fn test_search_control_mate() {
1386 let ml = parse_strict("go mate 12\n").unwrap();
1387 assert_eq!(ml.len(), 1);
1388
1389 let result = UciMessage::Go {
1390 time_control: None,
1391 search_control: Some(UciSearchControl::mate(12)),
1392 };
1393
1394 assert_eq!(ml[0], result);
1395 }
1396
1397 #[test]
1398 fn test_nodes_searchmoves() {
1399 let ml = parse_strict("go nodes 79093455456 searchmoves e2e4 d2d4 g2g1n\n").unwrap();
1400 assert_eq!(ml.len(), 1);
1401
1402 #[cfg(not(feature = "chess"))]
1403 let sc = UciSearchControl {
1404 depth: None,
1405 nodes: Some(79093455456),
1406 mate: None,
1407 search_moves: vec![
1408 UciMove::from_to(UciSquare::from('e', 2), UciSquare::from('e', 4)),
1409 UciMove::from_to(UciSquare::from('d', 2), UciSquare::from('d', 4)),
1410 UciMove {
1411 from: UciSquare::from('g', 2),
1412 to: UciSquare::from('g', 1),
1413 promotion: Some(UciPiece::Knight),
1414 },
1415 ],
1416 };
1417
1418 #[cfg(feature = "chess")]
1419 let sc = UciSearchControl {
1420 depth: None,
1421 nodes: Some(79093455456),
1422 mate: None,
1423 search_moves: vec![
1424 ChessMove::new(Square::E2, Square::E4, None),
1425 ChessMove::new(Square::D2, Square::D4, None),
1426 ChessMove::new(Square::G2, Square::G1, Some(Piece::Knight)),
1427 ],
1428 };
1429
1430 let result = UciMessage::Go {
1431 time_control: None,
1432 search_control: Some(sc),
1433 };
1434
1435 assert_eq!(ml[0], result);
1436 }
1437
1438 #[test]
1439 fn test_go_full_example() {
1440 let ml =
1441 parse_strict("go movetime 10000 searchmoves a1h8 depth 6 nodes 55000000\n").unwrap();
1442 assert_eq!(ml.len(), 1);
1443
1444 let tc = UciTimeControl::MoveTime(Duration::milliseconds(10000));
1445
1446 #[cfg(not(feature = "chess"))]
1447 let sc = UciSearchControl {
1448 depth: Some(6),
1449 nodes: Some(55000000),
1450 mate: None,
1451 search_moves: vec![UciMove::from_to(
1452 UciSquare::from('a', 1),
1453 UciSquare::from('h', 8),
1454 )],
1455 };
1456
1457 #[cfg(feature = "chess")]
1458 let sc = UciSearchControl {
1459 depth: Some(6),
1460 nodes: Some(55000000),
1461 mate: None,
1462 search_moves: vec![ChessMove::new(Square::A1, Square::H8, None)],
1463 };
1464
1465 let result = UciMessage::Go {
1466 time_control: Some(tc),
1467 search_control: Some(sc),
1468 };
1469
1470 assert_eq!(ml[0], result);
1471 }
1472
1473 #[test]
1474 fn test_two_command_doc_example() {
1475 let ml = parse_strict("position startpos\ngo ponder searchmoves e2e4 d2d4\n").unwrap();
1476 assert_eq!(ml.len(), 2);
1477 }
1478
1479 #[test]
1480 fn test_lax_mode() {
1481 let ml = parse("position startpos\nunknown command\ngo ponder searchmoves e2e4 d2d4\n");
1482 assert_eq!(ml.len(), 2);
1483
1484 match ml[0] {
1485 UciMessage::Position { .. } => {}
1486 _ => panic!("Expected a `position` message here"),
1487 };
1488
1489 match ml[1] {
1490 UciMessage::Go { .. } => {}
1491 _ => panic!("Expected a `go` message here"),
1492 };
1493 }
1494
1495 #[test]
1496 #[should_panic]
1497 fn test_strict_mode() {
1498 parse_strict("position startpos\nunknown command\ngo ponder searchmoves e2e4 d2d4\n")
1499 .unwrap();
1500 }
1501
1502 #[test]
1503 fn test_id() {
1504 let ml = parse_strict("id name Vampirc 1.0\nid author Matija Kejžar\n").unwrap();
1505 assert_eq!(ml.len(), 2);
1506
1507 let result = UciMessage::Id {
1508 name: Some("Vampirc 1.0".to_string()),
1509 author: None,
1510 };
1511
1512 assert_eq!(ml[0], result);
1513
1514 let result2 = UciMessage::Id {
1515 author: Some("Matija Kejžar".to_string()),
1516 name: None,
1517 };
1518
1519 assert_eq!(ml[1], result2);
1520 }
1521
1522 #[test]
1523 fn test_uciok() {
1524 let ml = parse_strict("uci\n uciok \r\n").unwrap();
1525 assert_eq!(ml.len(), 2);
1526 assert_eq!(ml[0], UciMessage::Uci);
1527 assert_eq!(ml[1], UciMessage::UciOk);
1528 }
1529
1530 #[test]
1531 fn test_readyok() {
1532 let ml = parse_strict("isready\nreadyok\n").unwrap();
1533 assert_eq!(ml.len(), 2);
1534 assert_eq!(ml[0], UciMessage::IsReady);
1535 assert_eq!(ml[1], UciMessage::ReadyOk);
1536 }
1537
1538 #[test]
1540 fn test_bestmove() {
1541 let ml = parse_strict("bestmove g1f3\n").unwrap();
1542 assert_eq!(ml.len(), 1);
1543
1544 #[cfg(not(feature = "chess"))]
1545 let m = UciMessage::BestMove {
1546 best_move: UciMove {
1547 from: UciSquare::from('g', 1),
1548 to: UciSquare::from('f', 3),
1549 promotion: None,
1550 },
1551
1552 ponder: None,
1553 };
1554
1555 #[cfg(feature = "chess")]
1556 let m = UciMessage::BestMove {
1557 best_move: ChessMove::new(Square::G1, Square::F3, None),
1558
1559 ponder: None,
1560 };
1561
1562 assert_eq!(m, ml[0]);
1563 }
1564
1565 #[test]
1567 fn test_bestmove_with_ponder() {
1568 let ml = parse_strict("bestmove g1f3 ponder d8f6\n").unwrap();
1569 assert_eq!(ml.len(), 1);
1570
1571 #[cfg(not(feature = "chess"))]
1572 let m = UciMessage::BestMove {
1573 best_move: UciMove {
1574 from: UciSquare::from('g', 1),
1575 to: UciSquare::from('f', 3),
1576 promotion: None,
1577 },
1578
1579 ponder: Some(UciMove {
1580 from: UciSquare::from('d', 8),
1581 to: UciSquare::from('f', 6),
1582 promotion: None,
1583 }),
1584 };
1585
1586 #[cfg(feature = "chess")]
1587 let m = UciMessage::BestMove {
1588 best_move: ChessMove::new(Square::G1, Square::F3, None),
1589
1590 ponder: Some(ChessMove::new(Square::D8, Square::F6, None)),
1591 };
1592
1593 assert_eq!(m, ml[0]);
1594 }
1595
1596 #[test]
1597 fn test_copyprotection() {
1598 let ml = parse_strict("copyprotection checking\ncopyprotection ok\n").unwrap();
1599 assert_eq!(ml.len(), 2);
1600 assert_eq!(ml[0], UciMessage::CopyProtection(ProtectionState::Checking));
1601 assert_eq!(ml[1], UciMessage::CopyProtection(ProtectionState::Ok));
1602 }
1603
1604 #[test]
1605 fn test_registration() {
1606 let ml = parse_strict("registration checking\nregistration error\n").unwrap();
1607 assert_eq!(ml.len(), 2);
1608 assert_eq!(ml[0], UciMessage::Registration(ProtectionState::Checking));
1609 assert_eq!(ml[1], UciMessage::Registration(ProtectionState::Error));
1610 }
1611
1612 #[test]
1613 fn test_parse_option_check() {
1614 let ml = parse_strict("option name Nullmove type check default true\n").unwrap();
1615
1616 let m = UciMessage::Option(UciOptionConfig::Check {
1617 name: "Nullmove".to_string(),
1618 default: Some(true),
1619 });
1620
1621 assert_eq!(m, ml[0]);
1622 }
1623
1624 #[test]
1625 fn test_parse_option_check_no_default() {
1626 let ml = parse_strict("option name A long option name type check \n").unwrap();
1627
1628 let m = UciMessage::Option(UciOptionConfig::Check {
1629 name: "A long option name".to_string(),
1630 default: None,
1631 });
1632
1633 assert_eq!(m, ml[0]);
1634 }
1635
1636 #[test]
1637 fn test_parse_option_spin() {
1638 let ml =
1639 parse_strict("option name Selectivity type spin default 2 min 0 max 4\n\n").unwrap();
1640
1641 let m = UciMessage::Option(UciOptionConfig::Spin {
1642 name: "Selectivity".to_string(),
1643 default: Some(2),
1644 min: Some(0),
1645 max: Some(4),
1646 });
1647
1648 assert_eq!(m, ml[0]);
1649 }
1650
1651 #[test]
1652 fn test_parse_option_spin_no_default() {
1653 let ml = parse_strict(
1654 "option name A spin option without a default type spin min -5676 max -33\n",
1655 )
1656 .unwrap();
1657
1658 let m = UciMessage::Option(UciOptionConfig::Spin {
1659 name: "A spin option without a default".to_string(),
1660 default: None,
1661 min: Some(-5676),
1662 max: Some(-33),
1663 });
1664
1665 assert_eq!(m, ml[0]);
1666 }
1667
1668 #[test]
1669 fn test_parse_option_spin_just_min() {
1670 let ml = parse_strict("option name JUST MIN type spin min -40964656\n").unwrap();
1671
1672 let m = UciMessage::Option(UciOptionConfig::Spin {
1673 name: "JUST MIN".to_string(),
1674 default: None,
1675 min: Some(-40964656),
1676 max: None,
1677 });
1678
1679 assert_eq!(m, ml[0]);
1680 }
1681
1682 #[test]
1683 fn test_parse_option_spin_just_max() {
1684 let ml = parse_strict("option name just_max type spin max 56565464509\n").unwrap();
1685
1686 let m = UciMessage::Option(UciOptionConfig::Spin {
1687 name: "just_max".to_string(),
1688 default: None,
1689 max: Some(56565464509),
1690 min: None,
1691 });
1692
1693 assert_eq!(m, ml[0]);
1694 }
1695
1696 #[test]
1697 fn test_parse_option_spin_just_default_and_max() {
1698 let ml = parse_strict("option name def max type spin default -5 max 55\n").unwrap();
1699
1700 let m = UciMessage::Option(UciOptionConfig::Spin {
1701 name: "def max".to_string(),
1702 default: Some(-5),
1703 max: Some(55),
1704 min: None,
1705 });
1706
1707 assert_eq!(m, ml[0]);
1708 }
1709
1710 #[test]
1711 fn test_parse_option_combo() {
1712 let ml = parse_strict(
1713 "option name Style type combo default Normal var Solid var Normal var Risky\n",
1714 )
1715 .unwrap();
1716
1717 let m = UciMessage::Option(UciOptionConfig::Combo {
1718 name: "Style".to_string(),
1719 default: Some("Normal".to_string()),
1720 var: vec![
1721 String::from("Solid"),
1722 String::from("Normal"),
1723 String::from("Risky"),
1724 ],
1725 });
1726
1727 assert_eq!(m, ml[0]);
1728 }
1729
1730 #[test]
1731 fn test_parse_option_combo_no_default() {
1732 let ml = parse_strict(
1733 "option name Some ccccc-combo type combo var A B C var D E F var 1 2 3\n",
1734 )
1735 .unwrap();
1736
1737 let m = UciMessage::Option(UciOptionConfig::Combo {
1738 name: "Some ccccc-combo".to_string(),
1739 default: None,
1740 var: vec![
1741 String::from("A B C"),
1742 String::from("D E F"),
1743 String::from("1 2 3"),
1744 ],
1745 });
1746
1747 assert_eq!(m, ml[0]);
1748 }
1749
1750 #[test]
1751 fn test_parse_option_string() {
1752 let ml = parse_strict("option name Nalimov Path type string default c:\\\n").unwrap();
1753
1754 let m = UciMessage::Option(UciOptionConfig::String {
1755 name: "Nalimov Path".to_string(),
1756 default: Some("c:\\".to_string()),
1757 });
1758
1759 assert_eq!(m, ml[0]);
1760 }
1761
1762 #[test]
1763 fn test_parse_option_string_no_default() {
1764 let ml = parse_strict("option name NP type string\r\n").unwrap();
1765
1766 let m = UciMessage::Option(UciOptionConfig::String {
1767 name: "NP".to_string(),
1768 default: None,
1769 });
1770
1771 assert_eq!(m, ml[0]);
1772 }
1773
1774 #[test]
1775 fn test_parse_option_button() {
1776 let ml = parse_strict("option name Clear Hash type button\n").unwrap();
1777
1778 let m = UciMessage::Option(UciOptionConfig::Button {
1779 name: "Clear Hash".to_string(),
1780 });
1781
1782 assert_eq!(m, ml[0]);
1783 }
1784
1785 #[test]
1786 fn test_parse_option_button_ignore_default() {
1787 let ml =
1788 parse_strict("option name CH type button default Ignore min 5 max 6 var A var B\n")
1789 .unwrap();
1790
1791 let m = UciMessage::Option(UciOptionConfig::Button {
1792 name: "CH".to_string(),
1793 });
1794
1795 assert_eq!(m, ml[0]);
1796 }
1797
1798 #[test]
1799 fn test_parse_option_string_empty() {
1800 let ml = parse_strict("option name Nalimov Path type string default <empty>\n").unwrap();
1801
1802 let m = UciMessage::Option(UciOptionConfig::String {
1803 name: "Nalimov Path".to_string(),
1804 default: Some("".to_string()),
1805 });
1806
1807 assert_eq!(m, ml[0]);
1808 }
1809
1810 #[test]
1811 fn test_parse_info_depth() {
1812 let ml = parse_strict("info depth 23\n").unwrap();
1813
1814 let m = UciMessage::Info(vec![UciInfoAttribute::Depth(23)]);
1815
1816 assert_eq!(m, ml[0]);
1817 }
1818
1819 #[test]
1820 fn test_parse_info_seldepth() {
1821 let ml = parse_strict("info seldepth 9\n").unwrap();
1822
1823 let m = UciMessage::Info(vec![UciInfoAttribute::SelDepth(9)]);
1824
1825 assert_eq!(m, ml[0]);
1826 }
1827
1828 #[test]
1829 fn test_parse_info_time() {
1830 let ml = parse_strict("info time 9002\n").unwrap();
1831
1832 let m = UciMessage::Info(vec![UciInfoAttribute::Time(Duration::milliseconds(9002))]);
1833
1834 assert_eq!(m, ml[0]);
1835 }
1836
1837 #[test]
1838 fn test_parse_info_nodes() {
1839 let ml = parse_strict("info nodes 56435234425\n").unwrap();
1840
1841 let m = UciMessage::Info(vec![UciInfoAttribute::Nodes(56435234425)]);
1842
1843 assert_eq!(m, ml[0]);
1844 }
1845
1846 #[test]
1847 fn test_parse_info_currmovenum() {
1848 let ml = parse_strict("info currmovenum 102\n").unwrap();
1849
1850 let m = UciMessage::Info(vec![UciInfoAttribute::CurrMoveNum(102)]);
1851
1852 assert_eq!(m, ml[0]);
1853 }
1854
1855 #[test]
1856 fn test_parse_info_hashfull() {
1857 let ml = parse_strict("info hashfull 673\n").unwrap();
1858
1859 let m = UciMessage::Info(vec![UciInfoAttribute::HashFull(673)]);
1860
1861 assert_eq!(m, ml[0]);
1862 }
1863
1864 #[test]
1865 fn test_parse_info_nps() {
1866 let ml = parse_strict("info nps 12003\n").unwrap();
1867
1868 let m = UciMessage::Info(vec![UciInfoAttribute::Nps(12003)]);
1869
1870 assert_eq!(m, ml[0]);
1871 }
1872
1873 #[test]
1874 fn test_parse_info_tbhits() {
1875 let ml = parse_strict("info tbhits 5305\n").unwrap();
1876
1877 let m = UciMessage::Info(vec![UciInfoAttribute::TbHits(5305)]);
1878
1879 assert_eq!(m, ml[0]);
1880 }
1881
1882 #[test]
1883 fn test_parse_info_sbhits() {
1884 let ml = parse_strict("info sbhits 0\n").unwrap();
1885
1886 let m = UciMessage::Info(vec![UciInfoAttribute::SbHits(0)]);
1887
1888 assert_eq!(m, ml[0]);
1889 }
1890
1891 #[test]
1892 fn test_parse_info_cpuload() {
1893 let ml = parse_strict("info cpuload 773\n").unwrap();
1894
1895 let m = UciMessage::Info(vec![UciInfoAttribute::CpuLoad(773)]);
1896
1897 assert_eq!(m, ml[0]);
1898 }
1899
1900 #[test]
1901 fn test_parse_info_multipv() {
1902 let ml = parse_strict("info multipv 2\n").unwrap();
1903
1904 let m = UciMessage::Info(vec![UciInfoAttribute::MultiPv(2)]);
1905
1906 assert_eq!(m, ml[0]);
1907 }
1908
1909 #[test]
1910 fn test_parse_info_string() {
1911 let ml = parse_strict("info string I am the Walrus! Cuckoo cachoo.\n").unwrap();
1912
1913 let m = UciMessage::Info(vec![UciInfoAttribute::String(
1914 "I am the Walrus! Cuckoo cachoo.".to_owned(),
1915 )]);
1916
1917 assert_eq!(m, ml[0]);
1918 }
1919
1920 #[test]
1921 fn test_parse_info_any() {
1922 let ml = parse_strict("info UCI_Whatever -29 A3 57\n").unwrap();
1923
1924 let m = UciMessage::Info(vec![UciInfoAttribute::Any(
1925 "UCI_Whatever".to_owned(),
1926 "-29 A3 57".to_owned(),
1927 )]);
1928
1929 assert_eq!(m, ml[0]);
1930 }
1931
1932 #[test]
1933 fn test_parse_info_currmove() {
1934 let ml = parse_strict("info currmove a7a8q\n").unwrap();
1935
1936 #[cfg(not(feature = "chess"))]
1937 let m = UciMessage::Info(vec![UciInfoAttribute::CurrMove(UciMove {
1938 from: UciSquare::from('a', 7),
1939 to: UciSquare::from('a', 8),
1940 promotion: Some(UciPiece::Queen),
1941 })]);
1942
1943 #[cfg(feature = "chess")]
1944 let m = UciMessage::Info(vec![UciInfoAttribute::CurrMove(ChessMove::new(
1945 Square::A7,
1946 Square::A8,
1947 Some(Piece::Queen),
1948 ))]);
1949
1950 assert_eq!(m, ml[0]);
1951 }
1952
1953 #[test]
1954 fn test_parse_info_pv() {
1955 let ml = parse_strict("info pv e2e4 e7e5 g1f3\n").unwrap();
1956
1957 #[cfg(not(feature = "chess"))]
1958 let m = UciMessage::Info(vec![UciInfoAttribute::Pv(vec![
1959 UciMove::from_to(UciSquare::from('e', 2), UciSquare::from('e', 4)),
1960 UciMove::from_to(UciSquare::from('e', 7), UciSquare::from('e', 5)),
1961 UciMove::from_to(UciSquare::from('g', 1), UciSquare::from('f', 3)),
1962 ])]);
1963
1964 #[cfg(feature = "chess")]
1965 let m = UciMessage::Info(vec![UciInfoAttribute::Pv(vec![
1966 ChessMove::new(Square::E2, Square::E4, None),
1967 ChessMove::new(Square::E7, Square::E5, None),
1968 ChessMove::new(Square::G1, Square::F3, None),
1969 ])]);
1970
1971 assert_eq!(m, ml[0]);
1972 }
1973
1974 #[test]
1975 fn test_parse_info_refutation() {
1976 let ml = parse_strict("info refutation d1h5 g6h5\n").unwrap();
1977
1978 #[cfg(not(feature = "chess"))]
1979 let m = UciMessage::Info(vec![UciInfoAttribute::Refutation(vec![
1980 UciMove::from_to(UciSquare::from('d', 1), UciSquare::from('h', 5)),
1981 UciMove::from_to(UciSquare::from('g', 6), UciSquare::from('h', 5)),
1982 ])]);
1983
1984 #[cfg(feature = "chess")]
1985 let m = UciMessage::Info(vec![UciInfoAttribute::Refutation(vec![
1986 ChessMove::new(Square::D1, Square::H5, None),
1987 ChessMove::new(Square::G6, Square::H5, None),
1988 ])]);
1989
1990 assert_eq!(m, ml[0]);
1991 }
1992
1993 #[test]
1994 fn test_info_currline_no_cpu_nr() {
1995 let ml = parse_strict("info currline d1h5 g6h5\n").unwrap();
1996
1997 #[cfg(not(feature = "chess"))]
1998 let m = UciMessage::Info(vec![UciInfoAttribute::CurrLine {
1999 cpu_nr: None,
2000 line: vec![
2001 UciMove::from_to(UciSquare::from('d', 1), UciSquare::from('h', 5)),
2002 UciMove::from_to(UciSquare::from('g', 6), UciSquare::from('h', 5)),
2003 ],
2004 }]);
2005
2006 #[cfg(feature = "chess")]
2007 let m = UciMessage::Info(vec![UciInfoAttribute::CurrLine {
2008 cpu_nr: None,
2009 line: vec![
2010 ChessMove::new(Square::D1, Square::H5, None),
2011 ChessMove::new(Square::G6, Square::H5, None),
2012 ],
2013 }]);
2014
2015 assert_eq!(m, ml[0]);
2016 }
2017
2018 #[test]
2019 fn test_info_currline_with_cpu_nr() {
2020 let ml = parse_strict("info currline 1 d1h5 g6h5\n").unwrap();
2021
2022 #[cfg(not(feature = "chess"))]
2023 let m = UciMessage::Info(vec![UciInfoAttribute::CurrLine {
2024 cpu_nr: Some(1),
2025 line: vec![
2026 UciMove::from_to(UciSquare::from('d', 1), UciSquare::from('h', 5)),
2027 UciMove::from_to(UciSquare::from('g', 6), UciSquare::from('h', 5)),
2028 ],
2029 }]);
2030
2031 #[cfg(feature = "chess")]
2032 let m = UciMessage::Info(vec![UciInfoAttribute::CurrLine {
2033 cpu_nr: Some(1),
2034 line: vec![
2035 ChessMove::new(Square::D1, Square::H5, None),
2036 ChessMove::new(Square::G6, Square::H5, None),
2037 ],
2038 }]);
2039
2040 assert_eq!(m, ml[0]);
2041 }
2042
2043 #[test]
2044 fn test_info_currline_multi_cpu_nr() {
2045 let ml = parse_strict("info currline 1 d1h5 g6h5 currline 2 e2e4 currline 3 d2d4 d7d5\n")
2046 .unwrap();
2047
2048 #[cfg(not(feature = "chess"))]
2049 let m = UciMessage::Info(vec![
2050 UciInfoAttribute::CurrLine {
2051 cpu_nr: Some(1),
2052 line: vec![
2053 UciMove::from_to(UciSquare::from('d', 1), UciSquare::from('h', 5)),
2054 UciMove::from_to(UciSquare::from('g', 6), UciSquare::from('h', 5)),
2055 ],
2056 },
2057 UciInfoAttribute::CurrLine {
2058 cpu_nr: Some(2),
2059 line: vec![UciMove::from_to(
2060 UciSquare::from('e', 2),
2061 UciSquare::from('e', 4),
2062 )],
2063 },
2064 UciInfoAttribute::CurrLine {
2065 cpu_nr: Some(3),
2066 line: vec![
2067 UciMove::from_to(UciSquare::from('d', 2), UciSquare::from('d', 4)),
2068 UciMove::from_to(UciSquare::from('d', 7), UciSquare::from('d', 5)),
2069 ],
2070 },
2071 ]);
2072
2073 #[cfg(feature = "chess")]
2074 let m = UciMessage::Info(vec![
2075 UciInfoAttribute::CurrLine {
2076 cpu_nr: Some(1),
2077 line: vec![
2078 ChessMove::new(Square::D1, Square::H5, None),
2079 ChessMove::new(Square::G6, Square::H5, None),
2080 ],
2081 },
2082 UciInfoAttribute::CurrLine {
2083 cpu_nr: Some(2),
2084 line: vec![ChessMove::new(Square::E2, Square::E4, None)],
2085 },
2086 UciInfoAttribute::CurrLine {
2087 cpu_nr: Some(3),
2088 line: vec![
2089 ChessMove::new(Square::D2, Square::D4, None),
2090 ChessMove::new(Square::D7, Square::D5, None),
2091 ],
2092 },
2093 ]);
2094
2095 assert_eq!(m, ml[0]);
2096 }
2097
2098 #[test]
2099 fn test_info_score_cp() {
2100 let ml = parse_strict("info score cp 20\n").unwrap();
2101
2102 let m = UciMessage::Info(vec![UciInfoAttribute::from_centipawns(20)]);
2103
2104 assert_eq!(m, ml[0]);
2105 }
2106
2107 #[test]
2108 fn test_info_score_mate() {
2109 let ml = parse_strict("info score mate -3\n").unwrap();
2110
2111 let m = UciMessage::Info(vec![UciInfoAttribute::from_mate(-3)]);
2112
2113 assert_eq!(m, ml[0]);
2114 }
2115
2116 #[test]
2117 fn test_info_score_cp_lowerbound() {
2118 let ml = parse_strict("info score cp -75 lowerbound\n").unwrap();
2119
2120 let m = UciMessage::Info(vec![UciInfoAttribute::Score {
2121 cp: Some(-75),
2122 mate: None,
2123 lower_bound: Some(true),
2124 upper_bound: None,
2125 }]);
2126
2127 assert_eq!(m, ml[0]);
2128 }
2129
2130 #[test]
2131 fn test_info_score_cp_upperbound() {
2132 let ml = parse_strict("info score cp 404 upperbound\n").unwrap();
2133
2134 let m = UciMessage::Info(vec![UciInfoAttribute::Score {
2135 cp: Some(404),
2136 mate: None,
2137 upper_bound: Some(true),
2138 lower_bound: None,
2139 }]);
2140
2141 assert_eq!(m, ml[0]);
2142 }
2143
2144 #[test]
2146 fn test_info_multi1() {
2147 let ml = parse_strict("info score cp 13 depth 1 nodes 13 time 15 pv f1b5\n").unwrap();
2148 println!("{}", ml[0].serialize());
2149 assert_eq!(1, ml.len());
2150
2151 #[cfg(not(feature = "chess"))]
2152 let m = UciMessage::Info(vec![
2153 UciInfoAttribute::from_centipawns(13),
2154 UciInfoAttribute::Depth(1),
2155 UciInfoAttribute::Nodes(13),
2156 UciInfoAttribute::Time(Duration::milliseconds(15)),
2157 UciInfoAttribute::Pv(vec![UciMove::from_to(
2158 UciSquare::from('f', 1),
2159 UciSquare::from('b', 5),
2160 )]),
2161 ]);
2162
2163 #[cfg(feature = "chess")]
2164 let m = UciMessage::Info(vec![
2165 UciInfoAttribute::from_centipawns(13),
2166 UciInfoAttribute::Depth(1),
2167 UciInfoAttribute::Nodes(13),
2168 UciInfoAttribute::Time(Duration::milliseconds(15)),
2169 UciInfoAttribute::Pv(vec![ChessMove::new(Square::F1, Square::B5, None)]),
2170 ]);
2171
2172 assert_eq!(m, ml[0]);
2173
2174 assert_eq!(
2175 m.serialize(),
2176 "info score cp 13 depth 1 nodes 13 time 15 pv f1b5"
2177 )
2178 }
2179
2180 #[test]
2182 fn test_info_multi2() {
2183 let ml = parse_strict("info depth 2 seldepth 2\n").unwrap();
2184 println!("{}", ml[0].serialize());
2185 assert_eq!(1, ml.len());
2186
2187 let m = UciMessage::Info(vec![
2188 UciInfoAttribute::Depth(2),
2189 UciInfoAttribute::SelDepth(2),
2190 ]);
2191
2192 assert_eq!(m, ml[0]);
2193 assert_eq!(m.serialize(), "info depth 2 seldepth 2")
2194 }
2195
2196 #[test]
2198 fn test_info_multi3() {
2199 let ml = parse_strict("info score cp 20 depth 3 nodes 423 time 15 pv f1c4 g8f6 b1c3 \n")
2200 .unwrap();
2201 println!("{}", ml[0].serialize());
2202 assert_eq!(1, ml.len());
2203
2204 #[cfg(not(feature = "chess"))]
2205 let m = UciMessage::Info(vec![
2206 UciInfoAttribute::from_centipawns(20),
2207 UciInfoAttribute::Depth(3),
2208 UciInfoAttribute::Nodes(423),
2209 UciInfoAttribute::Time(Duration::milliseconds(15)),
2210 UciInfoAttribute::Pv(vec![
2211 UciMove::from_to(UciSquare::from('f', 1), UciSquare::from('c', 4)),
2212 UciMove::from_to(UciSquare::from('g', 8), UciSquare::from('f', 6)),
2213 UciMove::from_to(UciSquare::from('b', 1), UciSquare::from('c', 3)),
2214 ]),
2215 ]);
2216
2217 #[cfg(feature = "chess")]
2218 let m = UciMessage::Info(vec![
2219 UciInfoAttribute::from_centipawns(20),
2220 UciInfoAttribute::Depth(3),
2221 UciInfoAttribute::Nodes(423),
2222 UciInfoAttribute::Time(Duration::milliseconds(15)),
2223 UciInfoAttribute::Pv(vec![
2224 ChessMove::new(Square::F1, Square::C4, None),
2225 ChessMove::new(Square::G8, Square::F6, None),
2226 ChessMove::new(Square::B1, Square::C3, None),
2227 ]),
2228 ]);
2229
2230 assert_eq!(m, ml[0]);
2231
2232 assert_eq!(
2233 m.serialize(),
2234 "info score cp 20 depth 3 nodes 423 time 15 pv f1c4 g8f6 b1c3"
2235 )
2236 }
2237
2238 #[test]
2239 fn test_parse_with_unknown() {
2240 let ml = parse_with_unknown("not really a message\n");
2241 assert_eq!(1, ml.len());
2242 assert_eq!(ml[0].is_unknown(), true);
2243
2244 match &ml[0] {
2245 UciMessage::Unknown(msg, _) => {
2246 assert_eq!(msg.as_str(), "not really a message");
2247 }
2248 _ => panic!("Expected a message of type UnknownMessage"),
2249 }
2250 }
2251
2252 #[test]
2253 fn test_parse_with_unknown_success() {
2254 let ml = parse_with_unknown("uci\nuciok\n");
2255 assert_eq!(2, ml.len());
2256 assert_eq!(ml[0].is_unknown(), false);
2257 assert_eq!(ml[1].is_unknown(), false);
2258
2259 assert_eq!(ml, vec![UciMessage::Uci, UciMessage::UciOk]);
2260 }
2261
2262 #[test]
2263 fn test_parse_go() {
2264 parse("go\n");
2265 let msgs = parse_strict("go\n").unwrap();
2266 assert_eq!(msgs.len(), 1);
2267 assert_eq!(msgs[0], UciMessage::go())
2268 }
2269
2270 #[test]
2271 fn test_parse_go_with_space() {
2272 parse("go\n");
2273 let msgs = parse_strict("go \n").unwrap();
2274 assert_eq!(msgs.len(), 1);
2275 assert_eq!(msgs[0], UciMessage::go())
2276 }
2277
2278 #[test]
2279 fn test_parse_go_eoi() {
2280 parse("go");
2281 let msg = parse_one("go");
2282 assert_eq!(msg, UciMessage::go())
2283 }
2284
2285 #[ignore]
2286 #[test]
2287 fn test_parse_stdin() {
2288 println!("Enter uci command: ");
2289 for line in stdin().lock().lines() {
2290 println!("got line: {:?}", line);
2291 let msgs: MessageList = parse(&line.unwrap());
2292 for msg in msgs {
2293 println!("parsed: {}", msg);
2294 }
2295 }
2296 }
2297
2298 #[test]
2299 fn test_no_line_at_end_multi_parse() {
2300 let msgs = parse("uci\ndebug on\nucinewgame\nstop\nquit");
2301 assert_eq!(msgs.len(), 5);
2302 assert_eq!(msgs[0], UciMessage::Uci);
2303 assert_eq!(msgs[1], UciMessage::Debug(true));
2304 assert_eq!(msgs[2], UciMessage::UciNewGame);
2305 assert_eq!(msgs[3], UciMessage::Stop);
2306 assert_eq!(msgs[4], UciMessage::Quit);
2307 }
2308
2309 #[test]
2310 fn test_no_line_at_end_single_parse() {
2311 let msgs = parse("uci\n");
2312 assert_eq!(msgs.len(), 1);
2313 assert_eq!(msgs[0], UciMessage::Uci);
2314
2315 let msgs2 = parse("go ponder\n");
2316 assert_eq!(msgs2.len(), 1);
2317 assert_eq!(msgs2[0], UciMessage::go_ponder());
2318 }
2319
2320 #[test]
2321 fn test_no_line_at_end_multi_parse_strict() {
2322 let msgs = parse("uci\ndebug on\nucinewgame\nstop\nquit");
2323 assert_eq!(msgs.len(), 5);
2324 assert_eq!(msgs[0], UciMessage::Uci);
2325 assert_eq!(msgs[1], UciMessage::Debug(true));
2326 assert_eq!(msgs[2], UciMessage::UciNewGame);
2327 assert_eq!(msgs[3], UciMessage::Stop);
2328 assert_eq!(msgs[4], UciMessage::Quit);
2329 }
2330
2331 #[test]
2332 fn test_no_line_at_end_single_parse_strict() {
2333 let msgs = parse_strict("uci\n").unwrap();
2334 assert_eq!(msgs.len(), 1);
2335 assert_eq!(msgs[0], UciMessage::Uci);
2336
2337 let msgs2 = parse_strict("info score cp 20").unwrap();
2338 assert_eq!(msgs2.len(), 1);
2339 assert_eq!(
2340 msgs2[0],
2341 UciMessage::Info(vec![UciInfoAttribute::Score {
2342 cp: Some(20),
2343 lower_bound: None,
2344 mate: None,
2345 upper_bound: None,
2346 }])
2347 );
2348 }
2349
2350 #[test]
2351 fn test_no_line_at_end_parse_with_unknown_with_unknown() {
2352 let msgs = parse_with_unknown("uci\ndebug on\nucinewgame\nabc\nstop\nquit");
2353 assert_eq!(msgs.len(), 6);
2354 assert_eq!(msgs[0], UciMessage::Uci);
2355 assert_eq!(msgs[1], UciMessage::Debug(true));
2356 assert_eq!(msgs[2], UciMessage::UciNewGame);
2357 assert_eq!(msgs[3], UciMessage::Unknown("abc".to_string(), None));
2358 assert_eq!(msgs[4], UciMessage::Stop);
2359 assert_eq!(msgs[5], UciMessage::Quit);
2360 }
2361
2362 #[test]
2363 fn test_complex_parse_with_unknown() {
2364 let msgs = parse_with_unknown("I am the walrus\nuci\ndebug on\nShould I stay \
2365 or should I go?\nLondon calling\nquit\nAre we there yet?\n");
2366 assert_eq!(msgs.len(), 7);
2367 assert_eq!(msgs[0], UciMessage::Unknown("I am the walrus".to_string(), None));
2368 assert_eq!(msgs[1], UciMessage::Uci);
2369 assert_eq!(msgs[2], UciMessage::Debug(true));
2370 assert_eq!(msgs[3], UciMessage::Unknown("Should I stay or should I go?".to_string(), None));
2371 assert_eq!(msgs[4], UciMessage::Unknown("London calling".to_string(), None));
2372 assert_eq!(msgs[5], UciMessage::Quit);
2373 assert_eq!(msgs[6], UciMessage::Unknown("Are we there yet?".to_string(), None));
2374 }
2375
2376 #[test]
2377 fn test_no_line_at_end_parse_with_unknown() {
2378 let msgs = parse_with_unknown("uci\ndebug on\nucinewgame\nstop\nquit");
2379 assert_eq!(msgs.len(), 5);
2380 assert_eq!(msgs[0], UciMessage::Uci);
2381 assert_eq!(msgs[1], UciMessage::Debug(true));
2382 assert_eq!(msgs[2], UciMessage::UciNewGame);
2383 assert_eq!(msgs[3], UciMessage::Stop);
2384 assert_eq!(msgs[4], UciMessage::Quit);
2385 }
2386
2387 #[test]
2388 fn test_no_line_at_end_parse_with_unknown_single() {
2389 let msgs = parse_with_unknown("uciok");
2390 assert_eq!(msgs.len(), 1);
2391 assert_eq!(msgs[0], UciMessage::UciOk);
2392 }
2393
2394 #[test]
2395 fn test_empty_nl_parse() {
2396 let msgs = parse("\n");
2397 assert_eq!(msgs.len(), 0);
2398 }
2399
2400 #[test]
2401 fn test_empty_nl_parse_strict() {
2402 let msgs = parse_strict("\n").unwrap();
2403 assert_eq!(msgs.len(), 0);
2404 }
2405
2406 #[test]
2407 fn test_empty_nl_parse_with_unknown() {
2408 let msgs = parse_with_unknown("\n");
2409 assert_eq!(msgs.len(), 0);
2410 }
2411
2412 #[test]
2413 fn test_empty_parse() {
2414 let msgs = parse("");
2415 assert_eq!(msgs.len(), 0);
2416 }
2417
2418 #[test]
2419 fn test_empty_parse_strict() {
2420 let msgs = parse_strict("").unwrap();
2421 assert_eq!(msgs.len(), 0);
2422 }
2423
2424 #[test]
2425 fn test_empty_parse_with_unknown() {
2426 let msgs = parse_with_unknown("");
2427 assert_eq!(msgs.len(), 0);
2428 }
2429
2430 #[test]
2431 fn test_parse_one_uci() {
2432 let msg = parse_one("uci");
2433 assert_eq!(msg, UciMessage::Uci);
2434 }
2435
2436 #[test]
2437 fn test_parse_one_go() {
2438 let msg = parse_one("go infinite \n");
2439 assert_eq!(msg, UciMessage::go_infinite());
2440 }
2441
2442 #[test]
2443 fn test_parse_one_empty() {
2444 let msg = parse_one("");
2445 match msg {
2446 UciMessage::Unknown(s, _) => {
2447 assert_eq!(s, String::new());
2448 }
2449 _ => panic!("Expected UciMessage::Unknown"),
2450 }
2451 }
2452
2453 #[test]
2454 fn test_parse_one_unknown() {
2455 let msg = parse_one("ax34\n");
2456 match msg {
2457 UciMessage::Unknown(s, _) => {
2458 assert_eq!(s, String::from("ax34"));
2459 }
2460 _ => panic!("Expected UciMessage::Unknown"),
2461 }
2462 }
2463
2464 #[test]
2465 fn test_parse_one_multi_commands() {
2466 let msg = parse_one("uci\nuciok\n");
2467 assert_eq!(msg, UciMessage::Uci);
2468 }
2469
2470 #[test]
2471 fn test_parse_negative_duration_wtime() {
2472 let parsed_msg = parse_one("go wtime -4061 btime 56826 movestogo 90\n");
2473
2474 let time_control = UciTimeControl::TimeLeft {
2475 white_time: Some(Duration::milliseconds(-4061)),
2476 black_time: Some(Duration::milliseconds(56826)),
2477 white_increment: None,
2478 black_increment: None,
2479 moves_to_go: Some(90),
2480 };
2481
2482 let test_msg = UciMessage::Go {
2483 time_control: Some(time_control),
2484 search_control: None,
2485 };
2486
2487 assert_eq!(test_msg, parsed_msg);
2488 }
2489
2490 #[test]
2491 fn test_parse_signed_positive_duration_wtime() {
2492 let parsed_msg = parse_one("go wtime +15030 btime +56826 movestogo 90\n");
2493
2494 let time_control = UciTimeControl::TimeLeft {
2495 white_time: Some(Duration::milliseconds(15030)),
2496 black_time: Some(Duration::milliseconds(56826)),
2497 white_increment: None,
2498 black_increment: None,
2499 moves_to_go: Some(90),
2500 };
2501
2502 let test_msg = UciMessage::Go {
2503 time_control: Some(time_control),
2504 search_control: None,
2505 };
2506
2507 assert_eq!(test_msg, parsed_msg);
2508 }
2509
2510 #[test]
2511 fn test_parse_signed_improperly_duration_wtime() {
2512 let parsed_msg = parse_one("go wtime !15030 btime +56826 movestogo 90\n");
2513
2514 match parsed_msg {
2515 UciMessage::Unknown(cmd, err) => {
2516 assert_eq!(cmd, "go wtime !15030 btime +56826 movestogo 90");
2517 assert!(err.is_some());
2518 }
2519 _ => unreachable!(),
2520 }
2521 }
2522
2523 #[test]
2524 fn test_parse_signed_improperly_duration_wtime_ignore() {
2525 let parsed_msg = parse("go wtime -15030 btime @56826 movestogo 90\n");
2526 assert!(parsed_msg.is_empty());
2527 }
2528
2529 #[test]
2530 fn test_parse_signed_improperly_duration_wtime_strict() {
2531 let err = parse_strict("go wtime -15030 btime x56826 movestogo 90\n");
2532 assert!(err.is_err());
2533 let e: pest::error::Error<_> = err.unwrap_err();
2534 match e.variant {
2535 pest::error::ErrorVariant::ParsingError {
2536 positives,
2537 negatives: _,
2538 } => {
2539 assert!(!positives.is_empty());
2540 }
2541 _ => unreachable!(),
2542 }
2543 }
2544}