1use std::collections::{HashMap, HashSet};
4use std::convert::TryFrom;
5use std::convert::TryInto;
6use std::fmt::{self, Display, Write};
7use std::hash::{Hash, Hasher};
8use std::io;
9use std::ops::Deref;
10use std::ops::{Index, IndexMut};
11use std::str::{FromStr, SplitWhitespace};
12
13use crate::coretypes::{Move, PlyKind};
14use crate::error::{self, ErrorKind};
15use crate::fen::Fen;
16use crate::movelist::MoveHistory;
17use crate::position::{Game, Position};
18
19#[derive(Debug, Clone, Eq, PartialEq)]
21pub enum UciCommand {
22 Uci,
23 Debug(bool),
24 IsReady,
25 SetOption(RawOption),
26 UciNewGame,
27 Pos(Game),
28 Go(SearchControls),
29 Stop,
30 PonderHit,
31 Quit,
32}
33
34impl UciCommand {
35 pub fn parse_command(input_str: &str) -> error::Result<Self> {
37 let mut input = input_str.split_whitespace();
38 let head = input.next().ok_or(ErrorKind::UciNoCommand)?;
39
40 match head {
41 "uci" => Ok(UciCommand::Uci),
42 "debug" => Self::parse_debug(input),
43 "isready" => Ok(UciCommand::IsReady),
44 "setoption" => Self::parse_setoption(input),
45 "ucinewgame" => Ok(UciCommand::UciNewGame),
46 "position" => Self::parse_pos(input),
47 "go" => Self::parse_go(input),
48 "stop" => Ok(UciCommand::Stop),
49 "ponderhit" => Ok(UciCommand::PonderHit),
50 "quit" => Ok(UciCommand::Quit),
51 _ => Err((ErrorKind::UciUnknownCommand, head).into()),
52 }
53 }
54
55 fn parse_debug(mut input: SplitWhitespace) -> error::Result<Self> {
58 let debug_mode_str = input.next().ok_or(ErrorKind::UciDebugNoMode)?;
59
60 match debug_mode_str {
61 "on" => Ok(Self::Debug(true)),
62 "off" => Ok(Self::Debug(false)),
63 _ => Err(ErrorKind::UciDebugIllegalMode.into()),
64 }
65 }
66
67 fn parse_setoption(mut input: SplitWhitespace) -> error::Result<Self> {
70 let name = input.next().ok_or(ErrorKind::UciSetOptionNoName)?;
71 (name == "name")
72 .then(|| ())
73 .ok_or(ErrorKind::UciSetOptionNoName)?;
74
75 let mut name = String::new();
76 let mut value = String::new();
77 let mut had_value = false;
78
79 while let Some(token) = input.next() {
82 if token == "value" {
83 had_value = true;
84 break;
85 } else {
86 name.push_str(token);
87 name.push(' ');
88 }
89 }
90 name.pop(); (name.len() > 0)
92 .then(|| ())
93 .ok_or(ErrorKind::UciSetOptionNoName)?;
94
95 if had_value {
98 for token in input {
99 value.push_str(token);
100 value.push(' ');
101 }
102 value.pop(); (value.len() > 0)
104 .then(|| ())
105 .ok_or((ErrorKind::UciNoArgument, "expected argument after value"))?;
106 }
107
108 Ok(UciCommand::SetOption(RawOption {
109 name: name.as_str().into(),
110 value,
111 }))
112 }
113
114 fn parse_pos(mut input: SplitWhitespace) -> error::Result<Self> {
117 let position_input = input.next().ok_or((
118 ErrorKind::UciNoArgument,
119 "position missing description [fen | startpos]",
120 ))?;
121
122 let base_position = match position_input {
124 "startpos" => Ok(Position::start_position()),
125 "fen" => {
126 let mut fen_str = String::new();
127 for _ in 0..6 {
128 fen_str.push_str(input.next().ok_or(ErrorKind::UciPositionMalformed)?);
129 fen_str.push(' ');
130 }
131 Position::parse_fen(&fen_str)
132 }
133 _ => return Err(ErrorKind::UciPositionMalformed.into()),
134 }?;
135
136 let mut moves = MoveHistory::new();
137
138 if let Some("moves") = input.next() {
140 for move_str in input {
141 moves.push(Move::from_str(move_str)?);
142 }
143 }
144
145 Game::new(base_position, moves).map(|game| UciCommand::Pos(game))
146 }
147
148 fn parse_go(mut input: SplitWhitespace) -> error::Result<Self> {
151 const HAS_U32_ARG: [&'static str; 9] = [
156 "wtime",
157 "btime",
158 "winc",
159 "binc",
160 "depth",
161 "movestogo",
162 "mate",
163 "movetime",
164 "nodes",
165 ];
166
167 let mut controls = SearchControls::new();
168
169 while let Some(input_str) = input.next() {
170 if HAS_U32_ARG.contains(&input_str) {
172 let argument: i64 = input
173 .next()
174 .ok_or(ErrorKind::UciNoArgument)?
175 .parse()
176 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?;
177
178 match input_str {
179 "wtime" => {
180 controls.wtime = Some(
181 argument
182 .try_into()
183 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
184 )
185 }
186 "btime" => {
187 controls.btime = Some(
188 argument
189 .try_into()
190 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
191 )
192 }
193 "winc" => {
194 controls.winc = Some(
195 argument
196 .try_into()
197 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
198 )
199 }
200 "binc" => {
201 controls.binc = Some(
202 argument
203 .try_into()
204 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
205 )
206 }
207 "depth" => {
208 controls.depth = Some(
209 argument
210 .try_into()
211 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
212 )
213 }
214 "movestogo" => {
215 controls.moves_to_go = Some(
216 argument
217 .try_into()
218 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
219 )
220 }
221 "mate" => {
222 controls.mate = Some(
223 argument
224 .try_into()
225 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
226 )
227 }
228 "movetime" => {
229 controls.move_time = Some(
230 argument
231 .try_into()
232 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
233 )
234 }
235 "nodes" => {
236 controls.nodes = Some(
237 argument
238 .try_into()
239 .map_err(|err| (ErrorKind::UciCannotParseInt, err))?,
240 )
241 }
242 _ => {
243 return Err(ErrorKind::UciInvalidOption.into());
244 }
245 };
246 } else if input_str == "infinite" {
247 controls.infinite = true;
248 } else {
249 return Err(ErrorKind::UciInvalidOption.into());
250 }
251 }
252
253 Ok(UciCommand::Go(controls))
254 }
255}
256
257impl FromStr for UciCommand {
258 type Err = error::Error;
259 fn from_str(s: &str) -> error::Result<Self> {
260 Self::parse_command(s)
261 }
262}
263
264#[derive(Debug, Clone)]
266pub enum UciResponse {
267 Id(String, String),
268 UciOk,
269 ReadyOk,
270 Opt(UciOption),
271 BestMove(Move),
272 Info(UciInfo),
273}
274
275impl UciResponse {
276 pub fn new_id(name: &str, author: &str) -> Self {
277 Self::Id(name.into(), author.into())
278 }
279
280 pub fn new_option(uci_opt: UciOption) -> Self {
281 Self::Opt(uci_opt)
282 }
283
284 pub fn new_best_move(move_: Move) -> Self {
285 Self::BestMove(move_)
286 }
287
288 pub fn new_info(uci_info: UciInfo) -> Self {
289 Self::Info(uci_info)
290 }
291
292 pub fn send(&self) -> io::Result<()> {
295 let stdout = io::stdout();
296 let mut handle = stdout.lock();
297 <io::StdoutLock as io::Write>::write_all(&mut handle, self.to_string().as_ref())?;
298 <io::StdoutLock as io::Write>::flush(&mut handle)
299 }
300}
301
302impl Display for UciResponse {
303 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304 match self {
305 Self::Id(name, author) => {
306 f.write_str("id name ")?;
307 f.write_str(name)?;
308 f.write_char('\n')?;
309 f.write_str("id author ")?;
310 f.write_str(author)?;
311 f.write_char('\n')
312 }
313 Self::UciOk => f.write_str("uciok\n"),
314 Self::ReadyOk => f.write_str("readyok\n"),
315 Self::BestMove(move_) => {
316 f.write_str("bestmove ")?;
317 move_.fmt(f)?;
318 f.write_char('\n')
319 }
320 Self::Opt(uci_opt) => {
321 write!(f, "{}\n", uci_opt)
322 }
323 Self::Info(_info) => {
324 f.write_str("info string todo\n")
326 }
327 }
328 }
329}
330
331pub fn debug(can_debug: bool, s: &str) -> io::Result<()> {
334 if can_debug {
335 let mut debug_str = String::from("info string debug ");
336 debug_str.push_str(s);
337 debug_str.push('\n');
338
339 let stdout = io::stdout();
340 let mut handle = stdout.lock();
341 <io::StdoutLock as io::Write>::write_all(&mut handle, debug_str.as_ref())?;
342 <io::StdoutLock as io::Write>::flush(&mut handle)
343 } else {
344 Ok(())
345 }
346}
347
348pub fn error(s: &str) -> io::Result<()> {
351 let mut error_str = String::from("info string error ");
352 error_str.push_str(s);
353 error_str.push('\n');
354
355 let stdout = io::stdout();
356 let mut handle = stdout.lock();
357 <io::StdoutLock as io::Write>::write_all(&mut handle, error_str.as_ref())?;
358 <io::StdoutLock as io::Write>::flush(&mut handle)
359}
360
361#[derive(Debug, Clone)]
362pub struct UciInfo {}
363
364#[derive(Debug, Clone, Eq, PartialEq)]
367pub struct RawOption {
368 name: CaselessString,
369 value: String,
370}
371
372#[derive(Debug, Copy, Clone, Eq, PartialEq)]
373pub struct Check {
374 pub value: bool,
375 pub default: bool,
376}
377
378#[derive(Debug, Copy, Clone, Eq, PartialEq)]
379pub struct Spin {
380 pub value: i64,
381 pub default: i64,
382 pub min: i64,
383 pub max: i64,
384}
385
386#[derive(Debug, Clone, Eq, PartialEq)]
387pub struct Combo {
388 pub value: String,
389 pub default: String,
390 pub choices: HashSet<String>,
391}
392
393#[derive(Debug, Copy, Clone, Eq, PartialEq)]
394pub struct Button {
395 pub pressed: bool,
396}
397
398#[derive(Debug, Clone, Eq, PartialEq)]
399pub struct UciOptionString {
400 pub value: String,
401 pub default: String,
402}
403
404impl Spin {
405 pub fn value<T: TryFrom<i64>>(&self) -> T {
409 match T::try_from(self.value) {
410 Ok(converted) => converted,
411 _ => panic!("spin value TryFrom<i64> conversion failed"),
412 }
413 }
414}
415
416#[derive(Debug, Clone, Eq, PartialEq)]
417pub enum UciOptionType {
418 Check(Check),
419 Spin(Spin),
420 Combo(Combo),
421 Button(Button),
422 String(UciOptionString),
423}
424
425impl Display for UciOptionType {
426 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
427 match self {
428 UciOptionType::Check(Check { default, .. }) => {
429 write!(f, "type check default {}", default)
430 }
431 UciOptionType::Spin(Spin {
432 default, min, max, ..
433 }) => {
434 write!(f, "type spin default {} min {} max {}", default, min, max)
435 }
436 UciOptionType::Combo(Combo {
437 default, choices, ..
438 }) => {
439 write!(f, "type combo default {}", default)?;
440 for choice in choices {
441 write!(f, " var {}", choice)?;
442 }
443 Ok(())
444 }
445 UciOptionType::Button(_) => f.write_str("type button"),
446 UciOptionType::String(UciOptionString { default, .. }) => {
447 write!(f, "type string default {}", default)
448 }
449 }
450 }
451}
452
453#[derive(Debug, Clone, Eq, PartialEq)]
459pub struct UciOption {
460 pub name: CaselessString,
461 pub option_type: UciOptionType,
462}
463
464impl UciOption {
465 pub fn new_check(name: &str, default: bool) -> Self {
467 Self {
468 name: name.into(),
469 option_type: UciOptionType::Check(Check {
470 value: default,
471 default,
472 }),
473 }
474 }
475
476 pub fn new_spin(name: &str, default: i64, min: i64, max: i64) -> Self {
478 assert!(min < max, "Illegal spin, min >= max");
479 assert!(default >= min, "Illegal spin, default < min");
480 assert!(default <= max, "Illegal spin, default > max");
481
482 Self {
483 name: name.into(),
484 option_type: UciOptionType::Spin(Spin {
485 value: default,
486 default,
487 min,
488 max,
489 }),
490 }
491 }
492
493 pub fn new_combo(name: &str, default: &str, choices: &[&str]) -> Self {
497 let default = default.trim().to_string();
498 let choices: HashSet<String> = choices.iter().map(|s| s.trim().to_string()).collect();
499
500 assert!(matches!(
502 choices
503 .iter()
504 .find(|item| item.to_lowercase() == default.to_lowercase()),
505 Some(_)
506 ));
507
508 Self {
509 name: name.into(),
510 option_type: UciOptionType::Combo(Combo {
511 value: default.clone(),
512 default,
513 choices,
514 }),
515 }
516 }
517
518 pub fn new_button(name: &str, pressed: bool) -> Self {
520 Self {
521 name: name.into(),
522 option_type: UciOptionType::Button(Button { pressed }),
523 }
524 }
525
526 pub fn new_string(name: &str, default: &str) -> Self {
528 Self {
529 name: name.into(),
530 option_type: UciOptionType::String(UciOptionString {
531 value: default.trim().to_string(),
532 default: default.trim().to_string(),
533 }),
534 }
535 }
536
537 pub fn check(&self) -> &Check {
540 match self.option_type {
541 UciOptionType::Check(ref check) => check,
542 _ => panic!("option type is not check"),
543 }
544 }
545 pub fn spin(&self) -> &Spin {
548 match self.option_type {
549 UciOptionType::Spin(ref spin) => spin,
550 _ => panic!("option type is not spin"),
551 }
552 }
553 pub fn combo(&self) -> &Combo {
556 match self.option_type {
557 UciOptionType::Combo(ref combo) => combo,
558 _ => panic!("option type is not combo"),
559 }
560 }
561 pub fn button(&self) -> &Button {
564 match self.option_type {
565 UciOptionType::Button(ref button) => button,
566 _ => panic!("option type is not button"),
567 }
568 }
569 pub fn string(&self) -> &UciOptionString {
572 match self.option_type {
573 UciOptionType::String(ref s) => s,
574 _ => panic!("option type is not String"),
575 }
576 }
577
578 pub fn check_mut(&mut self) -> &mut Check {
581 match self.option_type {
582 UciOptionType::Check(ref mut check) => check,
583 _ => panic!("option type is not check"),
584 }
585 }
586 pub fn spin_mut(&mut self) -> &mut Spin {
589 match self.option_type {
590 UciOptionType::Spin(ref mut spin) => spin,
591 _ => panic!("option type is not spin"),
592 }
593 }
594 pub fn combo_mut(&mut self) -> &mut Combo {
597 match self.option_type {
598 UciOptionType::Combo(ref mut combo) => combo,
599 _ => panic!("option type is not combo"),
600 }
601 }
602 pub fn button_mut(&mut self) -> &mut Button {
605 match self.option_type {
606 UciOptionType::Button(ref mut button) => button,
607 _ => panic!("option type is not button"),
608 }
609 }
610 pub fn string_mut(&mut self) -> &mut UciOptionString {
613 match self.option_type {
614 UciOptionType::String(ref mut s) => s,
615 _ => panic!("option type is not String"),
616 }
617 }
618
619 pub fn try_update(&mut self, raw_opt: &RawOption) -> error::Result<&mut Self> {
623 (self.name == raw_opt.name)
624 .then(|| ())
625 .ok_or((ErrorKind::UciOptionCannotUpdate, "names do not match"))?;
626
627 match self.option_type {
628 UciOptionType::Check(Check { ref mut value, .. }) => {
629 *value = bool::from_str(&raw_opt.value)
630 .map_err(|err| (ErrorKind::UciOptionCannotUpdate, err))?;
631 }
632 UciOptionType::Spin(Spin {
633 ref mut value,
634 min,
635 max,
636 ..
637 }) => {
638 let new_value = i64::from_str_radix(&raw_opt.value, 10)
639 .map_err(|err| (ErrorKind::UciOptionCannotUpdate, err))?;
640 (min..=max)
641 .contains(&new_value)
642 .then(|| ())
643 .ok_or((ErrorKind::UciOptionCannotUpdate, "value out of range"))?;
644 *value = new_value;
645 }
646 UciOptionType::Combo(Combo {
647 ref mut value,
648 ref choices,
649 ..
650 }) => {
651 choices
652 .contains(&raw_opt.value)
653 .then(|| ())
654 .ok_or((ErrorKind::UciOptionCannotUpdate, "value not a valid choice"))?;
655 *value = raw_opt.value.clone();
656 }
657 UciOptionType::Button(Button { ref mut pressed }) => *pressed = true,
658 UciOptionType::String(UciOptionString { ref mut value, .. }) => {
659 *value = raw_opt.value.clone()
660 }
661 };
662
663 Ok(self)
664 }
665}
666
667impl Display for UciOption {
668 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
669 write!(f, "option name {} {}", self.name.0, self.option_type)
670 }
671}
672
673#[derive(Debug, Clone)]
677pub struct CaselessString(String);
678
679impl PartialEq for CaselessString {
680 fn eq(&self, other: &Self) -> bool {
681 self.0.to_lowercase() == other.0.to_lowercase()
682 }
683}
684impl Eq for CaselessString {}
685
686impl Hash for CaselessString {
687 fn hash<H: Hasher>(&self, state: &mut H) {
688 self.0.to_lowercase().hash(state);
689 }
690}
691
692impl PartialEq<&str> for CaselessString {
693 fn eq(&self, other: &&str) -> bool {
694 *self == Self::from(*other)
695 }
696}
697
698impl Deref for CaselessString {
699 type Target = String;
700 fn deref(&self) -> &Self::Target {
701 &self.0
702 }
703}
704
705impl From<&str> for CaselessString {
706 fn from(s: &str) -> Self {
707 Self(s.trim().to_string())
708 }
709}
710
711type OptionsMap = HashMap<CaselessString, UciOption>;
713
714pub struct UciOptions(OptionsMap);
717
718impl UciOptions {
719 pub fn new() -> Self {
721 Self(OptionsMap::new())
722 }
723
724 pub fn insert(&mut self, uci_opt: UciOption) -> Option<UciOption> {
728 let key = uci_opt.name.clone();
729 let old_value = self.0.remove(&key);
731 self.0.insert(key, uci_opt);
732 old_value
733 }
734
735 pub fn contains<K: Into<CaselessString>>(&self, key: K) -> bool {
737 let key: CaselessString = key.into();
738 self.0.contains_key(&key)
739 }
740
741 pub fn update(&mut self, raw_opt: &RawOption) -> error::Result<&mut UciOption> {
745 self.0
746 .get_mut(&raw_opt.name)
747 .ok_or((
748 ErrorKind::UciOptionCannotUpdate,
749 "RawOption name not a valid UciOption",
750 ))?
751 .try_update(&raw_opt)
752 }
753}
754
755impl<K: Into<CaselessString>> Index<K> for UciOptions {
756 type Output = UciOption;
757 fn index(&self, key: K) -> &Self::Output {
758 let key: CaselessString = key.into();
759 &self.0[&key]
760 }
761}
762
763impl<K: Into<CaselessString>> IndexMut<K> for UciOptions {
764 fn index_mut(&mut self, key: K) -> &mut Self::Output {
765 let key: CaselessString = key.into();
766 self.0.get_mut(&key).expect("key not present")
767 }
768}
769
770impl Deref for UciOptions {
771 type Target = OptionsMap;
772 fn deref(&self) -> &Self::Target {
773 &self.0
774 }
775}
776
777#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
778pub struct SearchControls {
779 pub wtime: Option<i32>,
780 pub btime: Option<i32>,
781 pub winc: Option<u32>,
782 pub binc: Option<u32>,
783 pub moves_to_go: Option<u32>,
784 pub depth: Option<PlyKind>,
785 pub nodes: Option<u64>,
786 pub mate: Option<u32>,
787 pub move_time: Option<u32>,
788 pub infinite: bool,
789}
790
791impl SearchControls {
792 pub fn new() -> Self {
793 Self::default()
794 }
795}
796
797impl Default for SearchControls {
798 fn default() -> Self {
799 Self {
800 wtime: None,
801 btime: None,
802 winc: None,
803 binc: None,
804 moves_to_go: None,
805 depth: None,
806 nodes: None,
807 mate: None,
808 move_time: None,
809 infinite: false,
810 }
811 }
812}
813
814#[cfg(test)]
815mod tests {
816 use super::*;
817 use crate::coretypes::Square::*;
818
819 #[test]
821 fn parse_command_singles() {
822 {
823 let input = "uci";
824 let command = UciCommand::parse_command(&input);
825 assert_eq!(UciCommand::Uci, command.unwrap());
826 }
827 {
828 let input = "isready\n";
829 let command = UciCommand::parse_command(&input);
830 assert_eq!(UciCommand::IsReady, command.unwrap());
831 }
832 {
833 let input = "ucinewgame";
834 let command = UciCommand::parse_command(&input);
835 assert_eq!(UciCommand::UciNewGame, command.unwrap());
836 }
837 {
838 let input = "stop";
839 let command = UciCommand::parse_command(&input);
840 assert_eq!(UciCommand::Stop, command.unwrap());
841 }
842 {
843 let input = "ponderhit";
844 let command = UciCommand::parse_command(&input);
845 assert_eq!(UciCommand::PonderHit, command.unwrap());
846 }
847 {
848 let input = "quit";
849 let command = UciCommand::parse_command(&input);
850 assert_eq!(UciCommand::Quit, command.unwrap());
851 }
852 }
853
854 #[test]
855 fn parse_command_debug() {
856 let on = "debug on";
857 let off = "debug off";
858 let command_on = UciCommand::parse_command(on);
859 let command_off = UciCommand::parse_command(off);
860 assert_eq!(UciCommand::Debug(true), command_on.unwrap());
861 assert_eq!(UciCommand::Debug(false), command_off.unwrap());
862 }
863
864 #[test]
865 fn parse_command_setoption() {
866 {
867 let input = "setoption name Hash value 100\n";
868 let command = UciCommand::parse_command(input);
869 let raw_opt = RawOption {
870 name: "hash".into(),
871 value: String::from("100"),
872 };
873 assert_eq!(UciCommand::SetOption(raw_opt), command.unwrap());
874 }
875 {
876 let input = "setoption name Multi Word Name value this is a test string.c";
877 let command = UciCommand::parse_command(input);
878 let raw_opt = RawOption {
879 name: "Multi Word Name".into(),
880 value: String::from("this is a test string.c"),
881 };
882 assert_eq!(UciCommand::SetOption(raw_opt), command.unwrap());
883 }
884 {
885 let input = "setoption name Clear Hash \n";
886 let command = UciCommand::parse_command(input);
887 let raw_opt = RawOption {
888 name: "Clear Hash".into(),
889 value: String::from(""),
890 };
891 assert_eq!(UciCommand::SetOption(raw_opt), command.unwrap());
892 }
893 }
894
895 #[test]
896 fn parse_command_pos() {
897 {
898 let start_position = Game::new(Position::start_position(), MoveHistory::new()).unwrap();
900 let command_start_str = "position startpos";
901 let command_start1 = UciCommand::parse_command(command_start_str).unwrap();
902 assert_eq!(UciCommand::Pos(start_position), command_start1);
903 }
904
905 {
906 let mut moves = MoveHistory::new();
908 moves.push(Move::new(D2, D4, None));
909 moves.push(Move::new(D7, D5, None));
910 let base_pos = Position::start_position();
911 let mut final_pos = base_pos.clone();
912
913 moves.iter().for_each(|move_| {
914 final_pos.do_move(*move_);
915 });
916
917 let game = Game::new(base_pos, moves).unwrap();
918 let game_position = game.position.clone();
919
920 let command_start_moves_str = "position startpos moves d2d4 d7d5";
921 let command = UciCommand::parse_command(command_start_moves_str).unwrap();
922 assert_eq!(UciCommand::Pos(game), command);
923 assert_eq!(game_position, final_pos);
924 }
925
926 {
927 let pos_fen_str = "rnbqkbnr/pppp1ppp/8/4P3/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 2";
929 let command_str =
930 "position fen rnbqkbnr/pppp1ppp/8/4P3/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 2";
931 let pos = Position::parse_fen(pos_fen_str).unwrap();
932 let game = Game::new(pos, MoveHistory::new()).unwrap();
933 let game_position = game.position;
934 let command = UciCommand::parse_command(command_str).unwrap();
935
936 assert_eq!(UciCommand::Pos(game), command);
937 assert_eq!(game_position, pos);
938 }
939
940 {
941 let base_fen_str = "rnbqkbnr/pppp1ppp/8/4P3/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 2";
943 let post_fen_str = "rnbqkbnr/ppp2ppp/3P4/8/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 3";
944 let command_str = "position fen rnbqkbnr/pppp1ppp/8/4P3/8/8/PPP1PPPP/RNBQKBNR b KQkq - 0 2 moves d7d6 e5d6";
945 let pos_base = Position::parse_fen(base_fen_str).unwrap();
946 let pos_post = Position::parse_fen(post_fen_str).unwrap();
947 let mut moves = MoveHistory::new();
948 moves.push(Move::new(D7, D6, None));
949 moves.push(Move::new(E5, D6, None));
950
951 let game = Game::new(pos_base, moves).unwrap();
952
953 let command = UciCommand::parse_command(command_str).unwrap();
954 println!("pos: {}", pos_post);
955
956 if let UciCommand::Pos(ref inner_game) = command {
957 println!("com: {:?}", inner_game);
958 };
959 let game_position = game.position;
960 let game_base_position = game.base_position;
961 assert_eq!(UciCommand::Pos(game), command);
962 assert_eq!(game_position, pos_post);
963 assert_eq!(game_base_position, pos_base);
964 }
965 }
966
967 #[test]
968 fn parse_command_go() {
969 {
970 let input = "go depth 10 wtime 40000 \n";
971 let command = UciCommand::parse_command(input).unwrap();
972 let mut search_ctrl = SearchControls::new();
973 search_ctrl.depth = Some(10);
974 search_ctrl.wtime = Some(40000);
975 assert_eq!(UciCommand::Go(search_ctrl), command);
976 }
977 }
978
979 #[test]
980 fn ucioptions_insert_update_contains() {
981 let option_hash = UciOption::new_spin("Hash", 1, 1, 16000);
986 let option_clear_hash = UciOption::new_button("Clear Hash", false);
987 let option_ponder = UciOption::new_check("Ponder", false);
988 let option_threads = UciOption::new_spin("Threads", 1, 1, 32);
989
990 let mut uci_options = UciOptions::new();
991
992 assert_eq!(uci_options.len(), 0);
993 assert_eq!(uci_options.insert(option_hash.clone()), None);
994 assert_eq!(uci_options.insert(option_clear_hash.clone()), None);
995 assert_eq!(uci_options.insert(option_ponder.clone()), None);
996 assert_eq!(uci_options.insert(option_threads.clone()), None);
997 assert_eq!(uci_options.len(), 4);
998
999 let raw_hash = RawOption {
1000 name: "hash".into(),
1001 value: "14".into(),
1002 };
1003 assert!(matches!(uci_options.update(&raw_hash), Ok(_)));
1004
1005 assert_eq!(
1006 option_clear_hash,
1007 *uci_options.get(&"clear hash".into()).unwrap()
1008 );
1009 assert_eq!(option_ponder, *uci_options.get(&"ponder".into()).unwrap());
1010 assert_eq!(option_threads, *uci_options.get(&"threads".into()).unwrap());
1011 assert_ne!(option_hash, *uci_options.get(&"hash".into()).unwrap());
1012 }
1013}