chess_lab/common/constants/
pgn.rs

1use std::{cell::RefCell, fmt::Display, rc::Rc};
2
3use super::{GameStatus, Position};
4
5/// A struct representing a PGN line or variation
6/// Its also a tree node that contains a list of child nodes, the parent node,
7/// the move number and the move itself
8///
9#[derive(Debug, Clone)]
10pub struct PgnLine<T: PartialEq + Clone + Display> {
11    pub lines: Vec<Rc<RefCell<PgnLine<T>>>>,
12    pub parent: Option<Rc<RefCell<PgnLine<T>>>>,
13    pub halfmove_clock: u32,
14    pub fullmove_number: u32,
15    pub en_passant: Option<Position>,
16    pub castling_rights: u8,
17    pub game_status: GameStatus,
18    pub mov: T,
19}
20
21impl<T: PartialEq + Clone + Display> PartialEq for PgnLine<T> {
22    /// Compares two PgnLine structs
23    /// Two PgnLine structs are equal if their moves are equal
24    ///
25    /// # Arguments
26    /// * `other`: The other PgnLine struct
27    ///
28    /// # Returns
29    /// A boolean indicating if the two PgnLine structs are equal
30    ///
31    fn eq(&self, other: &Self) -> bool {
32        self.mov == other.mov
33    }
34}
35
36/// A struct representing a PGN tree
37/// It contains the game metadata and a list of lines
38/// The current line is the move node that is currently being checked
39///
40#[derive(Debug, Clone)]
41pub struct PgnTree<T: PartialEq + Clone + Display> {
42    pub event: Option<String>,
43    pub site: Option<String>,
44    pub date: Option<String>,
45    pub round: Option<String>,
46    pub white: Option<String>,
47    pub black: Option<String>,
48    pub result: Option<String>,
49    pub variant: Option<String>,
50    pub white_elo: Option<u32>,
51    pub black_elo: Option<u32>,
52    pub time_control: Option<String>,
53    pub termination: Option<String>,
54    lines: Vec<Rc<RefCell<PgnLine<T>>>>,
55    current_line: Option<Rc<RefCell<PgnLine<T>>>>,
56}
57
58impl<T: PartialEq + Clone + Display> Default for PgnTree<T> {
59    /// Creates a new PgnTree with no metadata and an empty list of lines
60    ///
61    /// # Returns
62    /// A new PgnTree
63    ///
64    /// # Examples
65    /// ```
66    /// use chess_lab::constants::pgn::PgnTree;
67    /// use chess_lab::constants::Move;
68    ///
69    /// let tree: PgnTree<Move> = PgnTree::default();
70    /// ```
71    ///
72    fn default() -> PgnTree<T> {
73        PgnTree {
74            event: None,
75            site: None,
76            date: None,
77            round: None,
78            white: None,
79            black: None,
80            result: None,
81            variant: None,
82            white_elo: None,
83            black_elo: None,
84            time_control: None,
85            termination: None,
86            lines: Vec::new(),
87            current_line: None,
88        }
89    }
90}
91
92impl<T: PartialEq + Clone + Display> PgnTree<T> {
93    /// Creates a new PgnTree with the provided metadata and an empty list of lines
94    ///
95    /// # Arguments
96    /// * `event`: The event name
97    /// * `site`: The site name
98    /// * `date`: The date of the game
99    /// * `round`: The round number
100    /// * `white`: The white player name
101    /// * `black`: The black player name
102    /// * `result`: The result of the game
103    /// * `variant`: The variant of the game
104    /// * `white_elo`: The white player ELO
105    /// * `black_elo`: The black player ELO
106    /// * `time_control`: The time control of the game
107    ///
108    /// # Returns
109    /// A new PgnTree
110    ///
111    /// # Examples
112    /// ```
113    /// use chess_lab::constants::pgn::PgnTree;
114    /// use chess_lab::constants::Move;
115    ///
116    /// let tree: PgnTree<Move> = PgnTree::new(
117    ///    Some("Event".to_string()),
118    ///    Some("Site".to_string()),
119    ///    Some("Date".to_string()),
120    ///    Some("Round".to_string()),
121    ///    Some("White".to_string()),
122    ///    Some("Black".to_string()),
123    ///    Some("Result".to_string()),
124    ///    Some("Variant".to_string()),
125    ///    Some(1000),
126    ///    Some(1000),
127    ///    Some("Time Control".to_string()),
128    ///    Some("Termination".to_string()),
129    /// );
130    /// ```
131    ///
132    pub fn new(
133        event: Option<String>,
134        site: Option<String>,
135        date: Option<String>,
136        round: Option<String>,
137        white: Option<String>,
138        black: Option<String>,
139        result: Option<String>,
140        variant: Option<String>,
141        white_elo: Option<u32>,
142        black_elo: Option<u32>,
143        time_control: Option<String>,
144        termination: Option<String>,
145    ) -> PgnTree<T> {
146        PgnTree {
147            event,
148            site,
149            date,
150            round,
151            white,
152            black,
153            result,
154            variant,
155            white_elo,
156            black_elo,
157            time_control,
158            termination,
159            lines: Vec::new(),
160            current_line: None,
161        }
162    }
163
164    /// Adds a move to the current line
165    ///
166    /// # Arguments
167    /// * `mov`: The move to add
168    /// * `halfmove_clock`: The halfmove clock
169    /// * `fullmove_number`: The fullmove number
170    /// * `en_passant`: The en passant position
171    /// * `castling_rights`: The castling rights
172    ///
173    /// # Examples
174    /// ```
175    /// use chess_lab::constants::{pgn::PgnTree, Move, MoveType, PieceType, Color, Position, GameStatus};
176    /// use chess_lab::logic::Piece;
177    ///
178    /// let mut pgn_tree: PgnTree<Move> = PgnTree::default();
179    /// let mov = Move::new(
180    ///     Piece::new(Color::Black, PieceType::Pawn),
181    ///     Position::from_string("e2"),
182    ///     Position::from_string("e4"),
183    ///     MoveType::Normal {
184    ///         capture: false,
185    ///         promotion: None,
186    ///     },
187    ///     None,
188    ///     None,
189    ///     (false, false),
190    ///     false,
191    ///     false,
192    /// );
193    /// pgn_tree.add_move(mov.clone(), 0, 0, None, 0, GameStatus::InProgress);
194    ///
195    /// assert_eq!(mov, pgn_tree.get_move().unwrap());
196    /// ```
197    ///
198    pub fn add_move(
199        &mut self,
200        mov: T,
201        halfmove_clock: u32,
202        fullmove_number: u32,
203        en_passant: Option<Position>,
204        castling_rights: u8,
205        game_status: GameStatus,
206    ) {
207        if let Some(current_line) = &self.current_line {
208            let new_line = Rc::new(RefCell::new(PgnLine {
209                lines: Vec::new(),
210                parent: Some(Rc::clone(&current_line)),
211                halfmove_clock,
212                fullmove_number,
213                en_passant,
214                castling_rights,
215                game_status,
216                mov,
217            }));
218            if !current_line.as_ref().borrow_mut().lines.contains(&new_line) {
219                current_line
220                    .as_ref()
221                    .borrow_mut()
222                    .lines
223                    .push(Rc::clone(&new_line));
224            }
225            self.current_line = Some(new_line);
226        } else {
227            let new_line = Rc::new(RefCell::new(PgnLine {
228                lines: Vec::new(),
229                parent: None,
230                halfmove_clock,
231                fullmove_number,
232                en_passant,
233                castling_rights,
234                game_status,
235                mov,
236            }));
237            self.lines.push(Rc::clone(&new_line));
238            self.current_line = Some(new_line);
239        }
240    }
241
242    /// Removes the current line
243    ///
244    /// # Examples
245    /// ```
246    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
247    /// use chess_lab::logic::Piece;
248    ///
249    /// let mut tree = PgnTree::default();
250    ///
251    /// tree.add_move(Move::new(
252    ///     Piece::new(Color::Black, PieceType::Pawn),
253    ///     Position::from_string("e2"),
254    ///     Position::from_string("e4"),
255    ///     MoveType::Normal {
256    ///         capture: false,
257    ///         promotion: None,
258    ///     },
259    ///     None,
260    ///     None,
261    ///     (false, false),
262    ///     false,
263    ///     false,
264    /// ), 0, 0, None, 0, GameStatus::InProgress);
265    /// tree.rm_move();
266    /// ```
267    ///
268    pub fn rm_move(&mut self) {
269        if let None = &self.current_line {
270            return;
271        }
272
273        let current_line = self.current_line.take().unwrap();
274        let current_line_borrowed = current_line.borrow();
275
276        if current_line_borrowed.parent.is_none() {
277            return;
278        }
279
280        let parent = Rc::clone(&current_line_borrowed.parent.as_ref().unwrap());
281        let index = parent
282            .borrow()
283            .lines
284            .iter()
285            .position(|x| Rc::ptr_eq(x, &self.current_line.as_ref().unwrap()))
286            .unwrap();
287
288        parent.borrow_mut().lines.remove(index);
289
290        self.current_line = Some(parent);
291    }
292
293    /// Returns the current move
294    ///
295    /// # Returns
296    /// The current move
297    ///
298    /// # Examples
299    /// ```
300    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
301    /// use chess_lab::logic::Piece;
302    ///
303    /// let mut tree = PgnTree::default();
304    /// let mov = Move::new(
305    ///     Piece::new(Color::Black, PieceType::Pawn),
306    ///     Position::from_string("e2"),
307    ///     Position::from_string("e4"),
308    ///     MoveType::Normal {
309    ///         capture: false,
310    ///         promotion: None,
311    ///     },
312    ///     None,
313    ///     None,
314    ///     (false, false),
315    ///     false,
316    ///     false,
317    /// );
318    /// tree.add_move(mov.clone(), 0, 0, None, 0, GameStatus::InProgress);
319    /// assert_eq!(tree.get_move(), Some(mov));
320    /// ```
321    ///
322    pub fn get_move(&self) -> Option<T> {
323        Some(self.current_line.as_ref()?.borrow().mov.clone())
324    }
325
326    /// Returns the move info
327    ///
328    /// # Returns
329    /// A tuple containing the halfmove clock, the fullmove number, the en passant position and the castling rights
330    ///
331    /// # Examples
332    /// ```
333    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
334    /// use chess_lab::logic::Piece;
335    ///
336    /// let mut tree = PgnTree::default();
337    /// let mov = Move::new(
338    ///     Piece::new(Color::Black, PieceType::Pawn),
339    ///     Position::from_string("e2"),
340    ///     Position::from_string("e4"),
341    ///     MoveType::Normal {
342    ///         capture: false,
343    ///         promotion: None,
344    ///     },
345    ///     None,
346    ///     None,
347    ///     (false, false),
348    ///     false,
349    ///     false,
350    /// );
351    /// tree.add_move(mov.clone(), 0, 0, None, 0, GameStatus::InProgress);
352    ///
353    /// assert_eq!(tree.get_prev_move_info(), (0, 0, None, 0, GameStatus::InProgress));
354    /// ```
355    ///
356    pub fn get_prev_move_info(&self) -> (u32, u32, Option<Position>, u8, GameStatus) {
357        let current_line = self
358            .current_line
359            .as_ref()
360            .unwrap_or_else(|| {
361                panic!("No current line found. Please add a move before calling this method")
362            })
363            .borrow();
364        (
365            current_line.halfmove_clock,
366            current_line.fullmove_number,
367            current_line.en_passant,
368            current_line.castling_rights,
369            current_line.game_status,
370        )
371    }
372
373    /// Returns the next move
374    ///
375    /// # Returns
376    /// The next move
377    ///
378    /// # Examples
379    /// ```
380    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
381    /// use chess_lab::logic::Piece;
382    ///
383    /// let mut pgn_tree = PgnTree::default();
384    /// let mov1 = Move::new(
385    ///     Piece::new(Color::Black, PieceType::Pawn),
386    ///     Position::from_string("e2"),
387    ///     Position::from_string("e4"),
388    ///     MoveType::Normal {
389    ///         capture: false,
390    ///         promotion: None,
391    ///     },
392    ///     None,
393    ///     None,
394    ///     (false, false),
395    ///     false,
396    ///     false,
397    /// );
398    /// let mov2 = Move::new(
399    ///     Piece::new(Color::White, PieceType::Pawn),
400    ///     Position::from_string("e7"),
401    ///     Position::from_string("e5"),
402    ///     MoveType::Normal {
403    ///         capture: false,
404    ///         promotion: None,
405    ///     },
406    ///     None,
407    ///     None,
408    ///     (false, false),
409    ///     false,
410    ///     false,
411    /// );
412    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
413    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
414    ///
415    /// assert_eq!(mov2, pgn_tree.get_move().unwrap());
416    /// assert_eq!(mov1, pgn_tree.prev_move().unwrap());
417    /// assert_eq!(mov2, pgn_tree.next_move().unwrap());
418    /// ```
419    ///
420    pub fn next_move(&mut self) -> Option<T> {
421        self.next_move_variant(0)
422    }
423
424    /// Returns the next move variant
425    ///
426    /// # Arguments
427    /// * `variant`: The variant to get
428    ///
429    /// # Returns
430    /// The next move variant
431    ///
432    /// # Examples
433    /// ```
434    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
435    /// use chess_lab::logic::Piece;
436    ///
437    /// let mut pgn_tree = PgnTree::default();
438    /// let mov1 = Move::new(
439    ///     Piece::new(Color::White, PieceType::Pawn),
440    ///     Position::from_string("e2"),
441    ///     Position::from_string("e4"),
442    ///     MoveType::Normal {
443    ///         capture: false,
444    ///         promotion: None,
445    ///     },
446    ///     None,
447    ///     None,
448    ///     (false, false),
449    ///     false,
450    ///     false,
451    /// );
452    /// let mov2 = Move::new(
453    ///     Piece::new(Color::White, PieceType::Pawn),
454    ///     Position::from_string("d2"),
455    ///     Position::from_string("d4"),
456    ///     MoveType::Normal {
457    ///         capture: false,
458    ///         promotion: None,
459    ///     },
460    ///     None,
461    ///     None,
462    ///     (false, false),
463    ///     false,
464    ///     false,
465    /// );
466    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
467    /// pgn_tree.prev_move();
468    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
469    ///
470    /// pgn_tree.prev_move();
471    /// assert_eq!(mov1, pgn_tree.next_move().unwrap());
472    ///
473    /// pgn_tree.prev_move();
474    /// assert_eq!(mov2, pgn_tree.next_move_variant(1).unwrap());
475    /// ```
476    ///
477    pub fn next_move_variant(&mut self, variant: u32) -> Option<T> {
478        if let Some(current_line) = &self.current_line {
479            if current_line.borrow().lines.len() > variant as usize {
480                let next_line = Rc::clone(&current_line.borrow().lines[variant as usize]);
481                self.current_line = Some(Rc::clone(&next_line));
482                return Some(next_line.borrow().mov.clone());
483            }
484        } else {
485            if self.lines.len() > variant as usize {
486                let next_line = Rc::clone(&self.lines[variant as usize]);
487                self.current_line = Some(Rc::clone(&next_line));
488                return Some(next_line.borrow().mov.clone());
489            }
490        }
491        None
492    }
493
494    /// Returns all the next moves
495    ///
496    /// # Returns
497    /// All the next moves
498    ///
499    /// # Example
500    /// ```
501    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
502    /// use chess_lab::logic::Piece;
503    ///
504    /// let mut pgn_tree = PgnTree::default();
505    /// let mov1 = Move::new(
506    ///     Piece::new(Color::White, PieceType::Pawn),
507    ///     Position::from_string("e4"),
508    ///     Position::from_string("e2"),
509    ///     MoveType::Normal {
510    ///         capture: false,
511    ///         promotion: None,
512    ///     },
513    ///     None,
514    ///     None,
515    ///     (false, false),
516    ///     false,
517    ///     false,
518    /// );
519    ///
520    /// let mov2 = Move::new(
521    ///     Piece::new(Color::White, PieceType::Pawn),
522    ///     Position::from_string("d2"),
523    ///     Position::from_string("d4"),
524    ///     MoveType::Normal {
525    ///         capture: false,
526    ///         promotion: None,
527    ///     },
528    ///     None,
529    ///     None,
530    ///     (false, false),
531    ///     false,
532    ///     false,
533    /// );
534    ///
535    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
536    /// pgn_tree.prev_move();
537    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
538    /// pgn_tree.prev_move();
539    ///
540    /// assert_eq!(vec![mov1.clone(), mov2.clone()], pgn_tree.all_next_moves());
541    /// ```
542    ///
543    pub fn all_next_moves(&self) -> Vec<T> {
544        let mut moves = Vec::new();
545        if let Some(current_line) = &self.current_line {
546            for line in current_line.borrow().lines.iter() {
547                moves.push(line.borrow().mov.clone());
548            }
549        } else {
550            for line in self.lines.iter() {
551                moves.push(line.borrow().mov.clone());
552            }
553        }
554        moves
555    }
556
557    /// Returns the previous move
558    ///
559    /// # Returns
560    /// The previous move
561    ///
562    /// # Examples
563    /// ```
564    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
565    /// use chess_lab::logic::Piece;
566    ///
567    /// let mut pgn_tree = PgnTree::default();
568    /// let mov1 = Move::new(
569    ///     Piece::new(Color::Black, PieceType::Pawn),
570    ///     Position::from_string("e2"),
571    ///     Position::from_string("e4"),
572    ///     MoveType::Normal {
573    ///         capture: false,
574    ///         promotion: None,
575    ///     },
576    ///     None,
577    ///     None,
578    ///     (false, false),
579    ///     false,
580    ///     false,
581    /// );
582    /// let mov2 = Move::new(
583    ///     Piece::new(Color::White, PieceType::Pawn),
584    ///     Position::from_string("e7"),
585    ///     Position::from_string("e5"),
586    ///     MoveType::Normal {
587    ///         capture: false,
588    ///         promotion: None,
589    ///     },
590    ///     None,
591    ///     None,
592    ///     (false, false),
593    ///     false,
594    ///     false,
595    /// );
596    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
597    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
598    ///
599    /// assert_eq!(mov2, pgn_tree.get_move().unwrap());
600    /// assert_eq!(mov1, pgn_tree.prev_move().unwrap());
601    /// ```
602    ///
603    pub fn prev_move(&mut self) -> Option<T> {
604        if self.current_line.is_none() || self.current_line.as_ref()?.borrow().parent.is_none() {
605            self.current_line = None;
606            return None;
607        }
608
609        let parent = Rc::clone(
610            &self
611                .current_line
612                .as_ref()?
613                .borrow()
614                .parent
615                .as_ref()
616                .unwrap(),
617        );
618        self.current_line = Some(Rc::clone(&parent));
619        Some(self.current_line.as_ref()?.borrow().mov.clone())
620    }
621
622    pub fn pgn(&self) -> String {
623        let mut pgn = String::new();
624        pgn.push_str(&self.pgn_header());
625        pgn.push_str(&self.pgn_moves());
626        pgn
627    }
628
629    /// Returns the PGN header
630    ///
631    /// # Returns
632    /// The PGN header
633    ///
634    fn pgn_header(&self) -> String {
635        let mut header = String::new();
636        if let Some(event) = &self.event {
637            header.push_str(&format!("[Event \"{}\"]\n", event));
638        }
639        if let Some(site) = &self.site {
640            header.push_str(&format!("[Site \"{}\"]\n", site));
641        }
642        if let Some(date) = &self.date {
643            header.push_str(&format!("[Date \"{}\"]\n", date));
644        }
645        if let Some(round) = &self.round {
646            header.push_str(&format!("[Round \"{}\"]\n", round));
647        }
648        if let Some(white) = &self.white {
649            header.push_str(&format!("[White \"{}\"]\n", white));
650        }
651        if let Some(black) = &self.black {
652            header.push_str(&format!("[Black \"{}\"]\n", black));
653        }
654        if let Some(result) = &self.result {
655            header.push_str(&format!("[Result \"{}\"]\n", result));
656        }
657        if let Some(white_elo) = &self.white_elo {
658            header.push_str(&format!("[WhiteElo \"{}\"]\n", white_elo));
659        }
660        if let Some(black_elo) = &self.black_elo {
661            header.push_str(&format!("[BlackElo \"{}\"]\n", black_elo));
662        }
663        if let Some(time_control) = &self.time_control {
664            header.push_str(&format!("[TimeControl \"{}\"]\n", time_control));
665        }
666        if let Some(variant) = &self.variant {
667            header.push_str(&format!("[Variant \"{}\"]\n", variant));
668        }
669        header
670    }
671
672    fn pgn_moves(&self) -> String {
673        let mut pgn = String::new();
674
675        if self.lines.is_empty() {
676            return pgn;
677        }
678
679        let line = self.lines[0].as_ref().borrow();
680        pgn.push_str(&format!("1. {}", line.mov));
681
682        for next in self.lines.iter().skip(1) {
683            pgn.push_str(&format!(
684                " {}",
685                self.pgn_line_moves(Rc::clone(next), 1, true)
686            ));
687        }
688
689        pgn.push_str(&format!(
690            " {}",
691            self.pgn_line_moves(Rc::clone(&self.lines[0]), 2, false)
692        ));
693
694        pgn
695    }
696
697    fn pgn_line_moves(
698        &self,
699        line: Rc<RefCell<PgnLine<T>>>,
700        move_number: u32,
701        secondary: bool,
702    ) -> String {
703        let mut pgn = String::new();
704
705        let mut tmp_move_number = move_number;
706
707        if secondary {
708            if tmp_move_number % 2 == 0 {
709                pgn.push_str(&format!("{}... ", tmp_move_number / 2))
710            } else {
711                pgn.push_str(&format!("{}. ", tmp_move_number / 2 + 1));
712            };
713            pgn.push_str(&format!("{} ", line.as_ref().borrow().mov));
714
715            tmp_move_number += 1;
716        }
717
718        let mut stack = vec![line];
719
720        while let Some(current) = stack.pop() {
721            let line = current.as_ref().borrow();
722
723            if line.lines.is_empty() {
724                pgn.pop();
725                continue;
726            } else {
727                if tmp_move_number % 2 != 0 {
728                    pgn.push_str(&format!("{}. ", tmp_move_number / 2 + 1));
729                };
730                tmp_move_number += 1;
731
732                let next = Rc::clone(&line.lines[0]);
733                pgn.push_str(&format!("{} ", next.as_ref().borrow().mov));
734                stack.push(Rc::clone(&next));
735
736                if line.lines.len() != 1 {
737                    for next in line.lines.iter().skip(1) {
738                        pgn.push_str(&format!(
739                            "{} ",
740                            self.pgn_line_moves(Rc::clone(next), tmp_move_number - 1, true)
741                        ));
742                    }
743                }
744            }
745        }
746
747        if secondary {
748            format!("({})", pgn)
749        } else {
750            pgn
751        }
752    }
753}
754
755impl<T: PartialEq + Clone + Display> Iterator for PgnTree<T> {
756    type Item = T;
757
758    /// Returns the next move
759    ///
760    /// # Returns
761    /// The next move
762    ///
763    /// # Examples
764    /// ```
765    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
766    /// use chess_lab::logic::Piece;
767    ///
768    /// let mut pgn_tree = PgnTree::default();
769    /// let mov1 = Move::new(
770    ///     Piece::new(Color::Black, PieceType::Pawn),
771    ///     Position::from_string("e2"),
772    ///     Position::from_string("e4"),
773    ///     MoveType::Normal {
774    ///         capture: false,
775    ///         promotion: None,
776    ///     },
777    ///     None,
778    ///     None,
779    ///     (false, false),
780    ///     false,
781    ///     false,
782    /// );
783    /// let mov2 = Move::new(
784    ///     Piece::new(Color::White, PieceType::Pawn),
785    ///     Position::from_string("e7"),
786    ///     Position::from_string("e5"),
787    ///     MoveType::Normal {
788    ///         capture: false,
789    ///         promotion: None,
790    ///     },
791    ///     None,
792    ///     None,
793    ///     (false, false),
794    ///     false,
795    ///     false,
796    /// );
797    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
798    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
799    ///
800    /// assert_eq!(mov2, pgn_tree.get_move().unwrap());
801    /// assert_eq!(mov1, pgn_tree.next_back().unwrap());
802    /// assert_eq!(mov2, pgn_tree.next().unwrap());
803    /// ```
804    ///
805    fn next(&mut self) -> Option<Self::Item> {
806        self.next_move()
807    }
808}
809
810impl<T: PartialEq + Clone + Display> DoubleEndedIterator for PgnTree<T> {
811    /// Returns the previous move
812    ///
813    /// # Returns
814    /// The previous move
815    ///
816    /// # Examples
817    /// ```
818    /// use chess_lab::constants::{pgn::PgnTree, Move, PieceType, MoveType, Color, Position, GameStatus};
819    /// use chess_lab::logic::Piece;
820    ///
821    /// let mut pgn_tree = PgnTree::default();
822    /// let mov1 = Move::new(
823    ///     Piece::new(Color::Black, PieceType::Pawn),
824    ///     Position::from_string("e2"),
825    ///     Position::from_string("e4"),
826    ///     MoveType::Normal {
827    ///         capture: false,
828    ///         promotion: None,
829    ///     },
830    ///     None,
831    ///     None,
832    ///     (false, false),
833    ///     false,
834    ///     false,
835    /// );
836    /// let mov2 = Move::new(
837    ///     Piece::new(Color::White, PieceType::Pawn),
838    ///     Position::from_string("e7"),
839    ///     Position::from_string("e5"),
840    ///     MoveType::Normal {
841    ///         capture: false,
842    ///         promotion: None,
843    ///     },
844    ///     None,
845    ///     None,
846    ///     (false, false),
847    ///     false,
848    ///     false,
849    /// );
850    /// pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
851    /// pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
852    ///
853    /// assert_eq!(mov2, pgn_tree.get_move().unwrap());
854    /// assert_eq!(mov1, pgn_tree.next_back().unwrap());
855    /// ```
856    ///
857    fn next_back(&mut self) -> Option<Self::Item> {
858        self.prev_move()
859    }
860}
861
862#[cfg(test)]
863mod tests {
864    use crate::constants::pgn::PgnTree;
865    use crate::constants::{Color, GameStatus, Move, MoveType, PieceType, Position};
866    use crate::logic::Piece;
867
868    #[test]
869    fn test_add_move() {
870        let mut pgn_tree = PgnTree::default();
871        let mov = Move::new(
872            Piece::new(Color::Black, PieceType::Pawn),
873            Position::from_string("e2"),
874            Position::from_string("e4"),
875            MoveType::Normal {
876                capture: false,
877                promotion: None,
878            },
879            None,
880            None,
881            (false, false),
882            false,
883            false,
884        );
885        pgn_tree.add_move(mov.clone(), 0, 0, None, 0, GameStatus::InProgress);
886
887        assert_eq!(mov, pgn_tree.get_move().unwrap());
888    }
889
890    #[test]
891    fn test_rm_move() {
892        let mut pgn_tree = PgnTree::default();
893        let mov = Move::new(
894            Piece::new(Color::Black, PieceType::Pawn),
895            Position::from_string("e2"),
896            Position::from_string("e4"),
897            MoveType::Normal {
898                capture: false,
899                promotion: None,
900            },
901            None,
902            None,
903            (false, false),
904            false,
905            false,
906        );
907        pgn_tree.add_move(mov.clone(), 0, 0, None, 0, GameStatus::InProgress);
908        pgn_tree.rm_move();
909
910        assert!(pgn_tree.get_move().is_none());
911    }
912
913    #[test]
914    fn test_prev_move() {
915        let mut pgn_tree = PgnTree::default();
916        let mov1 = Move::new(
917            Piece::new(Color::Black, PieceType::Pawn),
918            Position::from_string("e2"),
919            Position::from_string("e4"),
920            MoveType::Normal {
921                capture: false,
922                promotion: None,
923            },
924            None,
925            None,
926            (false, false),
927            false,
928            false,
929        );
930        let mov2 = Move::new(
931            Piece::new(Color::White, PieceType::Pawn),
932            Position::from_string("e7"),
933            Position::from_string("e5"),
934            MoveType::Normal {
935                capture: false,
936                promotion: None,
937            },
938            None,
939            None,
940            (false, false),
941            false,
942            false,
943        );
944        pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
945        pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
946
947        assert_eq!(mov2, pgn_tree.get_move().unwrap());
948        assert_eq!(mov1, pgn_tree.prev_move().unwrap());
949        assert!(pgn_tree.prev_move().is_none());
950    }
951
952    #[test]
953    fn test_next_move() {
954        let mut pgn_tree = PgnTree::default();
955        let mov1 = Move::new(
956            Piece::new(Color::Black, PieceType::Pawn),
957            Position::from_string("e2"),
958            Position::from_string("e4"),
959            MoveType::Normal {
960                capture: false,
961                promotion: None,
962            },
963            None,
964            None,
965            (false, false),
966            false,
967            false,
968        );
969        let mov2 = Move::new(
970            Piece::new(Color::White, PieceType::Pawn),
971            Position::from_string("e7"),
972            Position::from_string("e5"),
973            MoveType::Normal {
974                capture: false,
975                promotion: None,
976            },
977            None,
978            None,
979            (false, false),
980            false,
981            false,
982        );
983        pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
984        pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
985
986        assert_eq!(mov2, pgn_tree.get_move().unwrap());
987        assert_eq!(mov1, pgn_tree.prev_move().unwrap());
988        assert_eq!(mov2, pgn_tree.next_move().unwrap());
989    }
990
991    #[test]
992    fn test_all_next_moves() {
993        let mut pgn_tree = PgnTree::default();
994        let mov1 = Move::new(
995            Piece::new(Color::White, PieceType::Pawn),
996            Position::from_string("e4"),
997            Position::from_string("e2"),
998            MoveType::Normal {
999                capture: false,
1000                promotion: None,
1001            },
1002            None,
1003            None,
1004            (false, false),
1005            false,
1006            false,
1007        );
1008
1009        let mov2 = Move::new(
1010            Piece::new(Color::White, PieceType::Pawn),
1011            Position::from_string("d2"),
1012            Position::from_string("d4"),
1013            MoveType::Normal {
1014                capture: false,
1015                promotion: None,
1016            },
1017            None,
1018            None,
1019            (false, false),
1020            false,
1021            false,
1022        );
1023
1024        pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
1025        pgn_tree.prev_move();
1026        pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
1027        pgn_tree.prev_move();
1028
1029        assert_eq!(vec![mov1.clone(), mov2.clone()], pgn_tree.all_next_moves());
1030    }
1031
1032    #[test]
1033    fn test_pgn_header() {
1034        let mut tree: PgnTree<Move> = PgnTree::default();
1035        tree.event = Some("Event".to_string());
1036        tree.site = Some("Site".to_string());
1037        tree.date = Some("Date".to_string());
1038        tree.round = Some("Round".to_string());
1039        tree.white = Some("White".to_string());
1040        tree.black = Some("Black".to_string());
1041        tree.result = Some("Result".to_string());
1042        tree.white_elo = Some(1000);
1043        tree.black_elo = Some(1000);
1044        tree.time_control = Some("TimeControl".to_string());
1045        tree.variant = Some("Variant".to_string());
1046
1047        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[WhiteElo \"1000\"]\n[BlackElo \"1000\"]\n[TimeControl \"TimeControl\"]\n[Variant \"Variant\"]\n");
1048    }
1049
1050    #[test]
1051    fn test_next_variant() {
1052        let mut pgn_tree = PgnTree::default();
1053        let mov1 = Move::new(
1054            Piece::new(Color::White, PieceType::Pawn),
1055            Position::from_string("e2"),
1056            Position::from_string("e4"),
1057            MoveType::Normal {
1058                capture: false,
1059                promotion: None,
1060            },
1061            None,
1062            None,
1063            (false, false),
1064            false,
1065            false,
1066        );
1067        let mov2 = Move::new(
1068            Piece::new(Color::White, PieceType::Pawn),
1069            Position::from_string("d2"),
1070            Position::from_string("d4"),
1071            MoveType::Normal {
1072                capture: false,
1073                promotion: None,
1074            },
1075            None,
1076            None,
1077            (false, false),
1078            false,
1079            false,
1080        );
1081        pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
1082        pgn_tree.prev_move();
1083        pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
1084
1085        pgn_tree.prev_move();
1086        assert_eq!(mov1, pgn_tree.next_move().unwrap());
1087
1088        pgn_tree.prev_move();
1089        assert_eq!(mov2, pgn_tree.next_move_variant(1).unwrap());
1090    }
1091
1092    #[test]
1093    fn test_pgn() {
1094        let mut pgn_tree = PgnTree::default();
1095        let mov1 = Move::new(
1096            Piece::new(Color::White, PieceType::Pawn),
1097            Position::from_string("e2"),
1098            Position::from_string("e4"),
1099            MoveType::Normal {
1100                capture: false,
1101                promotion: None,
1102            },
1103            None,
1104            None,
1105            (false, false),
1106            false,
1107            false,
1108        );
1109        let mov2 = Move::new(
1110            Piece::new(Color::Black, PieceType::Pawn),
1111            Position::from_string("e7"),
1112            Position::from_string("e5"),
1113            MoveType::Normal {
1114                capture: false,
1115                promotion: None,
1116            },
1117            None,
1118            None,
1119            (false, false),
1120            false,
1121            false,
1122        );
1123        pgn_tree.add_move(mov1.clone(), 0, 0, None, 0, GameStatus::InProgress);
1124        pgn_tree.add_move(mov2.clone(), 0, 0, None, 0, GameStatus::InProgress);
1125
1126        assert_eq!(pgn_tree.pgn(), "1. e4 e5");
1127    }
1128}