Skip to main content

chess_lab/core/
pgn_tree.rs

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/// An enum representing optional PGN metadata
14#[derive(Debug, Clone)]
15pub(crate) enum OptionPGNMetadata {
16    // Game metadata
17    /// The time control of the game
18    TimeControl(String),
19    /// The termination of the game
20    Termination(String),
21
22    // Player metadata
23    /// The white player's ELO
24    WhiteElo(u32),
25    /// The black player's ELO
26    BlackElo(u32),
27    /// The white player's title
28    WhiteTitle(String),
29    /// The black player's title
30    BlackTitle(String),
31    /// The white player's USCF rating
32    WhiteUSCF(String),
33    /// The black player's USCF rating
34    BlackUSCF(String),
35    /// The white player's national association
36    WhiteNA(String),
37    /// The black player's national association
38    BlackNA(String),
39    /// The white player's type
40    WhiteType(String),
41    /// The black player's type
42    BlackType(String),
43
44    // Event metadata
45    /// The event date
46    EventDate(String),
47    /// The event sponsor
48    EventSponsor(String),
49    /// The section of the event
50    Section(String),
51    /// The stage of the event
52    Stage(String),
53    /// The board number
54    Board(String),
55
56    // Opening metadata
57    /// The opening name
58    Opening(String),
59    /// The variation name
60    Variation(String),
61    /// The sub-variation name
62    SubVariation(String),
63    /// The ECO code
64    ECO(String),
65    /// The NIC code
66    NIC(String),
67
68    // Time and date metadata
69    /// The time
70    Time(String),
71    /// The UTC date
72    UTCDate(String),
73    /// The UTC time
74    UTCTime(String),
75
76    // Starting position metadata
77    /// The setup flag
78    SetUp(String),
79    /// The FEN string
80    FEN(String),
81
82    // Other metadata
83    /// The annotator name
84    Annotator(String),
85    /// The mode
86    Mode(String),
87    /// The play count
88    PlyCount(u32),
89}
90
91impl OptionPGNMetadata {
92    /// Create a new [OptionPGNMetadata] from a key and a value.
93    ///
94    /// # Arguments
95    /// * `key` - The key of the metadata.
96    /// * `value` - The value of the metadata.
97    ///
98    /// # Returns
99    /// An [OptionPGNMetadata] if the key is valid, otherwise None.
100    ///
101    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/// A struct representing a PGN line or variation
188/// Its also a tree node that contains a list of child nodes, the parent node,
189/// the move number and the move itself
190///
191#[derive(Debug, Clone)]
192struct PgnLine<T: PartialEq + Clone + Display + Debug> {
193    /// The list of child nodes
194    lines: Vec<Rc<RefCell<PgnLine<T>>>>,
195    /// The parent node
196    parent: Option<Weak<RefCell<PgnLine<T>>>>,
197    /// The halfmove clock
198    halfmove_clock: u32,
199    /// The fullmove number
200    fullmove_number: u32,
201    /// The en passant position
202    en_passant: Option<Position>,
203    /// The castling rights
204    castling_rights: u8,
205    /// The game status
206    game_status: GameStatus,
207    /// The previous positions
208    prev_positions: HashMap<String, u32>,
209    /// The move itself
210    mov: T,
211}
212
213impl<T: PartialEq + Clone + Display + Debug> PartialEq for PgnLine<T> {
214    /// Compares two PgnLine structs
215    /// Two PgnLine structs are equal if their moves are equal
216    ///
217    /// # Arguments
218    /// * `other`: The other PgnLine struct
219    ///
220    /// # Returns
221    /// A `bool` indicating if the two PgnLine structs are equal
222    ///
223    fn eq(&self, other: &Self) -> bool {
224        self.mov == other.mov
225    }
226}
227
228/// A struct representing a PGN tree
229/// It contains the game metadata and a list of lines
230/// The current line is the move node that is currently being checked
231///
232#[derive(Debug, Clone)]
233pub struct PGNTree<T: PartialEq + Clone + Display + Debug> {
234    /// The event name
235    pub event: String,
236    /// The site name
237    pub site: String,
238    /// The date of the game
239    pub date: String,
240    /// The round number
241    pub round: String,
242    /// The white player name
243    pub white: String,
244    /// The black player name
245    pub black: String,
246    /// The result of the game
247    pub result: String,
248    /// The variant of the game
249    pub variant: Option<String>,
250    /// The list of other metadata
251    pub(crate) option_metadata: Vec<OptionPGNMetadata>,
252    /// The list of lines
253    lines: Vec<Rc<RefCell<PgnLine<T>>>>,
254    /// The current line
255    current_line: Option<Rc<RefCell<PgnLine<T>>>>,
256}
257
258impl<T: PartialEq + Clone + Display + Debug> Default for PGNTree<T> {
259    /// Creates a new [PGNTree] with no metadata and an empty list of lines
260    ///
261    /// # Returns
262    /// A an empty [PGNTree]
263    ///
264    /// # Examples
265    /// ```
266    /// use chess_lab::core::{PGNTree, Move};
267    ///
268    /// let tree: PGNTree<Move> = PGNTree::default();
269    /// ```
270    ///
271    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    /// Creates a new [PGNTree] with the provided metadata and an empty list of lines
290    ///
291    /// # Arguments
292    /// * `event`: The event name
293    /// * `site`: The site name
294    /// * `date`: The date of the game
295    /// * `round`: The round number
296    /// * `white`: The white player name
297    /// * `black`: The black player name
298    /// * `result`: The result of the game
299    /// * `variant`: The variant of the game
300    /// * `white_elo`: The white player ELO
301    /// * `black_elo`: The black player ELO
302    /// * `time_control`: The time control of the game
303    ///
304    /// # Returns
305    /// A new [PGNTree]
306    ///
307    /// # Examples
308    /// ```
309    /// use chess_lab::core::{PGNTree, Move};
310    ///
311    /// let tree: PGNTree<Move> = PGNTree::new(
312    ///    "Event".to_string(),
313    ///    "Site".to_string(),
314    ///    "Date".to_string(),
315    ///    "Round".to_string(),
316    ///    "White".to_string(),
317    ///    "Black".to_string(),
318    ///    "Result".to_string(),
319    ///    None,
320    /// );
321    /// ```
322    ///
323    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    /// Adds metadata to the [PGNTree]
349    ///
350    /// # Arguments
351    /// * `key`: The metadata key
352    /// * `value`: The metadata value
353    ///
354    /// # Returns
355    /// A Result<(), PGNMetadataError>
356    /// * `Ok(())`: If the metadata was added successfully
357    /// * `Err(PGNMetadataError)`: If the metadata key is invalid
358    ///
359    /// # Examples
360    /// ```
361    /// use chess_lab::core::{PGNTree, Move};
362    ///
363    /// let mut tree: PGNTree<Move> = PGNTree::default();
364    /// tree.add_metadata("Event", "My Event").unwrap();
365    /// assert_eq!(tree.event, "My Event");
366    /// ```
367    ///
368    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    /// Adds a move to the current line
403    ///
404    /// # Arguments
405    /// * `mov`: The move to add
406    /// * `halfmove_clock`: The halfmove clock
407    /// * `fullmove_number`: The fullmove number
408    /// * `en_passant`: The en passant position
409    /// * `castling_rights`: The castling rights
410    /// * `game_status`: The game status
411    /// * `prev_positions`: The previous positions
412    ///
413    /// # Examples
414    /// ```
415    /// use chess_lab::core::{PGNTree, Piece, Move, MoveType, PieceType, Color, Position, GameStatus};
416    /// use std::collections::HashMap;
417    ///
418    /// let mut pgn_tree: PGNTree<Move> = PGNTree::default();
419    /// let mov = Move::new(
420    ///     Piece::new(Color::Black, PieceType::Pawn),
421    ///     Position::from_string("e2").unwrap(),
422    ///     Position::from_string("e4").unwrap(),
423    ///     MoveType::Normal {
424    ///         capture: false,
425    ///         promotion: None,
426    ///     },
427    ///     None,
428    ///     None,
429    ///     (false, false),
430    ///     false,
431    ///     false,
432    /// ).unwrap();
433    /// pgn_tree.add_move(mov.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
434    ///
435    /// assert_eq!(mov, pgn_tree.get_move().unwrap());
436    /// ```
437    ///
438    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(&current_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    /// Removes the current line
510    ///
511    /// # Examples
512    /// ```
513    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
514    /// use std::collections::HashMap;
515    ///
516    /// let mut tree = PGNTree::default();
517    ///
518    /// tree.add_move(Move::new(
519    ///     Piece::new(Color::Black, PieceType::Pawn),
520    ///     Position::from_string("e2").unwrap(),
521    ///     Position::from_string("e4").unwrap(),
522    ///     MoveType::Normal {
523    ///         capture: false,
524    ///         promotion: None,
525    ///     },
526    ///     None,
527    ///     None,
528    ///     (false, false),
529    ///     false,
530    ///     false,
531    /// ).unwrap(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
532    ///
533    /// tree.rm_move();
534    /// ```
535    ///
536    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, &current_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            // If there is no parent, we are at the root level.
562            let index = match self.lines.iter().position(|x| Rc::ptr_eq(x, &current_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    /// Returns the current move
580    ///
581    /// # Returns
582    /// The current move
583    ///
584    /// # Examples
585    /// ```
586    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
587    /// use std::collections::HashMap;
588    ///
589    /// let mut tree = PGNTree::default();
590    /// let mov = Move::new(
591    ///     Piece::new(Color::Black, PieceType::Pawn),
592    ///     Position::from_string("e2").unwrap(),
593    ///     Position::from_string("e4").unwrap(),
594    ///     MoveType::Normal {
595    ///         capture: false,
596    ///         promotion: None,
597    ///     },
598    ///     None,
599    ///     None,
600    ///     (false, false),
601    ///     false,
602    ///     false,
603    /// ).unwrap();
604    ///
605    /// tree.add_move(mov.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
606    ///
607    /// assert_eq!(tree.get_move(), Some(mov));
608    /// ```
609    ///
610    pub fn get_move(&self) -> Option<T> {
611        Some(self.current_line.as_ref()?.borrow().mov.clone())
612    }
613
614    /// Returns the move info
615    ///
616    /// # Returns
617    /// A tuple containing the halfmove clock, the fullmove number, the en passant position and the castling rights
618    ///
619    /// # Examples
620    /// ```
621    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus, MoveInfo};
622    /// use std::collections::HashMap;
623    ///
624    /// let mut tree = PGNTree::default();
625    /// let mov = Move::new(
626    ///     Piece::new(Color::Black, PieceType::Pawn),
627    ///     Position::from_string("e2").unwrap(),
628    ///     Position::from_string("e4").unwrap(),
629    ///     MoveType::Normal {
630    ///         capture: false,
631    ///         promotion: None,
632    ///     },
633    ///     None,
634    ///     None,
635    ///     (false, false),
636    ///     false,
637    ///     false,
638    /// ).unwrap();
639    ///
640    /// tree.add_move(mov.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
641    ///
642    /// assert_eq!(tree.get_move_info(), Some(MoveInfo::new(0, 0, None, 0, GameStatus::InProgress, HashMap::new())));
643    /// ```
644    ///
645    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    /// Returns the next move
658    ///
659    /// # Returns
660    /// The next move
661    ///
662    /// # Examples
663    /// ```
664    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
665    /// use std::collections::HashMap;
666    ///
667    /// let mut pgn_tree = PGNTree::default();
668    /// let mov1 = Move::new(
669    ///     Piece::new(Color::Black, PieceType::Pawn),
670    ///     Position::from_string("e2").unwrap(),
671    ///     Position::from_string("e4").unwrap(),
672    ///     MoveType::Normal {
673    ///         capture: false,
674    ///         promotion: None,
675    ///     },
676    ///     None,
677    ///     None,
678    ///     (false, false),
679    ///     false,
680    ///     false,
681    /// ).unwrap();
682    /// let mov2 = Move::new(
683    ///     Piece::new(Color::White, PieceType::Pawn),
684    ///     Position::from_string("e7").unwrap(),
685    ///     Position::from_string("e5").unwrap(),
686    ///     MoveType::Normal {
687    ///         capture: false,
688    ///         promotion: None,
689    ///     },
690    ///     None,
691    ///     None,
692    ///     (false, false),
693    ///     false,
694    ///     false,
695    /// ).unwrap();
696    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
697    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
698    ///
699    /// assert_eq!(mov2, pgn_tree.get_move().unwrap());
700    /// assert_eq!(mov1, pgn_tree.prev_move().unwrap());
701    /// assert_eq!(mov2, pgn_tree.next_move().unwrap());
702    /// ```
703    ///
704    pub fn next_move(&mut self) -> Option<T> {
705        self.next_move_variant(0)
706    }
707
708    /// Returns the next move variant
709    ///
710    /// # Arguments
711    /// * `variant`: The variant to get
712    ///
713    /// # Returns
714    /// The next move variant
715    ///
716    /// # Examples
717    /// ```
718    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
719    /// use std::collections::HashMap;
720    ///
721    /// let mut pgn_tree = PGNTree::default();
722    /// let mov1 = Move::new(
723    ///     Piece::new(Color::White, PieceType::Pawn),
724    ///     Position::from_string("e2").unwrap(),
725    ///     Position::from_string("e4").unwrap(),
726    ///     MoveType::Normal {
727    ///         capture: false,
728    ///         promotion: None,
729    ///     },
730    ///     None,
731    ///     None,
732    ///     (false, false),
733    ///     false,
734    ///     false,
735    /// ).unwrap();
736    /// let mov2 = Move::new(
737    ///     Piece::new(Color::White, PieceType::Pawn),
738    ///     Position::from_string("d2").unwrap(),
739    ///     Position::from_string("d4").unwrap(),
740    ///     MoveType::Normal {
741    ///         capture: false,
742    ///         promotion: None,
743    ///     },
744    ///     None,
745    ///     None,
746    ///     (false, false),
747    ///     false,
748    ///     false,
749    /// ).unwrap();
750    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
751    /// pgn_tree.prev_move();
752    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
753    ///
754    /// pgn_tree.prev_move();
755    /// assert_eq!(mov1, pgn_tree.next_move().unwrap());
756    ///
757    /// pgn_tree.prev_move();
758    /// assert_eq!(mov2, pgn_tree.next_move_variant(1).unwrap());
759    /// ```
760    ///
761    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(&current_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    /// Returns all the next moves
779    ///
780    /// # Returns
781    /// All the next moves
782    ///
783    /// # Example
784    /// ```
785    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
786    /// use std::collections::HashMap;
787    ///
788    /// let mut pgn_tree = PGNTree::default();
789    /// let mov1 = Move::new(
790    ///     Piece::new(Color::White, PieceType::Pawn),
791    ///     Position::from_string("e4").unwrap(),
792    ///     Position::from_string("e2").unwrap(),
793    ///     MoveType::Normal {
794    ///         capture: false,
795    ///         promotion: None,
796    ///     },
797    ///     None,
798    ///     None,
799    ///     (false, false),
800    ///     false,
801    ///     false,
802    /// ).unwrap();
803    ///
804    /// let mov2 = Move::new(
805    ///     Piece::new(Color::White, PieceType::Pawn),
806    ///     Position::from_string("d2").unwrap(),
807    ///     Position::from_string("d4").unwrap(),
808    ///     MoveType::Normal {
809    ///         capture: false,
810    ///         promotion: None,
811    ///     },
812    ///     None,
813    ///     None,
814    ///     (false, false),
815    ///     false,
816    ///     false,
817    /// ).unwrap();
818    ///
819    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
820    /// pgn_tree.prev_move();
821    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
822    /// pgn_tree.prev_move();
823    ///
824    /// assert_eq!(vec![mov1.clone(), mov2.clone()], pgn_tree.all_next_moves());
825    /// ```
826    ///
827    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    /// Returns the previous move
842    ///
843    /// # Returns
844    /// The previous move
845    ///
846    /// # Examples
847    /// ```
848    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
849    /// use std::collections::HashMap;
850    ///
851    /// let mut pgn_tree = PGNTree::default();
852    /// let mov1 = Move::new(
853    ///     Piece::new(Color::Black, PieceType::Pawn),
854    ///     Position::from_string("e2").unwrap(),
855    ///     Position::from_string("e4").unwrap(),
856    ///     MoveType::Normal {
857    ///         capture: false,
858    ///         promotion: None,
859    ///     },
860    ///     None,
861    ///     None,
862    ///     (false, false),
863    ///     false,
864    ///     false,
865    /// ). unwrap();
866    /// let mov2 = Move::new(
867    ///     Piece::new(Color::White, PieceType::Pawn),
868    ///     Position::from_string("e7").unwrap(),
869    ///     Position::from_string("e5").unwrap(),
870    ///     MoveType::Normal {
871    ///         capture: false,
872    ///         promotion: None,
873    ///     },
874    ///     None,
875    ///     None,
876    ///     (false, false),
877    ///     false,
878    ///     false,
879    /// ).unwrap();
880    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
881    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
882    ///
883    /// assert_eq!(mov2, pgn_tree.get_move().unwrap());
884    /// assert_eq!(mov1, pgn_tree.prev_move().unwrap());
885    /// ```
886    ///
887    pub fn prev_move(&mut self) -> Option<T> {
888        if let Some(current_line) = self.current_line.take() {
889            if let Some(parent_weak) = &current_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    /// Returns whether there is a next move
902    ///
903    /// # Returns
904    /// Whether there is a next move
905    ///
906    /// # Examples
907    /// ```
908    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
909    /// use std::collections::HashMap;
910    ///
911    /// let mut pgn_tree = PGNTree::default();
912    /// let mov1 = Move::new(
913    ///     Piece::new(Color::White, PieceType::Pawn),
914    ///     Position::from_string("e2").unwrap(),
915    ///     Position::from_string("e4").unwrap(),
916    ///     MoveType::Normal {
917    ///         capture: false,
918    ///         promotion: None,
919    ///     },
920    ///     None,
921    ///     None,
922    ///     (false, false),
923    ///     false,
924    ///     false,
925    /// ).unwrap();
926    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
927    /// pgn_tree.prev_move();
928    ///
929    /// assert!(pgn_tree.has_next_move());
930    /// ```
931    ///
932    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    /// Returns whether there is a previous move
940    ///
941    /// # Returns
942    /// Whether there is a previous move
943    ///
944    /// # Examples
945    /// ```
946    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
947    /// use std::collections::HashMap;
948    ///
949    /// let mut pgn_tree = PGNTree::default();
950    /// let mov1 = Move::new(
951    ///     Piece::new(Color::White, PieceType::Pawn),
952    ///     Position::from_string("e2").unwrap(),
953    ///     Position::from_string("e4").unwrap(),
954    ///     MoveType::Normal {
955    ///         capture: false,
956    ///         promotion: None,
957    ///     },
958    ///     None,
959    ///     None,
960    ///     (false, false),
961    ///     false,
962    ///     false,
963    /// ).unwrap();
964    ///
965    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
966    /// assert!(pgn_tree.has_prev_move());
967    /// ```
968    ///
969    pub fn has_prev_move(&self) -> bool {
970        self.current_line.is_some()
971    }
972
973    /// Returns the PGN of the game
974    ///
975    /// # Returns
976    /// The PGN of the game
977    ///
978    /// # Examples
979    /// ```
980    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
981    /// use std::collections::HashMap;
982    ///
983    /// let mut pgn_tree = PGNTree::default();
984    /// let mov1 = Move::new(
985    ///     Piece::new(Color::White, PieceType::Pawn),
986    ///     Position::from_string("e2").unwrap(),
987    ///     Position::from_string("e4").unwrap(),
988    ///     MoveType::Normal {
989    ///         capture: false,
990    ///         promotion: None,
991    ///     },
992    ///     None,
993    ///     None,
994    ///     (false, false),
995    ///     false,
996    ///     false,
997    /// ).unwrap();
998    /// let mov2 = Move::new(
999    ///     Piece::new(Color::Black, PieceType::Pawn),
1000    ///     Position::from_string("e7").unwrap(),
1001    ///     Position::from_string("e5").unwrap(),
1002    ///     MoveType::Normal {
1003    ///         capture: false,
1004    ///         promotion: None,
1005    ///     },
1006    ///     None,
1007    ///     None,
1008    ///     (false, false),
1009    ///     false,
1010    ///     false,
1011    /// ).unwrap();
1012    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
1013    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
1014    ///
1015    /// assert_eq!(pgn_tree.pgn(), "[Event \"\"]\n[Site \"\"]\n[Date \"\"]\n[Round \"\"]\n[White \"\"]\n[Black \"\"]\n[Result \"\"]\n1. e4 e5");
1016    /// ```
1017    ///
1018    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    /// Sets the game result
1029    ///
1030    /// # Arguments
1031    /// * `game_status` - The game status
1032    ///
1033    /// # Examples
1034    /// ```
1035    /// use chess_lab::core::{PGNTree, Move, GameStatus, WinReason};
1036    ///
1037    /// let mut pgn_tree: PGNTree<Move> = PGNTree::default();
1038    ///
1039    /// pgn_tree.game_over(GameStatus::WhiteWins(WinReason::Checkmate));
1040    ///
1041    /// assert_eq!(pgn_tree.result, "1-0".to_string());
1042    /// ```
1043    ///
1044    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    /// Returns the PGN header
1060    ///
1061    /// # Returns
1062    /// The PGN header
1063    ///
1064    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    /// Returns the PGN moves
1085    ///
1086    /// # Returns
1087    /// The PGN moves
1088    ///
1089    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    /// Returns the PGN moves of a line
1126    ///
1127    /// # Arguments
1128    /// * `line` - The line
1129    /// * `move_number` - The move number
1130    /// * `secondary` - Whether the line is a subsequent line
1131    ///
1132    /// # Returns
1133    /// The PGN moves of a line
1134    ///
1135    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    /// Returns the next move
1209    ///
1210    /// # Returns
1211    /// The next move
1212    ///
1213    /// # Examples
1214    /// ```
1215    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
1216    /// use std::collections::HashMap;
1217    ///
1218    /// let mut pgn_tree = PGNTree::default();
1219    /// let mov1 = Move::new(
1220    ///     Piece::new(Color::Black, PieceType::Pawn),
1221    ///     Position::from_string("e2").unwrap(),
1222    ///     Position::from_string("e4").unwrap(),
1223    ///     MoveType::Normal {
1224    ///         capture: false,
1225    ///         promotion: None,
1226    ///     },
1227    ///     None,
1228    ///     None,
1229    ///     (false, false),
1230    ///     false,
1231    ///     false,
1232    /// ).unwrap();
1233    /// let mov2 = Move::new(
1234    ///     Piece::new(Color::White, PieceType::Pawn),
1235    ///     Position::from_string("e7").unwrap(),
1236    ///     Position::from_string("e5").unwrap(),
1237    ///     MoveType::Normal {
1238    ///         capture: false,
1239    ///         promotion: None,
1240    ///     },
1241    ///     None,
1242    ///     None,
1243    ///     (false, false),
1244    ///     false,
1245    ///     false,
1246    /// ).unwrap();
1247    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
1248    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
1249    ///
1250    /// assert_eq!(mov2, pgn_tree.get_move().unwrap());
1251    /// assert_eq!(mov1, pgn_tree.next_back().unwrap());
1252    /// assert_eq!(mov2, pgn_tree.next().unwrap());
1253    /// ```
1254    ///
1255    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    /// Returns the previous move
1262    ///
1263    /// # Returns
1264    /// The previous move
1265    ///
1266    /// # Examples
1267    /// ```
1268    /// use chess_lab::core::{PGNTree, Piece, Move, PieceType, MoveType, Color, Position, GameStatus};
1269    /// use std::collections::HashMap;
1270    ///
1271    /// let mut pgn_tree = PGNTree::default();
1272    /// let mov1 = Move::new(
1273    ///     Piece::new(Color::Black, PieceType::Pawn),
1274    ///     Position::from_string("e2").unwrap(),
1275    ///     Position::from_string("e4").unwrap(),
1276    ///     MoveType::Normal {
1277    ///         capture: false,
1278    ///         promotion: None,
1279    ///     },
1280    ///     None,
1281    ///     None,
1282    ///     (false, false),
1283    ///     false,
1284    ///     false,
1285    /// ).unwrap();
1286    /// let mov2 = Move::new(
1287    ///     Piece::new(Color::White, PieceType::Pawn),
1288    ///     Position::from_string("e7").unwrap(),
1289    ///     Position::from_string("e5").unwrap(),
1290    ///     MoveType::Normal {
1291    ///         capture: false,
1292    ///         promotion: None,
1293    ///     },
1294    ///     None,
1295    ///     None,
1296    ///     (false, false),
1297    ///     false,
1298    ///     false,
1299    /// ).unwrap();
1300    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
1301    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress, HashMap::new());
1302    ///
1303    /// assert_eq!(mov2, pgn_tree.get_move().unwrap());
1304    /// assert_eq!(mov1, pgn_tree.next_back().unwrap());
1305    /// ```
1306    ///
1307    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}