1use std::{
2 cell::RefCell,
3 collections::HashMap,
4 fmt::{Debug, Display},
5 rc::{Rc, Weak},
6};
7
8use crate::{
9 core::{GameStatus, MoveInfo, Position},
10 errors::PGNMetadataError,
11};
12
13#[derive(Debug, Clone)]
15pub(crate) enum OptionPGNMetadata {
16 TimeControl(String),
19 Termination(String),
21
22 WhiteElo(u32),
25 BlackElo(u32),
27 WhiteTitle(String),
29 BlackTitle(String),
31 WhiteUSCF(String),
33 BlackUSCF(String),
35 WhiteNA(String),
37 BlackNA(String),
39 WhiteType(String),
41 BlackType(String),
43
44 EventDate(String),
47 EventSponsor(String),
49 Section(String),
51 Stage(String),
53 Board(String),
55
56 Opening(String),
59 Variation(String),
61 SubVariation(String),
63 ECO(String),
65 NIC(String),
67
68 Time(String),
71 UTCDate(String),
73 UTCTime(String),
75
76 SetUp(String),
79 FEN(String),
81
82 Annotator(String),
85 Mode(String),
87 PlyCount(u32),
89}
90
91impl OptionPGNMetadata {
92 pub fn from_string(key: &str, value: &str) -> Option<OptionPGNMetadata> {
102 match key {
103 "TimeControl" => Some(OptionPGNMetadata::TimeControl(value.to_string())),
104 "Termination" => Some(OptionPGNMetadata::Termination(value.to_string())),
105 "WhiteElo" => value.parse().ok().map(OptionPGNMetadata::WhiteElo),
106 "BlackElo" => value.parse().ok().map(OptionPGNMetadata::BlackElo),
107 "WhiteTitle" => Some(OptionPGNMetadata::WhiteTitle(value.to_string())),
108 "BlackTitle" => Some(OptionPGNMetadata::BlackTitle(value.to_string())),
109 "WhiteUSCF" => Some(OptionPGNMetadata::WhiteUSCF(value.to_string())),
110 "BlackUSCF" => Some(OptionPGNMetadata::BlackUSCF(value.to_string())),
111 "WhiteNA" => Some(OptionPGNMetadata::WhiteNA(value.to_string())),
112 "BlackNA" => Some(OptionPGNMetadata::BlackNA(value.to_string())),
113 "WhiteType" => Some(OptionPGNMetadata::WhiteType(value.to_string())),
114 "BlackType" => Some(OptionPGNMetadata::BlackType(value.to_string())),
115 "EventDate" => Some(OptionPGNMetadata::EventDate(value.to_string())),
116 "EventSponsor" => Some(OptionPGNMetadata::EventSponsor(value.to_string())),
117 "Section" => Some(OptionPGNMetadata::Section(value.to_string())),
118 "Stage" => Some(OptionPGNMetadata::Stage(value.to_string())),
119 "Board" => Some(OptionPGNMetadata::Board(value.to_string())),
120 "Opening" => Some(OptionPGNMetadata::Opening(value.to_string())),
121 "Variation" => Some(OptionPGNMetadata::Variation(value.to_string())),
122 "SubVariation" => Some(OptionPGNMetadata::SubVariation(value.to_string())),
123 "ECO" => Some(OptionPGNMetadata::ECO(value.to_string())),
124 "NIC" => Some(OptionPGNMetadata::NIC(value.to_string())),
125 "Time" => Some(OptionPGNMetadata::Time(value.to_string())),
126 "UTCDate" => Some(OptionPGNMetadata::UTCDate(value.to_string())),
127 "UTCTime" => Some(OptionPGNMetadata::UTCTime(value.to_string())),
128 "SetUp" => Some(OptionPGNMetadata::SetUp(value.to_string())),
129 "FEN" => Some(OptionPGNMetadata::FEN(value.to_string())),
130 "Annotator" => Some(OptionPGNMetadata::Annotator(value.to_string())),
131 "Mode" => Some(OptionPGNMetadata::Mode(value.to_string())),
132 "PlyCount" => value.parse().ok().map(OptionPGNMetadata::PlyCount),
133 _ => None,
134 }
135 }
136}
137
138impl Display for OptionPGNMetadata {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 match self {
141 OptionPGNMetadata::TimeControl(time_control) => {
142 write!(f, "[TimeControl \"{}\"]", time_control)
143 }
144 OptionPGNMetadata::Termination(termination) => {
145 write!(f, "[Termination \"{}\"]", termination)
146 }
147 OptionPGNMetadata::WhiteElo(white_elo) => write!(f, "[WhiteElo \"{}\"]", white_elo),
148 OptionPGNMetadata::BlackElo(black_elo) => write!(f, "[BlackElo \"{}\"]", black_elo),
149 OptionPGNMetadata::WhiteTitle(white_title) => {
150 write!(f, "[WhiteTitle \"{}\"]", white_title)
151 }
152 OptionPGNMetadata::BlackTitle(black_title) => {
153 write!(f, "[BlackTitle \"{}\"]", black_title)
154 }
155 OptionPGNMetadata::WhiteUSCF(white_uscf) => write!(f, "[WhiteUSCF \"{}\"]", white_uscf),
156 OptionPGNMetadata::BlackUSCF(black_uscf) => write!(f, "[BlackUSCF \"{}\"]", black_uscf),
157 OptionPGNMetadata::WhiteNA(white_na) => write!(f, "[WhiteNA \"{}\"]", white_na),
158 OptionPGNMetadata::BlackNA(black_na) => write!(f, "[BlackNA \"{}\"]", black_na),
159 OptionPGNMetadata::WhiteType(white_type) => write!(f, "[WhiteType \"{}\"]", white_type),
160 OptionPGNMetadata::BlackType(black_type) => write!(f, "[BlackType \"{}\"]", black_type),
161 OptionPGNMetadata::EventDate(event_date) => write!(f, "[EventDate \"{}\"]", event_date),
162 OptionPGNMetadata::EventSponsor(event_sponsor) => {
163 write!(f, "[EventSponsor \"{}\"]", event_sponsor)
164 }
165 OptionPGNMetadata::Section(section) => write!(f, "[Section \"{}\"]", section),
166 OptionPGNMetadata::Stage(stage) => write!(f, "[Stage \"{}\"]", stage),
167 OptionPGNMetadata::Board(board) => write!(f, "[Board \"{}\"]", board),
168 OptionPGNMetadata::Opening(opening) => write!(f, "[Opening \"{}\"]", opening),
169 OptionPGNMetadata::Variation(variation) => write!(f, "[Variation \"{}\"]", variation),
170 OptionPGNMetadata::SubVariation(sub_variation) => {
171 write!(f, "[SubVariation \"{}\"]", sub_variation)
172 }
173 OptionPGNMetadata::ECO(eco) => write!(f, "[ECO \"{}\"]", eco),
174 OptionPGNMetadata::NIC(nic) => write!(f, "[NIC \"{}\"]", nic),
175 OptionPGNMetadata::Time(time) => write!(f, "[Time \"{}\"]", time),
176 OptionPGNMetadata::UTCDate(utc_date) => write!(f, "[UTCDate \"{}\"]", utc_date),
177 OptionPGNMetadata::UTCTime(utc_time) => write!(f, "[UTCTime \"{}\"]", utc_time),
178 OptionPGNMetadata::SetUp(set_up) => write!(f, "[SetUp \"{}\"]", set_up),
179 OptionPGNMetadata::FEN(fen) => write!(f, "[FEN \"{}\"]", fen),
180 OptionPGNMetadata::Annotator(annotator) => write!(f, "[Annotator \"{}\"]", annotator),
181 OptionPGNMetadata::Mode(mode) => write!(f, "[Mode \"{}\"]", mode),
182 OptionPGNMetadata::PlyCount(ply_count) => write!(f, "[PlyCount \"{}\"]", ply_count),
183 }
184 }
185}
186
187#[derive(Debug, Clone)]
192struct PgnLine<T: PartialEq + Clone + Display + Debug> {
193 lines: Vec<Rc<RefCell<PgnLine<T>>>>,
195 parent: Option<Weak<RefCell<PgnLine<T>>>>,
197 halfmove_clock: u32,
199 fullmove_number: u32,
201 en_passant: Option<Position>,
203 castling_rights: u8,
205 game_status: GameStatus,
207 prev_positions: HashMap<String, u32>,
209 mov: T,
211}
212
213impl<T: PartialEq + Clone + Display + Debug> PartialEq for PgnLine<T> {
214 fn eq(&self, other: &Self) -> bool {
224 self.mov == other.mov
225 }
226}
227
228#[derive(Debug, Clone)]
233pub struct PGNTree<T: PartialEq + Clone + Display + Debug> {
234 pub event: String,
236 pub site: String,
238 pub date: String,
240 pub round: String,
242 pub white: String,
244 pub black: String,
246 pub result: String,
248 pub variant: Option<String>,
250 pub(crate) option_metadata: Vec<OptionPGNMetadata>,
252 lines: Vec<Rc<RefCell<PgnLine<T>>>>,
254 current_line: Option<Rc<RefCell<PgnLine<T>>>>,
256}
257
258impl<T: PartialEq + Clone + Display + Debug> Default for PGNTree<T> {
259 fn default() -> PGNTree<T> {
272 PGNTree {
273 event: "".to_string(),
274 site: "".to_string(),
275 date: "".to_string(),
276 round: "".to_string(),
277 white: "".to_string(),
278 black: "".to_string(),
279 result: "".to_string(),
280 variant: None,
281 option_metadata: Vec::new(),
282 lines: Vec::new(),
283 current_line: None,
284 }
285 }
286}
287
288impl<T: PartialEq + Clone + Display + Debug> PGNTree<T> {
289 pub fn new(
324 event: String,
325 site: String,
326 date: String,
327 round: String,
328 white: String,
329 black: String,
330 result: String,
331 variant: Option<String>,
332 ) -> PGNTree<T> {
333 PGNTree {
334 event,
335 site,
336 date,
337 round,
338 white,
339 black,
340 result,
341 variant,
342 option_metadata: Vec::new(),
343 lines: Vec::new(),
344 current_line: None,
345 }
346 }
347
348 pub fn add_metadata(&mut self, key: &str, value: &str) -> Result<(), PGNMetadataError> {
369 match key {
370 "Event" => {
371 self.event = value.to_string();
372 }
373 "Site" => {
374 self.site = value.to_string();
375 }
376 "Date" => {
377 self.date = value.to_string();
378 }
379 "Round" => {
380 self.round = value.to_string();
381 }
382 "White" => {
383 self.white = value.to_string();
384 }
385 "Black" => {
386 self.black = value.to_string();
387 }
388 "Result" => {
389 self.result = value.to_string();
390 }
391 "Variant" => {
392 self.variant = Some(value.to_string());
393 }
394 _ => self.option_metadata.push(
395 OptionPGNMetadata::from_string(key, value)
396 .ok_or(PGNMetadataError::new(format!("[{} \"{}\"]", key, value)))?,
397 ),
398 }
399 Ok(())
400 }
401
402 pub fn add_move(
439 &mut self,
440 mov: T,
441 halfmove_clock: u32,
442 fullmove_number: u32,
443 en_passant: Option<Position>,
444 castling_rights: u8,
445 game_status: GameStatus,
446 prev_positions: HashMap<String, u32>,
447 ) {
448 if let Some(current_line) = &self.current_line {
449 let new_line = Rc::new(RefCell::new(PgnLine {
450 lines: Vec::new(),
451 parent: Some(Rc::downgrade(¤t_line)),
452 halfmove_clock,
453 fullmove_number,
454 en_passant,
455 castling_rights,
456 game_status,
457 mov,
458 prev_positions,
459 }));
460
461 if current_line.as_ref().borrow_mut().lines.contains(&new_line) {
462 let index = match current_line
463 .as_ref()
464 .borrow()
465 .lines
466 .iter()
467 .position(|x| *x == new_line)
468 {
469 Some(idx) => idx,
470 None => unreachable!(),
471 };
472
473 self.next_move_variant(index as u32);
474 } else {
475 current_line
476 .as_ref()
477 .borrow_mut()
478 .lines
479 .push(Rc::clone(&new_line));
480 self.current_line = Some(new_line);
481 }
482 } else {
483 let new_line = Rc::new(RefCell::new(PgnLine {
484 lines: Vec::new(),
485 parent: None,
486 halfmove_clock,
487 fullmove_number,
488 en_passant,
489 castling_rights,
490 game_status,
491 mov,
492 prev_positions,
493 }));
494
495 if self.lines.contains(&new_line) {
496 let index = match self.lines.iter().position(|x| *x == new_line) {
497 Some(idx) => idx,
498 None => unreachable!(),
499 };
500
501 self.next_move_variant(index as u32);
502 } else {
503 self.lines.push(Rc::clone(&new_line));
504 self.current_line = Some(new_line);
505 }
506 }
507 }
508
509 pub fn rm_move(&mut self) {
537 let current_line = match self.current_line.take() {
538 Some(line) => line,
539 None => return,
540 };
541
542 let borrowed_line = current_line.borrow();
543
544 if let Some(weak_parent) = &borrowed_line.parent {
545 let parent = weak_parent.upgrade().unwrap();
546
547 let index = match parent
548 .borrow()
549 .lines
550 .iter()
551 .position(|x| Rc::ptr_eq(x, ¤t_line))
552 {
553 Some(idx) => idx,
554 None => unreachable!(),
555 };
556
557 parent.as_ref().borrow_mut().lines.remove(index);
558
559 self.current_line = Some(parent);
560 } else {
561 let index = match self.lines.iter().position(|x| Rc::ptr_eq(x, ¤t_line)) {
563 Some(idx) => idx,
564 None => unreachable!(),
565 };
566 self.lines.remove(index);
567
568 match self.lines.get(0) {
569 Some(main_line) => {
570 self.current_line = Some(Rc::clone(main_line));
571 }
572 None => {
573 self.current_line = None;
574 }
575 }
576 }
577 }
578
579 pub fn get_move(&self) -> Option<T> {
611 Some(self.current_line.as_ref()?.borrow().mov.clone())
612 }
613
614 pub fn get_move_info(&self) -> Option<MoveInfo> {
646 let current_line = self.current_line.as_ref()?.borrow();
647 Some(MoveInfo::new(
648 current_line.halfmove_clock,
649 current_line.fullmove_number,
650 current_line.en_passant,
651 current_line.castling_rights,
652 current_line.game_status,
653 current_line.prev_positions.clone(),
654 ))
655 }
656
657 pub fn next_move(&mut self) -> Option<T> {
705 self.next_move_variant(0)
706 }
707
708 pub fn next_move_variant(&mut self, variant: u32) -> Option<T> {
762 if let Some(current_line) = &self.current_line {
763 if current_line.as_ref().borrow().lines.len() > variant as usize {
764 let next_line = Rc::clone(¤t_line.as_ref().borrow().lines[variant as usize]);
765 self.current_line = Some(Rc::clone(&next_line));
766 return Some(next_line.as_ref().borrow().mov.clone());
767 }
768 } else {
769 if self.lines.len() > variant as usize {
770 let next_line = Rc::clone(&self.lines[variant as usize]);
771 self.current_line = Some(Rc::clone(&next_line));
772 return Some(next_line.as_ref().borrow().mov.clone());
773 }
774 }
775 None
776 }
777
778 pub fn all_next_moves(&self) -> Vec<T> {
828 let mut moves = Vec::new();
829 if let Some(current_line) = &self.current_line {
830 for line in current_line.as_ref().borrow().lines.iter() {
831 moves.push(line.as_ref().borrow().mov.clone());
832 }
833 } else {
834 for line in self.lines.iter() {
835 moves.push(line.as_ref().borrow().mov.clone());
836 }
837 }
838 moves
839 }
840
841 pub fn prev_move(&mut self) -> Option<T> {
888 if let Some(current_line) = self.current_line.take() {
889 if let Some(parent_weak) = ¤t_line.borrow().parent {
890 if let Some(parent_rc) = parent_weak.upgrade() {
891 let mov = parent_rc.borrow().mov.clone();
892 self.current_line = Some(parent_rc);
893 return Some(mov);
894 }
895 }
896 }
897 self.current_line = None;
898 None
899 }
900
901 pub fn has_next_move(&self) -> bool {
933 match &self.current_line {
934 Some(current_line) => current_line.borrow().lines.len() > 0,
935 None => !self.lines.is_empty(),
936 }
937 }
938
939 pub fn has_prev_move(&self) -> bool {
970 self.current_line.is_some()
971 }
972
973 pub fn pgn(&self) -> String {
1019 let mut pgn = String::new();
1020 pgn.push_str(&self.pgn_header());
1021 pgn.push_str(&self.pgn_moves());
1022 if !self.result.is_empty() {
1023 pgn.push_str(&format!(" {}\n", self.result));
1024 }
1025 pgn
1026 }
1027
1028 pub fn game_over(&mut self, game_status: GameStatus) {
1045 match game_status {
1046 GameStatus::WhiteWins(_) => {
1047 self.result = "1-0".to_string();
1048 }
1049 GameStatus::BlackWins(_) => {
1050 self.result = "0-1".to_string();
1051 }
1052 GameStatus::Draw(_) => {
1053 self.result = "1/2-1/2".to_string();
1054 }
1055 _ => (),
1056 }
1057 }
1058
1059 fn pgn_header(&self) -> String {
1065 let mut header = String::new();
1066 header.push_str(&format!("[Event \"{}\"]\n", self.event));
1067 header.push_str(&format!("[Site \"{}\"]\n", self.site));
1068 header.push_str(&format!("[Date \"{}\"]\n", self.date));
1069 header.push_str(&format!("[Round \"{}\"]\n", self.round));
1070 header.push_str(&format!("[White \"{}\"]\n", self.white));
1071 header.push_str(&format!("[Black \"{}\"]\n", self.black));
1072 if let Some(variant) = &self.variant {
1073 if variant != "Standard" {
1074 header.push_str(&format!("[Variant \"{}\"]\n", variant));
1075 }
1076 }
1077 header.push_str(&format!("[Result \"{}\"]\n", self.result));
1078 for metadata in self.option_metadata.iter() {
1079 header.push_str(&format!("{}\n", metadata));
1080 }
1081 header
1082 }
1083
1084 fn pgn_moves(&self) -> String {
1090 let mut pgn = String::new();
1091
1092 if self.lines.is_empty() {
1093 return pgn;
1094 }
1095
1096 let line = self.lines[0].as_ref().borrow();
1097 pgn.push_str(&format!("1. {}", line.mov));
1098
1099 for next in self.lines.iter().skip(1) {
1100 pgn.push_str(&format!(
1101 " {}",
1102 self.pgn_line_moves(Rc::clone(next), 1, true)
1103 ));
1104 }
1105
1106 if self.lines.len() > 1 {
1107 pgn.push_str(&format!(
1108 " 1... {}",
1109 self.pgn_line_moves(Rc::clone(&self.lines[0]), 2, false)
1110 ));
1111 } else {
1112 pgn.push_str(&format!(
1113 " {}",
1114 self.pgn_line_moves(Rc::clone(&self.lines[0]), 2, false)
1115 ));
1116 };
1117
1118 if pgn.ends_with(' ') {
1119 pgn.pop();
1120 }
1121
1122 pgn
1123 }
1124
1125 fn pgn_line_moves(
1136 &self,
1137 line: Rc<RefCell<PgnLine<T>>>,
1138 move_number: u32,
1139 secondary: bool,
1140 ) -> String {
1141 let mut pgn = String::new();
1142
1143 let mut tmp_move_number = move_number;
1144
1145 if secondary {
1146 if tmp_move_number % 2 == 0 {
1147 pgn.push_str(&format!("{}... ", tmp_move_number / 2));
1148 } else {
1149 pgn.push_str(&format!("{}. ", tmp_move_number / 2 + 1));
1150 }
1151 pgn.push_str(&format!("{} ", line.as_ref().borrow().mov));
1152
1153 tmp_move_number += 1;
1154 }
1155
1156 let mut stack = vec![line];
1157
1158 let mut is_half_sequence = false;
1159
1160 while let Some(current) = stack.pop() {
1161 let line = current.as_ref().borrow();
1162
1163 if line.lines.is_empty() {
1164 pgn.pop();
1165 continue;
1166 } else {
1167 if tmp_move_number % 2 != 0 {
1168 pgn.push_str(&format!("{}. ", tmp_move_number / 2 + 1));
1169 };
1170
1171 let next = Rc::clone(&line.lines[0]);
1172 if is_half_sequence {
1173 is_half_sequence = false;
1174 pgn.push_str(&format!(
1175 "{}... {} ",
1176 tmp_move_number / 2,
1177 next.as_ref().borrow().mov
1178 ));
1179 } else {
1180 pgn.push_str(&format!("{} ", next.as_ref().borrow().mov));
1181 }
1182 stack.push(Rc::clone(&next));
1183 tmp_move_number += 1;
1184
1185 if line.lines.len() != 1 {
1186 is_half_sequence = tmp_move_number % 2 == 0;
1187 for next in line.lines.iter().skip(1) {
1188 pgn.push_str(&format!(
1189 "{} ",
1190 self.pgn_line_moves(Rc::clone(next), tmp_move_number - 1, true)
1191 ));
1192 }
1193 }
1194 }
1195 }
1196
1197 if secondary {
1198 format!("({})", pgn)
1199 } else {
1200 pgn
1201 }
1202 }
1203}
1204
1205impl<T: PartialEq + Clone + Display + Debug> Iterator for PGNTree<T> {
1206 type Item = T;
1207
1208 fn next(&mut self) -> Option<Self::Item> {
1256 self.next_move()
1257 }
1258}
1259
1260impl<T: PartialEq + Clone + Display + Debug> DoubleEndedIterator for PGNTree<T> {
1261 fn next_back(&mut self) -> Option<Self::Item> {
1308 self.prev_move()
1309 }
1310}
1311
1312#[cfg(test)]
1313mod tests {
1314 use std::collections::HashMap;
1315
1316 use crate::core::{Color, Move, MoveType, Piece, PieceType, WinReason};
1317
1318 use super::*;
1319
1320 #[test]
1321 fn test_add_move() {
1322 let mut pgn_tree = PGNTree::default();
1323 let mov = Move::new(
1324 Piece::new(Color::Black, PieceType::Pawn),
1325 Position::from_string("e2").unwrap(),
1326 Position::from_string("e4").unwrap(),
1327 MoveType::Normal {
1328 capture: false,
1329 promotion: None,
1330 },
1331 None,
1332 None,
1333 (false, false),
1334 false,
1335 false,
1336 )
1337 .unwrap();
1338
1339 pgn_tree.add_move(
1340 mov.clone(),
1341 0,
1342 0,
1343 None,
1344 0,
1345 GameStatus::InProgress,
1346 HashMap::new(),
1347 );
1348
1349 assert_eq!(mov, pgn_tree.get_move().unwrap());
1350 }
1351
1352 #[test]
1353 fn test_rm_move() {
1354 let mut pgn_tree = PGNTree::default();
1355 let mov = Move::new(
1356 Piece::new(Color::Black, PieceType::Pawn),
1357 Position::from_string("e2").unwrap(),
1358 Position::from_string("e4").unwrap(),
1359 MoveType::Normal {
1360 capture: false,
1361 promotion: None,
1362 },
1363 None,
1364 None,
1365 (false, false),
1366 false,
1367 false,
1368 )
1369 .unwrap();
1370 pgn_tree.add_move(
1371 mov.clone(),
1372 0,
1373 0,
1374 None,
1375 0,
1376 GameStatus::InProgress,
1377 HashMap::new(),
1378 );
1379 pgn_tree.rm_move();
1380
1381 assert!(pgn_tree.get_move().is_none());
1382 }
1383
1384 #[test]
1385 fn test_prev_move() {
1386 let mut pgn_tree = PGNTree::default();
1387 let mov1 = Move::new(
1388 Piece::new(Color::Black, PieceType::Pawn),
1389 Position::from_string("e2").unwrap(),
1390 Position::from_string("e4").unwrap(),
1391 MoveType::Normal {
1392 capture: false,
1393 promotion: None,
1394 },
1395 None,
1396 None,
1397 (false, false),
1398 false,
1399 false,
1400 )
1401 .unwrap();
1402 let mov2 = Move::new(
1403 Piece::new(Color::White, PieceType::Pawn),
1404 Position::from_string("e7").unwrap(),
1405 Position::from_string("e5").unwrap(),
1406 MoveType::Normal {
1407 capture: false,
1408 promotion: None,
1409 },
1410 None,
1411 None,
1412 (false, false),
1413 false,
1414 false,
1415 )
1416 .unwrap();
1417 pgn_tree.add_move(
1418 mov1.clone(),
1419 0,
1420 0,
1421 None,
1422 0,
1423 GameStatus::InProgress,
1424 HashMap::new(),
1425 );
1426 pgn_tree.add_move(
1427 mov2.clone(),
1428 0,
1429 0,
1430 None,
1431 0,
1432 GameStatus::InProgress,
1433 HashMap::new(),
1434 );
1435
1436 assert_eq!(mov2, pgn_tree.get_move().unwrap());
1437 assert_eq!(mov1, pgn_tree.prev_move().unwrap());
1438 assert!(pgn_tree.prev_move().is_none());
1439 }
1440
1441 #[test]
1442 fn test_next_move() {
1443 let mut pgn_tree = PGNTree::default();
1444 let mov1 = Move::new(
1445 Piece::new(Color::Black, PieceType::Pawn),
1446 Position::from_string("e2").unwrap(),
1447 Position::from_string("e4").unwrap(),
1448 MoveType::Normal {
1449 capture: false,
1450 promotion: None,
1451 },
1452 None,
1453 None,
1454 (false, false),
1455 false,
1456 false,
1457 )
1458 .unwrap();
1459 let mov2 = Move::new(
1460 Piece::new(Color::White, PieceType::Pawn),
1461 Position::from_string("e7").unwrap(),
1462 Position::from_string("e5").unwrap(),
1463 MoveType::Normal {
1464 capture: false,
1465 promotion: None,
1466 },
1467 None,
1468 None,
1469 (false, false),
1470 false,
1471 false,
1472 )
1473 .unwrap();
1474 pgn_tree.add_move(
1475 mov1.clone(),
1476 0,
1477 0,
1478 None,
1479 0,
1480 GameStatus::InProgress,
1481 HashMap::new(),
1482 );
1483 pgn_tree.add_move(
1484 mov2.clone(),
1485 0,
1486 0,
1487 None,
1488 0,
1489 GameStatus::InProgress,
1490 HashMap::new(),
1491 );
1492
1493 assert_eq!(mov2, pgn_tree.get_move().unwrap());
1494 assert_eq!(mov1, pgn_tree.prev_move().unwrap());
1495 assert_eq!(mov2, pgn_tree.next_move().unwrap());
1496 }
1497
1498 #[test]
1499 fn test_all_next_moves() {
1500 let mut pgn_tree = PGNTree::default();
1501 let mov1 = Move::new(
1502 Piece::new(Color::White, PieceType::Pawn),
1503 Position::from_string("e4").unwrap(),
1504 Position::from_string("e2").unwrap(),
1505 MoveType::Normal {
1506 capture: false,
1507 promotion: None,
1508 },
1509 None,
1510 None,
1511 (false, false),
1512 false,
1513 false,
1514 )
1515 .unwrap();
1516
1517 let mov2 = Move::new(
1518 Piece::new(Color::White, PieceType::Pawn),
1519 Position::from_string("d2").unwrap(),
1520 Position::from_string("d4").unwrap(),
1521 MoveType::Normal {
1522 capture: false,
1523 promotion: None,
1524 },
1525 None,
1526 None,
1527 (false, false),
1528 false,
1529 false,
1530 )
1531 .unwrap();
1532
1533 pgn_tree.add_move(
1534 mov1.clone(),
1535 0,
1536 0,
1537 None,
1538 0,
1539 GameStatus::InProgress,
1540 HashMap::new(),
1541 );
1542 pgn_tree.prev_move();
1543 pgn_tree.add_move(
1544 mov2.clone(),
1545 0,
1546 0,
1547 None,
1548 0,
1549 GameStatus::InProgress,
1550 HashMap::new(),
1551 );
1552 pgn_tree.prev_move();
1553
1554 assert_eq!(vec![mov1.clone(), mov2.clone()], pgn_tree.all_next_moves());
1555 }
1556
1557 #[test]
1558 fn test_pgn_header() {
1559 let tree: PGNTree<Move> = PGNTree::new(
1560 "Event".to_string(),
1561 "Site".to_string(),
1562 "Date".to_string(),
1563 "Round".to_string(),
1564 "White".to_string(),
1565 "Black".to_string(),
1566 "Result".to_string(),
1567 None,
1568 );
1569
1570 assert_eq!(tree.pgn_header(), "[Event \"Event\"]\n[Site \"Site\"]\n[Date \"Date\"]\n[Round \"Round\"]\n[White \"White\"]\n[Black \"Black\"]\n[Result \"Result\"]\n");
1571 }
1572
1573 #[test]
1574 fn test_pgn_optional_metadata() {
1575 let mut tree: PGNTree<Move> = PGNTree::default();
1576 tree.add_metadata("TimeControl", "3+2").unwrap();
1577 tree.add_metadata("Termination", "Draw").unwrap();
1578 tree.add_metadata("WhiteTitle", "GM").unwrap();
1579 tree.add_metadata("BlackTitle", "IM").unwrap();
1580 tree.add_metadata("WhiteUSCF", "no rating").unwrap();
1581 tree.add_metadata("BlackUSCF", "no rating").unwrap();
1582 tree.add_metadata("WhiteNA", "no rating").unwrap();
1583 tree.add_metadata("BlackNA", "no rating").unwrap();
1584 tree.add_metadata("WhiteType", "no rating").unwrap();
1585 tree.add_metadata("BlackType", "no rating").unwrap();
1586 tree.add_metadata("EventSponsor", "none").unwrap();
1587 tree.add_metadata("Section", "none").unwrap();
1588 tree.add_metadata("Stage", "none").unwrap();
1589 tree.add_metadata("Board", "none").unwrap();
1590 tree.add_metadata("Variation", "Queen's Gambit").unwrap();
1591 tree.add_metadata("SubVariation", "Catalan System").unwrap();
1592 tree.add_metadata("NIC", "none").unwrap();
1593 tree.add_metadata("Time", "3pm").unwrap();
1594 tree.add_metadata("SetUp", "none").unwrap();
1595 tree.add_metadata(
1596 "FEN",
1597 "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
1598 )
1599 .unwrap();
1600 tree.add_metadata("Mode", "Standard").unwrap();
1601
1602 assert!(tree.pgn_header().contains("[TimeControl \"3+2\"]"));
1603 assert!(tree.pgn_header().contains("[Termination \"Draw\"]"));
1604 assert!(tree.pgn_header().contains("[WhiteTitle \"GM\"]"));
1605 assert!(tree.pgn_header().contains("[BlackTitle \"IM\"]"));
1606 assert!(tree.pgn_header().contains("[WhiteUSCF \"no rating\"]"));
1607 assert!(tree.pgn_header().contains("[BlackUSCF \"no rating\"]"));
1608 assert!(tree.pgn_header().contains("[WhiteNA \"no rating\"]"));
1609 assert!(tree.pgn_header().contains("[BlackNA \"no rating\"]"));
1610 assert!(tree.pgn_header().contains("[WhiteType \"no rating\"]"));
1611 assert!(tree.pgn_header().contains("[BlackType \"no rating\"]"));
1612 assert!(tree.pgn_header().contains("[EventSponsor \"none\"]"));
1613 assert!(tree.pgn_header().contains("[Section \"none\"]"));
1614 assert!(tree.pgn_header().contains("[Stage \"none\"]"));
1615 assert!(tree.pgn_header().contains("[Board \"none\"]"));
1616 assert!(tree.pgn_header().contains("[Variation \"Queen's Gambit\"]"));
1617 assert!(tree
1618 .pgn_header()
1619 .contains("[SubVariation \"Catalan System\"]"));
1620 assert!(tree.pgn_header().contains("[NIC \"none\"]"));
1621 assert!(tree.pgn_header().contains("[Time \"3pm\"]"));
1622 assert!(tree.pgn_header().contains("[SetUp \"none\"]"));
1623 assert!(tree
1624 .pgn_header()
1625 .contains("[FEN \"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1\"]"));
1626 assert!(tree.pgn_header().contains("[Mode \"Standard\"]"));
1627 }
1628
1629 #[test]
1630 fn test_next_variant() {
1631 let mut pgn_tree = PGNTree::default();
1632 let mov1 = Move::new(
1633 Piece::new(Color::White, PieceType::Pawn),
1634 Position::from_string("e2").unwrap(),
1635 Position::from_string("e4").unwrap(),
1636 MoveType::Normal {
1637 capture: false,
1638 promotion: None,
1639 },
1640 None,
1641 None,
1642 (false, false),
1643 false,
1644 false,
1645 )
1646 .unwrap();
1647 let mov2 = Move::new(
1648 Piece::new(Color::White, PieceType::Pawn),
1649 Position::from_string("d2").unwrap(),
1650 Position::from_string("d4").unwrap(),
1651 MoveType::Normal {
1652 capture: false,
1653 promotion: None,
1654 },
1655 None,
1656 None,
1657 (false, false),
1658 false,
1659 false,
1660 )
1661 .unwrap();
1662 pgn_tree.add_move(
1663 mov1.clone(),
1664 0,
1665 0,
1666 None,
1667 0,
1668 GameStatus::InProgress,
1669 HashMap::new(),
1670 );
1671 pgn_tree.prev_move();
1672 pgn_tree.add_move(
1673 mov2.clone(),
1674 0,
1675 0,
1676 None,
1677 0,
1678 GameStatus::InProgress,
1679 HashMap::new(),
1680 );
1681
1682 pgn_tree.prev_move();
1683 assert_eq!(mov1, pgn_tree.next_move().unwrap());
1684
1685 pgn_tree.prev_move();
1686 assert_eq!(mov2, pgn_tree.next_move_variant(1).unwrap());
1687 }
1688
1689 #[test]
1690 fn test_pgn() {
1691 let mut pgn_tree = PGNTree::default();
1692 let mov1 = Move::new(
1693 Piece::new(Color::White, PieceType::Pawn),
1694 Position::from_string("e2").unwrap(),
1695 Position::from_string("e4").unwrap(),
1696 MoveType::Normal {
1697 capture: false,
1698 promotion: None,
1699 },
1700 None,
1701 None,
1702 (false, false),
1703 false,
1704 false,
1705 )
1706 .unwrap();
1707 let mov2 = Move::new(
1708 Piece::new(Color::Black, PieceType::Pawn),
1709 Position::from_string("e7").unwrap(),
1710 Position::from_string("e5").unwrap(),
1711 MoveType::Normal {
1712 capture: false,
1713 promotion: None,
1714 },
1715 None,
1716 None,
1717 (false, false),
1718 false,
1719 false,
1720 )
1721 .unwrap();
1722 pgn_tree.add_move(
1723 mov1.clone(),
1724 0,
1725 0,
1726 None,
1727 0,
1728 GameStatus::InProgress,
1729 HashMap::new(),
1730 );
1731 pgn_tree.add_move(
1732 mov2.clone(),
1733 0,
1734 0,
1735 None,
1736 0,
1737 GameStatus::InProgress,
1738 HashMap::new(),
1739 );
1740
1741 assert_eq!(pgn_tree.pgn(), "[Event \"\"]\n[Site \"\"]\n[Date \"\"]\n[Round \"\"]\n[White \"\"]\n[Black \"\"]\n[Result \"\"]\n1. e4 e5");
1742 }
1743
1744 #[test]
1745 fn test_empty_pgn() {
1746 let pgn_tree = PGNTree::<Move>::default();
1747
1748 assert_eq!(pgn_tree.pgn(), "[Event \"\"]\n[Site \"\"]\n[Date \"\"]\n[Round \"\"]\n[White \"\"]\n[Black \"\"]\n[Result \"\"]\n");
1749 }
1750
1751 #[test]
1752 fn test_play_same_move() {
1753 let mut pgn_tree = PGNTree::default();
1754 let mov1 = Move::new(
1755 Piece::new(Color::White, PieceType::Pawn),
1756 Position::from_string("e2").unwrap(),
1757 Position::from_string("e4").unwrap(),
1758 MoveType::Normal {
1759 capture: false,
1760 promotion: None,
1761 },
1762 None,
1763 None,
1764 (false, false),
1765 false,
1766 false,
1767 )
1768 .unwrap();
1769
1770 let mov2 = Move::new(
1771 Piece::new(Color::White, PieceType::Pawn),
1772 Position::from_string("e7").unwrap(),
1773 Position::from_string("e5").unwrap(),
1774 MoveType::Normal {
1775 capture: false,
1776 promotion: None,
1777 },
1778 None,
1779 None,
1780 (false, false),
1781 false,
1782 false,
1783 )
1784 .unwrap();
1785
1786 pgn_tree.add_move(
1787 mov1.clone(),
1788 0,
1789 0,
1790 None,
1791 0,
1792 GameStatus::InProgress,
1793 HashMap::new(),
1794 );
1795 pgn_tree.prev_move();
1796 pgn_tree.add_move(
1797 mov1.clone(),
1798 0,
1799 0,
1800 None,
1801 0,
1802 GameStatus::InProgress,
1803 HashMap::new(),
1804 );
1805 pgn_tree.add_move(
1806 mov2.clone(),
1807 0,
1808 0,
1809 None,
1810 0,
1811 GameStatus::InProgress,
1812 HashMap::new(),
1813 );
1814 pgn_tree.prev_move();
1815 pgn_tree.add_move(
1816 mov2.clone(),
1817 0,
1818 0,
1819 None,
1820 0,
1821 GameStatus::InProgress,
1822 HashMap::new(),
1823 );
1824
1825 assert_eq!(1, pgn_tree.lines.len());
1826 assert_eq!(1, pgn_tree.lines[0].as_ref().borrow().lines.len());
1827 }
1828
1829 #[test]
1830 fn test_rm_move_subline() {
1831 let mut pgn_tree = PGNTree::default();
1832 let mov1 = Move::new(
1833 Piece::new(Color::White, PieceType::Pawn),
1834 Position::from_string("e2").unwrap(),
1835 Position::from_string("e4").unwrap(),
1836 MoveType::Normal {
1837 capture: false,
1838 promotion: None,
1839 },
1840 None,
1841 None,
1842 (false, false),
1843 false,
1844 false,
1845 )
1846 .unwrap();
1847 let mov2 = Move::new(
1848 Piece::new(Color::White, PieceType::Pawn),
1849 Position::from_string("d2").unwrap(),
1850 Position::from_string("d4").unwrap(),
1851 MoveType::Normal {
1852 capture: false,
1853 promotion: None,
1854 },
1855 None,
1856 None,
1857 (false, false),
1858 false,
1859 false,
1860 )
1861 .unwrap();
1862 pgn_tree.add_move(
1863 mov1.clone(),
1864 0,
1865 0,
1866 None,
1867 0,
1868 GameStatus::InProgress,
1869 HashMap::new(),
1870 );
1871 pgn_tree.prev_move();
1872 pgn_tree.add_move(
1873 mov2.clone(),
1874 0,
1875 0,
1876 None,
1877 0,
1878 GameStatus::InProgress,
1879 HashMap::new(),
1880 );
1881
1882 pgn_tree.rm_move();
1883
1884 assert_eq!(1, pgn_tree.lines.len());
1885 assert_eq!(pgn_tree.current_line.unwrap(), pgn_tree.lines[0]);
1886 assert_eq!(0, pgn_tree.lines[0].as_ref().borrow().lines.len());
1887 }
1888
1889 #[test]
1890 fn test_rm_move_not_at_root() {
1891 let mut pgn_tree = PGNTree::default();
1892 let mov1 = Move::new(
1893 Piece::new(Color::White, PieceType::Pawn),
1894 Position::from_string("e2").unwrap(),
1895 Position::from_string("e4").unwrap(),
1896 MoveType::Normal {
1897 capture: false,
1898 promotion: None,
1899 },
1900 None,
1901 None,
1902 (false, false),
1903 false,
1904 false,
1905 )
1906 .unwrap();
1907 let mov2 = Move::new(
1908 Piece::new(Color::White, PieceType::Pawn),
1909 Position::from_string("d2").unwrap(),
1910 Position::from_string("d4").unwrap(),
1911 MoveType::Normal {
1912 capture: false,
1913 promotion: None,
1914 },
1915 None,
1916 None,
1917 (false, false),
1918 false,
1919 false,
1920 )
1921 .unwrap();
1922 pgn_tree.add_move(
1923 mov1.clone(),
1924 0,
1925 0,
1926 None,
1927 0,
1928 GameStatus::InProgress,
1929 HashMap::new(),
1930 );
1931 pgn_tree.add_move(
1932 mov2.clone(),
1933 0,
1934 0,
1935 None,
1936 0,
1937 GameStatus::InProgress,
1938 HashMap::new(),
1939 );
1940
1941 pgn_tree.rm_move();
1942
1943 assert_eq!(1, pgn_tree.lines.len());
1944 assert_eq!(0, pgn_tree.lines[0].as_ref().borrow().lines.len());
1945 }
1946
1947 #[test]
1948 fn test_rm_move_no_current_line() {
1949 let mut pgn_tree = PGNTree::<Move>::default();
1950
1951 pgn_tree.rm_move();
1952
1953 assert!(pgn_tree.current_line.is_none());
1954 assert!(pgn_tree.lines.is_empty());
1955 }
1956
1957 #[test]
1958 fn test_has_prev_move() {
1959 let mut pgn_tree = PGNTree::default();
1960 let mov1 = Move::new(
1961 Piece::new(Color::White, PieceType::Pawn),
1962 Position::from_string("e2").unwrap(),
1963 Position::from_string("e4").unwrap(),
1964 MoveType::Normal {
1965 capture: false,
1966 promotion: None,
1967 },
1968 None,
1969 None,
1970 (false, false),
1971 false,
1972 false,
1973 )
1974 .unwrap();
1975
1976 assert!(!pgn_tree.has_prev_move());
1977 pgn_tree.add_move(
1978 mov1.clone(),
1979 0,
1980 0,
1981 None,
1982 0,
1983 GameStatus::InProgress,
1984 HashMap::new(),
1985 );
1986
1987 assert!(pgn_tree.has_prev_move());
1988 }
1989
1990 #[test]
1991 fn test_has_next_move() {
1992 let mut pgn_tree = PGNTree::default();
1993 let mov1 = Move::new(
1994 Piece::new(Color::White, PieceType::Pawn),
1995 Position::from_string("e2").unwrap(),
1996 Position::from_string("e4").unwrap(),
1997 MoveType::Normal {
1998 capture: false,
1999 promotion: None,
2000 },
2001 None,
2002 None,
2003 (false, false),
2004 false,
2005 false,
2006 )
2007 .unwrap();
2008 let mov2 = Move::new(
2009 Piece::new(Color::Black, PieceType::Pawn),
2010 Position::from_string("e7").unwrap(),
2011 Position::from_string("e5").unwrap(),
2012 MoveType::Normal {
2013 capture: false,
2014 promotion: None,
2015 },
2016 None,
2017 None,
2018 (false, false),
2019 false,
2020 false,
2021 )
2022 .unwrap();
2023
2024 pgn_tree.add_move(
2025 mov1.clone(),
2026 0,
2027 0,
2028 None,
2029 0,
2030 GameStatus::InProgress,
2031 HashMap::new(),
2032 );
2033 pgn_tree.add_move(
2034 mov2.clone(),
2035 0,
2036 0,
2037 None,
2038 0,
2039 GameStatus::InProgress,
2040 HashMap::new(),
2041 );
2042 pgn_tree.prev_move();
2043
2044 assert!(pgn_tree.has_next_move());
2045
2046 pgn_tree.next_move();
2047 assert!(!pgn_tree.has_next_move());
2048 }
2049
2050 #[test]
2051 fn test_all_next_moves_non_root() {
2052 let mut pgn_tree = PGNTree::default();
2053 let mov1 = Move::new(
2054 Piece::new(Color::White, PieceType::Pawn),
2055 Position::from_string("e4").unwrap(),
2056 Position::from_string("e2").unwrap(),
2057 MoveType::Normal {
2058 capture: false,
2059 promotion: None,
2060 },
2061 None,
2062 None,
2063 (false, false),
2064 false,
2065 false,
2066 )
2067 .unwrap();
2068 let mov2 = Move::new(
2069 Piece::new(Color::White, PieceType::Pawn),
2070 Position::from_string("d2").unwrap(),
2071 Position::from_string("d4").unwrap(),
2072 MoveType::Normal {
2073 capture: false,
2074 promotion: None,
2075 },
2076 None,
2077 None,
2078 (false, false),
2079 false,
2080 false,
2081 )
2082 .unwrap();
2083 pgn_tree.add_move(
2084 mov1.clone(),
2085 0,
2086 0,
2087 None,
2088 0,
2089 GameStatus::InProgress,
2090 HashMap::new(),
2091 );
2092 pgn_tree.add_move(
2093 mov2.clone(),
2094 0,
2095 0,
2096 None,
2097 0,
2098 GameStatus::InProgress,
2099 HashMap::new(),
2100 );
2101 pgn_tree.prev_move();
2102
2103 assert_eq!(vec![mov2.clone()], pgn_tree.all_next_moves());
2104 }
2105
2106 #[test]
2107 fn test_all_next_moves_empty() {
2108 let pgn_tree = PGNTree::<Move>::default();
2109
2110 assert!(pgn_tree.all_next_moves().is_empty());
2111 }
2112
2113 #[test]
2114 fn next_move_variant_out_of_bounds() {
2115 let mut pgn_tree = PGNTree::default();
2116 let mov1 = Move::new(
2117 Piece::new(Color::White, PieceType::Pawn),
2118 Position::from_string("e2").unwrap(),
2119 Position::from_string("e4").unwrap(),
2120 MoveType::Normal {
2121 capture: false,
2122 promotion: None,
2123 },
2124 None,
2125 None,
2126 (false, false),
2127 false,
2128 false,
2129 )
2130 .unwrap();
2131
2132 pgn_tree.add_move(
2133 mov1.clone(),
2134 0,
2135 0,
2136 None,
2137 0,
2138 GameStatus::InProgress,
2139 HashMap::new(),
2140 );
2141
2142 assert!(pgn_tree.next_move_variant(1).is_none());
2143 }
2144
2145 #[test]
2146 fn test_game_over() {
2147 let mut pgn_tree: PGNTree<Move> = PGNTree::default();
2148
2149 pgn_tree.game_over(GameStatus::InProgress);
2150
2151 assert_eq!(pgn_tree.result, "".to_string());
2152
2153 pgn_tree.game_over(GameStatus::WhiteWins(WinReason::Checkmate));
2154
2155 assert_eq!(pgn_tree.result, "1-0".to_string());
2156
2157 pgn_tree.game_over(GameStatus::BlackWins(WinReason::Resignation));
2158
2159 assert_eq!(pgn_tree.result, "0-1".to_string());
2160 }
2161
2162 #[test]
2163 fn test_iterator() {
2164 let mut pgn_tree = PGNTree::default();
2165 let mov1 = Move::new(
2166 Piece::new(Color::Black, PieceType::Pawn),
2167 Position::from_string("e2").unwrap(),
2168 Position::from_string("e4").unwrap(),
2169 MoveType::Normal {
2170 capture: false,
2171 promotion: None,
2172 },
2173 None,
2174 None,
2175 (false, false),
2176 false,
2177 false,
2178 )
2179 .unwrap();
2180 let mov2 = Move::new(
2181 Piece::new(Color::White, PieceType::Pawn),
2182 Position::from_string("e7").unwrap(),
2183 Position::from_string("e5").unwrap(),
2184 MoveType::Normal {
2185 capture: false,
2186 promotion: None,
2187 },
2188 None,
2189 None,
2190 (false, false),
2191 false,
2192 false,
2193 )
2194 .unwrap();
2195 pgn_tree.add_move(
2196 mov1.clone(),
2197 0,
2198 0,
2199 None,
2200 0,
2201 GameStatus::InProgress,
2202 HashMap::new(),
2203 );
2204 pgn_tree.add_move(
2205 mov2.clone(),
2206 0,
2207 0,
2208 None,
2209 0,
2210 GameStatus::InProgress,
2211 HashMap::new(),
2212 );
2213
2214 pgn_tree.prev_move();
2215 pgn_tree.prev_move();
2216
2217 assert_eq!(mov1, pgn_tree.next().unwrap());
2218 assert_eq!(mov2, pgn_tree.next().unwrap());
2219 }
2220
2221 #[test]
2222 fn test_double_ended_iter() {
2223 let mut pgn_tree = PGNTree::default();
2224 let mov1 = Move::new(
2225 Piece::new(Color::Black, PieceType::Pawn),
2226 Position::from_string("e2").unwrap(),
2227 Position::from_string("e4").unwrap(),
2228 MoveType::Normal {
2229 capture: false,
2230 promotion: None,
2231 },
2232 None,
2233 None,
2234 (false, false),
2235 false,
2236 false,
2237 )
2238 .unwrap();
2239 let mov2 = Move::new(
2240 Piece::new(Color::White, PieceType::Pawn),
2241 Position::from_string("e7").unwrap(),
2242 Position::from_string("e5").unwrap(),
2243 MoveType::Normal {
2244 capture: false,
2245 promotion: None,
2246 },
2247 None,
2248 None,
2249 (false, false),
2250 false,
2251 false,
2252 )
2253 .unwrap();
2254 pgn_tree.add_move(
2255 mov1.clone(),
2256 0,
2257 0,
2258 None,
2259 0,
2260 GameStatus::InProgress,
2261 HashMap::new(),
2262 );
2263 pgn_tree.add_move(
2264 mov2.clone(),
2265 0,
2266 0,
2267 None,
2268 0,
2269 GameStatus::InProgress,
2270 HashMap::new(),
2271 );
2272
2273 assert_eq!(mov2, pgn_tree.get_move().unwrap());
2274 assert_eq!(mov1, pgn_tree.next_back().unwrap());
2275 }
2276}