Skip to main content

chess_lab/logic/
game.rs

1use std::{collections::HashMap, fmt};
2
3use regex::Regex;
4
5use crate::{
6    core::{
7        piece_movement, CastleType, Color, DrawReason, GameStatus, Move, MoveType, PGNTree, Piece,
8        PieceType, Position, WinReason,
9    },
10    errors::{FenError, MoveError},
11    parsing::fen::parse_fen,
12};
13
14use super::board::Board;
15
16/// Represents a game of chess
17/// It contains the board, the turn, the halfmove clock, the fullmove number,
18/// the en passant square, the castling rights, the start position, the history,
19/// a flag to indicate if the king needs to be captured, the previous positions
20/// and the game status
21///
22/// # Example
23/// ```
24/// # use chess_lab::logic::Game;
25/// let game = Game::default();
26/// assert_eq!(game.to_string(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
27/// ```
28///
29#[derive(Debug, Clone)]
30pub struct Game {
31    capture_king: bool,
32    /// Board state for the current game.
33    pub board: Board,
34    /// True when it's white to move.
35    pub is_white_turn: bool,
36    /// Halfmove clock for the fifty-move rule.
37    pub halfmove_clock: u32,
38    /// Fullmove number (starts at 1).
39    pub fullmove_number: u32,
40    /// En passant target square, if any.
41    pub en_passant: Option<Position>,
42    /// Castling rights bitmask (KQkq).
43    pub castling_rights: u8,
44    /// Starting FEN used to initialize the game.
45    pub starting_fen: String,
46    /// PGN tree storing move history and variations.
47    pub history: PGNTree<Move>,
48    /// Reduced FEN positions for repetition tracking.
49    pub prev_positions: HashMap<String, u32>,
50    /// Current game status.
51    pub status: GameStatus,
52}
53
54impl Default for Game {
55    /// Creates a new game with the default values
56    ///
57    /// # Example
58    /// ```
59    /// # use chess_lab::logic::Game;
60    /// let game = Game::default();
61    /// assert_eq!(game.to_string(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
62    /// ```
63    ///
64    fn default() -> Game {
65        let fen = String::from("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
66        let mut map = HashMap::new();
67        map.insert(
68            String::from("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -"),
69            1,
70        );
71
72        Game {
73            board: Board::default(),
74            is_white_turn: true,
75            castling_rights: 0b1111,
76            en_passant: None,
77            halfmove_clock: 0,
78            fullmove_number: 1,
79            starting_fen: fen,
80            history: PGNTree::default(),
81            capture_king: false,
82            prev_positions: map,
83            status: GameStatus::InProgress,
84        }
85    }
86}
87
88impl Game {
89    /// Creates a new game
90    ///
91    /// # Arguments
92    /// * `fen`: A string slice that holds the FEN representation of the game
93    /// * `capture_king`: A boolean that indicates if the king needs to be captured
94    ///
95    /// # Returns
96    /// * `Ok(Game)`: A new game
97    /// * `Err(FenError)`: An error if the FEN is invalid
98    ///
99    /// # Example
100    /// ```
101    /// # use chess_lab::logic::Game;
102    /// let game = Game::new("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", true).unwrap();
103    /// assert_eq!(game.to_string(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
104    /// ```
105    ///
106    pub fn new(fen: &str, capture_king: bool) -> Result<Game, FenError> {
107        let mut game = Game::from_fen(fen)?;
108
109        game.capture_king = capture_king;
110
111        Ok(game)
112    }
113
114    /// Creates a new game from a FEN string
115    ///
116    /// # Arguments
117    /// * `fen`: A string slice that holds the FEN representation of the game
118    ///
119    /// # Returns
120    /// * `Ok(Game)`: A new game
121    /// * `Err(FenError)`: An error if the FEN is invalid
122    ///
123    /// # Example
124    /// ```
125    /// # use chess_lab::logic::Game;
126    /// let game = Game::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
127    /// assert_eq!(game.to_string(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
128    /// ```
129    ///
130    pub fn from_fen(fen: &str) -> Result<Game, FenError> {
131        let mut game = parse_fen(fen)?;
132        game.history.add_metadata("FEN", fen).unwrap();
133        game.history
134            .add_metadata("Variant", "From Position")
135            .unwrap();
136        Ok(game)
137    }
138
139    /// Returns the variant of the game
140    ///
141    /// # Returns
142    /// A string that holds the variant of the game
143    ///
144    /// # Example
145    ///
146    /// ```
147    /// # use chess_lab::logic::Game;
148    /// let game = Game::default();
149    /// assert_eq!(game.get_variant(), "Standard");
150    /// ```
151    ///
152    pub fn get_variant(&self) -> String {
153        match self.history.variant.clone() {
154            Some(variant) => variant,
155            None => String::from("Standard"),
156        }
157    }
158
159    /// Moves a piece on the board
160    ///
161    /// # Arguments
162    /// * `move_str`: A string slice that holds the move
163    ///
164    /// # Returns
165    /// * `Ok(GameStatus)`: The status of the game after the move
166    /// * `Err(MoveError)`: An error if the move is invalid
167    ///
168    /// # Example
169    /// ```
170    /// # use chess_lab::logic::Game;
171    /// let mut game = Game::default();
172    /// game.move_piece("e4").unwrap();
173    /// assert_eq!(game.to_string(), "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1");
174    /// ```
175    ///
176    pub fn move_piece(&mut self, move_str: &str) -> Result<GameStatus, MoveError> {
177        if self.status != GameStatus::InProgress {
178            return Ok(self.status);
179        }
180
181        let (piece_type, start_pos_info, end_pos, move_type) = self.parse_move(move_str)?;
182        let color = if self.is_white_turn {
183            Color::White
184        } else {
185            Color::Black
186        };
187
188        let ambiguity =
189            self.move_ambiguity(piece_type, color, start_pos_info, &end_pos, &move_type);
190
191        let positions = self.find_pieces(piece_type, color, start_pos_info, &end_pos, &move_type);
192
193        let start_pos = match positions.len() {
194            0 => return Err(MoveError::Illegal(move_str.to_string())),
195            1 => positions[0],
196            _ => {
197                return Err(MoveError::Ambiguous(move_str.to_string()));
198            }
199        };
200
201        let mut rook_start: Option<Position> = None;
202        let mut captured_piece: Option<PieceType> =
203            self.board.get_piece(&end_pos).map(|p| p.piece_type);
204
205        match self.board.move_piece(&start_pos, &end_pos) {
206            Ok(_) => {
207                match &move_type {
208                    MoveType::Castle { side } => {
209                        let rook_end = match side {
210                            CastleType::KingSide => Position {
211                                col: 5,
212                                row: start_pos.row,
213                            },
214                            CastleType::QueenSide => Position {
215                                col: 3,
216                                row: start_pos.row,
217                            },
218                        };
219
220                        let rooks = self.board.find(PieceType::Rook, color);
221
222                        for rook in rooks {
223                            match side {
224                                CastleType::KingSide => {
225                                    if rook.col > start_pos.col && rook.row == start_pos.row {
226                                        rook_start = Some(rook);
227                                        self.board.move_piece(&rook, &rook_end).unwrap();
228                                        break;
229                                    }
230                                }
231                                CastleType::QueenSide => {
232                                    if rook.col < start_pos.col && rook.row == start_pos.row {
233                                        rook_start = Some(rook);
234                                        self.board.move_piece(&rook, &rook_end).unwrap();
235                                        break;
236                                    }
237                                }
238                            }
239                        }
240                    }
241                    MoveType::EnPassant => {
242                        let captured_pos = Position {
243                            col: end_pos.col,
244                            row: start_pos.row,
245                        };
246                        captured_piece =
247                            Some(self.board.delete_piece(&captured_pos).unwrap().piece_type);
248                    }
249                    MoveType::Normal {
250                        capture: _,
251                        promotion: Some(piece_type),
252                    } => {
253                        self.board.delete_piece(&end_pos).unwrap();
254                        self.board
255                            .set_piece(Piece::new(color, piece_type.to_owned()), &end_pos)
256                            .unwrap();
257                    }
258                    _ => {}
259                }
260
261                self.update_rules(
262                    Move::new(
263                        Piece::new(color, piece_type),
264                        start_pos,
265                        end_pos,
266                        move_type,
267                        captured_piece,
268                        rook_start,
269                        ambiguity,
270                        false,
271                        false,
272                    )
273                    .map_err(|e| MoveError::Invalid(e.error))?,
274                );
275
276                Ok(self.status)
277            }
278            Err(_) => unreachable!(),
279        }
280    }
281
282    /// Returns the FEN representation of the game
283    ///
284    /// # Returns
285    /// A string that holds the FEN representation of the game
286    ///
287    /// # Example
288    /// ```
289    /// use chess_lab::logic::Game;
290    ///
291    /// let game = Game::default();
292    /// assert_eq!(game.fen(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
293    /// ```
294    ///
295    pub fn fen(&self) -> String {
296        let mut fen = String::new();
297        fen.push_str(&self.board.to_string());
298        fen.push(' ');
299        fen.push(if self.is_white_turn { 'w' } else { 'b' });
300        fen.push(' ');
301        if self.castling_rights == 0 {
302            fen.push('-');
303        } else {
304            if self.castling_rights & 0b1000 != 0 {
305                fen.push('K');
306            }
307            if self.castling_rights & 0b0100 != 0 {
308                fen.push('Q');
309            }
310            if self.castling_rights & 0b0010 != 0 {
311                fen.push('k');
312            }
313            if self.castling_rights & 0b0001 != 0 {
314                fen.push('q');
315            }
316        }
317        fen.push(' ');
318        fen.push_str(
319            &self
320                .en_passant
321                .as_ref()
322                .map_or(String::from("-"), |pos| pos.to_string()),
323        );
324        fen.push(' ');
325        fen.push_str(&self.halfmove_clock.to_string());
326        fen.push(' ');
327        fen.push_str(&self.fullmove_number.to_string());
328
329        fen
330    }
331
332    /// Undoes the last move
333    ///
334    /// # Example
335    /// ```
336    /// use chess_lab::logic::Game;
337    ///
338    /// let mut game = Game::default();
339    /// game.move_piece("e4").unwrap();
340    /// game.undo();
341    /// assert_eq!(game.fen(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
342    /// ```
343    ///
344    pub fn undo(&mut self) {
345        let mov = self.history.get_move();
346
347        if let None = mov {
348            return;
349        }
350
351        let mov = mov.unwrap();
352        let info = self.history.get_move_info().unwrap();
353
354        self.board.move_piece(&mov.to, &mov.from).unwrap();
355
356        match mov.move_type {
357            MoveType::Normal {
358                capture: true,
359                promotion,
360            } => {
361                self.board
362                    .set_piece(
363                        Piece::new(mov.piece.color.opposite(), mov.captured_piece.unwrap()),
364                        &mov.to,
365                    )
366                    .unwrap();
367                if let Some(_) = promotion {
368                    self.board.delete_piece(&mov.from).unwrap();
369                    self.board
370                        .set_piece(Piece::new(mov.piece.color, PieceType::Pawn), &mov.from)
371                        .unwrap();
372                }
373            }
374            MoveType::EnPassant => {
375                let captured_pos = Position {
376                    col: mov.to.col,
377                    row: mov.from.row,
378                };
379                self.board
380                    .set_piece(
381                        Piece::new(mov.piece.color.opposite(), mov.captured_piece.unwrap()),
382                        &captured_pos,
383                    )
384                    .unwrap();
385            }
386            MoveType::Castle { side } => {
387                let rook_from = mov.rook_from.unwrap();
388                let rook_to = match side {
389                    CastleType::KingSide => Position {
390                        col: 5,
391                        row: mov.to.row,
392                    },
393                    CastleType::QueenSide => Position {
394                        col: 3,
395                        row: mov.to.row,
396                    },
397                };
398                self.board.move_piece(&rook_to, &rook_from).unwrap();
399            }
400            _ => {}
401        }
402
403        self.is_white_turn = !self.is_white_turn;
404
405        self.halfmove_clock = info.halfmove_clock;
406        self.fullmove_number = info.fullmove_number;
407        self.en_passant = info.en_passant;
408        self.castling_rights = info.castling_rights;
409        self.status = info.game_status;
410        self.prev_positions = info.prev_positions;
411
412        self.history.prev_move();
413    }
414
415    /// Redoes the last undone move
416    ///
417    /// # Example
418    /// ```
419    /// use chess_lab::logic::Game;
420    ///
421    /// let mut game = Game::default();
422    /// game.move_piece("e4").unwrap();
423    /// game.undo();
424    /// game.redo();
425    ///
426    /// assert_eq!(game.fen(), "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1");
427    /// ```
428    ///
429    pub fn redo(&mut self) {
430        let mov = self.history.next_move();
431
432        if let None = mov {
433            return;
434        }
435
436        let mov = mov.unwrap();
437        self.history.prev_move();
438
439        self.move_piece(mov.to_string().as_str()).unwrap();
440    }
441
442    /// Redoes the nth variation of the last undone move
443    ///
444    /// # Arguments
445    /// * `n` - The number of the variation to redo
446    ///
447    /// # Example
448    /// ```
449    /// use chess_lab::logic::Game;
450    ///
451    /// let mut game = Game::default();
452    /// game.move_piece("e4").unwrap();
453    /// game.undo();
454    /// game.move_piece("d4").unwrap();
455    /// game.undo();
456    /// game.redo_nth(1);
457    /// assert_eq!(
458    ///     game.fen(),
459    ///     "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1"
460    /// );
461    /// ```
462    ///
463    pub fn redo_nth(&mut self, n: u32) {
464        let mov = self.history.next_move_variant(n);
465
466        if let None = mov {
467            return;
468        }
469
470        let mov = mov.unwrap();
471
472        self.move_piece(mov.to_string().as_str()).unwrap();
473    }
474
475    /// Returns the last move made in the game
476    ///
477    /// # Returns
478    /// An `Option<Move>` containing the last move made in the game, or `None` if no moves have been made
479    ///
480    /// # Example
481    /// ```
482    /// use chess_lab::logic::Game;
483    ///
484    /// let mut game = Game::default();
485    /// game.move_piece("e4").unwrap();
486    ///
487    /// let last_move = game.get_last_move().unwrap();
488    /// assert_eq!(last_move.to_string(), "e4");
489    /// ```
490    ///
491    pub fn get_last_move(&self) -> Option<Move> {
492        self.history.get_move()
493    }
494
495    /// Returns the piece at a given position
496    ///
497    /// # Arguments
498    /// * `pos`: The position to get the piece from
499    ///
500    /// # Returns
501    /// An `Option<Piece>` containing the piece at the given position, or `None` if the position is empty
502    ///
503    /// # Example
504    /// ```
505    /// use chess_lab::core::{PieceType, Position};
506    /// use chess_lab::logic::Game;
507    ///
508    /// let game = Game::default();
509    /// let piece = game.get_piece_at(Position::from_string("e2").unwrap()).unwrap();
510    /// assert_eq!(piece.piece_type, PieceType::Pawn);
511    /// ```
512    ///
513    pub fn get_piece_at(&self, pos: Position) -> Option<Piece> {
514        self.board.get_piece(&pos)
515    }
516
517    /// Undoes all moves until the starting position
518    ///
519    /// # Example
520    /// ```
521    /// use chess_lab::logic::Game;
522    ///
523    /// let mut game = Game::default();
524    /// game.move_piece("e4").unwrap();
525    /// game.move_piece("e5").unwrap();
526    /// game.start();
527    ///
528    /// assert_eq!(game.fen(), "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
529    /// ```
530    ///
531    pub fn start(&mut self) {
532        while self.history.has_prev_move() {
533            self.undo();
534        }
535    }
536
537    /// Redoes all moves until the last move
538    ///
539    /// # Example
540    /// ```
541    /// use chess_lab::logic::Game;
542    ///
543    /// let mut game = Game::default();
544    /// game.move_piece("e4").unwrap();
545    /// game.move_piece("e5").unwrap();
546    ///
547    /// game.start();
548    /// game.end();
549    ///
550    /// assert_eq!(game.fen(), "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2");
551    /// ```
552    ///
553    pub fn end(&mut self) {
554        while self.history.has_next_move() {
555            self.redo();
556        }
557    }
558
559    /// Returns the PGN of the game
560    ///
561    /// # Returns
562    /// A string containing the PGN of the game
563    ///
564    /// # Example
565    /// ```
566    /// use chess_lab::logic::Game;
567    ///
568    /// let mut game = Game::default();
569    /// game.move_piece("e4").unwrap();
570    /// game.move_piece("e5").unwrap();
571    /// println!("{}", game.pgn());
572    /// ```
573    ///
574    pub fn pgn(&self) -> String {
575        self.history.pgn()
576    }
577
578    /// Parse a move string and return the start and end positions
579    ///
580    /// # Arguments
581    /// * `move_str`: A string slice that holds the move to be parsed
582    ///
583    /// # Returns
584    /// * `Ok((PieceType, (Option<u8>, Option<u8>), Position, MoveType))`: A tuple containing the piece type, the start position, the end position and the move type
585    /// * `Err(MoveError)`: An error if the move is invalid
586    ///
587    pub fn parse_move(
588        &self,
589        move_str: &str,
590    ) -> Result<(PieceType, (Option<u8>, Option<u8>), Position, MoveType), MoveError> {
591        let mut move_str = move_str.to_string();
592        let re =
593            Regex::new(r"^([PNBRQK]?[a-h]?[1-8]?x?[a-h][1-8](=[NBRQ])?|O(-O){1,2})[+#]?").unwrap();
594        if !re.is_match(move_str.as_str()) || move_str.starts_with('x') {
595            return Err(MoveError::Invalid(move_str));
596        }
597
598        if move_str.chars().last().unwrap() == '+' || move_str.chars().last().unwrap() == '#' {
599            move_str.remove(move_str.len() - 1);
600        }
601
602        if move_str.starts_with('O') {
603            let castle_side;
604            let end_pos;
605            if move_str == "O-O" {
606                if (self.castling_rights & 0b1000 == 0 || !self.is_white_turn)
607                    && (self.castling_rights & 0b0010 == 0 || self.is_white_turn)
608                {
609                    return Err(MoveError::Invalid(move_str));
610                }
611                castle_side = CastleType::KingSide;
612                end_pos = if self.is_white_turn {
613                    Position::from_string("g1").unwrap()
614                } else {
615                    Position::from_string("g8").unwrap()
616                };
617            } else if move_str == "O-O-O" {
618                if (self.castling_rights & 0b0100 == 0 || !self.is_white_turn)
619                    && (self.castling_rights & 0b0001 == 0 || self.is_white_turn)
620                {
621                    return Err(MoveError::Invalid(move_str));
622                }
623                castle_side = CastleType::QueenSide;
624
625                end_pos = if self.is_white_turn {
626                    Position::from_string("c1").unwrap()
627                } else {
628                    Position::from_string("c8").unwrap()
629                };
630            } else {
631                unreachable!()
632            }
633            return Ok((
634                PieceType::King,
635                (None, None),
636                end_pos,
637                MoveType::Castle { side: castle_side },
638            ));
639        } else {
640            let start_col;
641            let start_row;
642            let end_pos;
643            let promotion;
644            let end_pos_index;
645            let piece = match move_str.chars().next().unwrap() {
646                'N' => PieceType::Knight,
647                'B' => PieceType::Bishop,
648                'R' => PieceType::Rook,
649                'Q' => PieceType::Queen,
650                'K' => PieceType::King,
651                'P' => PieceType::Pawn,
652                _ => {
653                    move_str = format!("P{}", move_str);
654                    PieceType::Pawn
655                }
656            };
657
658            if move_str.contains('=') {
659                if "NBRQK".contains(move_str.chars().next().unwrap()) {
660                    return Err(MoveError::Invalid(move_str));
661                }
662
663                promotion = Some(PieceType::from_char(move_str.chars().last().unwrap()).unwrap());
664                end_pos = Position::from_string(&move_str[move_str.len() - 4..move_str.len() - 2])
665                    .unwrap();
666                end_pos_index = move_str.len() - 4;
667
668                if end_pos.row != 0 && end_pos.row != 7 {
669                    return Err(MoveError::Invalid(move_str));
670                }
671            } else {
672                end_pos = Position::from_string(&move_str[move_str.len() - 2..]).unwrap();
673                end_pos_index = move_str.len() - 2;
674                promotion = None;
675            }
676
677            let capture = move_str.contains("x")
678                || self.board.is_ocupied(&end_pos)
679                || self.en_passant.map_or_else(|| false, |pos| pos == end_pos);
680
681            if end_pos_index > 1 {
682                if "abcdefgh".contains(move_str.chars().nth(1).unwrap()) {
683                    start_col = Some(move_str.chars().nth(1).unwrap() as u8 - 'a' as u8);
684                    if "12345678".contains(move_str.chars().nth(2).unwrap()) {
685                        start_row = Some(move_str.chars().nth(2).unwrap() as u8 - '1' as u8);
686                    } else {
687                        start_row = None;
688                    }
689                } else if "12345678".contains(move_str.chars().nth(1).unwrap()) {
690                    start_col = None;
691                    start_row = Some(move_str.chars().nth(1).unwrap() as u8 - '1' as u8);
692                } else {
693                    start_col = None;
694                    start_row = None;
695                }
696            } else {
697                start_col = None;
698                start_row = None;
699            }
700
701            if capture
702                && piece == PieceType::Pawn
703                && self.en_passant.map_or_else(|| false, |pos| pos == end_pos)
704            {
705                return Ok((
706                    PieceType::Pawn,
707                    (start_col, start_row),
708                    end_pos,
709                    MoveType::EnPassant,
710                ));
711            }
712
713            return Ok((
714                piece,
715                (start_col, start_row),
716                end_pos,
717                MoveType::Normal { capture, promotion },
718            ));
719        }
720    }
721
722    /// Check if a move is legal
723    ///
724    /// # Arguments
725    /// * `piece`: The piece being moved
726    /// * `start_pos`: The starting position of the piece
727    /// * `end_pos`: The ending position of the piece
728    /// * `move_type`: The type of move being made
729    ///
730    /// # Returns
731    /// Whether the move is legal
732    ///
733    pub fn is_legal(
734        &self,
735        piece: &Piece,
736        start_pos: &Position,
737        end_pos: &Position,
738        move_type: &MoveType,
739    ) -> bool {
740        if piece.piece_type != PieceType::Knight && piece.piece_type != PieceType::King {
741            match self.board.piece_between(start_pos, end_pos) {
742                Ok(true) | Err(_) => return false,
743                Ok(false) => (),
744            }
745        }
746        if move_type == &MoveType::EnPassant && piece.piece_type != PieceType::Pawn {
747            return false;
748        }
749
750        if let MoveType::Castle { side } = move_type {
751            return self.is_castle_legal(piece, start_pos, end_pos, side);
752        }
753        if !piece_movement(piece, start_pos, end_pos) {
754            return false;
755        }
756        if let MoveType::Normal {
757            capture: true,
758            promotion: _,
759        } = move_type
760        {
761            if !self.board.is_ocupied(end_pos)
762                || self.board.get_piece(end_pos).unwrap().color == piece.color
763                || (piece.piece_type == PieceType::Pawn && start_pos.col == end_pos.col)
764            {
765                return false;
766            }
767        }
768        if let MoveType::Normal {
769            capture: false,
770            promotion: _,
771        } = move_type
772        {
773            if self.board.is_ocupied(end_pos) {
774                return false;
775            }
776        }
777        if piece.piece_type == PieceType::Pawn {
778            if matches!(
779                move_type,
780                MoveType::Normal {
781                    capture: false,
782                    promotion: _
783                }
784            ) && (self.board.get_piece(end_pos).is_some() || start_pos.col != end_pos.col)
785            {
786                return false;
787            }
788
789            if let MoveType::Normal {
790                capture: _,
791                promotion: Some(_),
792            } = move_type
793            {
794                if (piece.color == Color::White && end_pos.row != 7)
795                    || (piece.color == Color::Black && end_pos.row != 0)
796                {
797                    return false;
798                }
799            }
800            if let MoveType::Normal {
801                capture: _,
802                promotion: None,
803            } = move_type
804            {
805                if (piece.color == Color::White && end_pos.row == 7)
806                    || (piece.color == Color::Black && end_pos.row == 0)
807                {
808                    return false;
809                }
810            }
811
812            if let MoveType::EnPassant = move_type {
813                if self.en_passant.map_or_else(|| false, |pos| pos != *end_pos)
814                    || ((piece.color == Color::White && end_pos.row != 5)
815                        || (piece.color == Color::Black && end_pos.row != 2))
816                {
817                    return false;
818                }
819            }
820        }
821
822        if self.capture_king {
823            return true;
824        }
825
826        let mut board = self.board.clone();
827        board.move_piece(start_pos, end_pos).unwrap();
828
829        let king = board.find(PieceType::King, piece.color)[0];
830
831        return !board.is_attacked(king, piece.color.opposite());
832    }
833
834    /// Returns all the legal moves for a piece at a given position
835    ///
836    /// # Arguments
837    /// * `pos`: The position of the piece to get the legal moves for
838    ///
839    /// # Returns
840    /// A vector containing all the legal moves for the piece at the given position
841    ///
842    /// # Example
843    /// ```
844    /// use chess_lab::core::Position;
845    /// use chess_lab::logic::Game;
846    ///
847    /// let game = Game::default();
848    /// let legal_moves = game.get_legal_moves(Position::from_string("e2").unwrap());
849    /// assert_eq!(legal_moves.len(), 2);
850    /// assert_eq!(legal_moves[0].to_string(), "e3");
851    /// assert_eq!(legal_moves[1].to_string(), "e4");
852    /// ```
853    ///
854    pub fn get_legal_moves(&self, pos: Position) -> Vec<Move> {
855        if self.board.get_piece(&pos).is_none() {
856            return vec![];
857        }
858
859        let piece = self.board.get_piece(&pos).unwrap();
860        if piece.color
861            != if self.is_white_turn {
862                Color::White
863            } else {
864                Color::Black
865            }
866        {
867            return vec![];
868        }
869
870        let mut moves = Vec::new();
871
872        for col in 0..8 {
873            for row in 0..8 {
874                let end_pos = Position { col, row };
875                let move_type = MoveType::Normal {
876                    capture: self
877                        .board
878                        .get_piece(&end_pos)
879                        .map_or_else(|| false, |p| p.color != piece.color),
880                    promotion: None,
881                };
882                let en_passant_move_type = MoveType::EnPassant;
883                let promotion_move_type = MoveType::Normal {
884                    capture: self
885                        .board
886                        .get_piece(&end_pos)
887                        .map_or_else(|| false, |p| p.color != piece.color),
888                    promotion: Some(PieceType::Queen),
889                };
890
891                if self.is_legal(&piece, &pos, &end_pos, &move_type) {
892                    moves.push(
893                        Move::new(
894                            piece.clone(),
895                            pos,
896                            end_pos,
897                            move_type,
898                            self.board.get_piece(&end_pos).map(|p| p.piece_type),
899                            None,
900                            (false, false),
901                            false,
902                            false,
903                        )
904                        .unwrap(),
905                    );
906                } else if self.is_legal(&piece, &pos, &end_pos, &en_passant_move_type) {
907                    moves.push(
908                        Move::new(
909                            piece.clone(),
910                            pos,
911                            end_pos,
912                            en_passant_move_type,
913                            self.board.get_piece(&end_pos).map(|p| p.piece_type),
914                            None,
915                            (false, false),
916                            false,
917                            false,
918                        )
919                        .unwrap(),
920                    );
921                } else if self.is_legal(&piece, &pos, &end_pos, &promotion_move_type) {
922                    moves.push(
923                        Move::new(
924                            piece.clone(),
925                            pos,
926                            end_pos,
927                            promotion_move_type,
928                            self.board.get_piece(&end_pos).map(|p| p.piece_type),
929                            None,
930                            (false, false),
931                            false,
932                            false,
933                        )
934                        .unwrap(),
935                    );
936                } else if piece.piece_type == PieceType::King {
937                    if self.is_legal(
938                        &piece,
939                        &pos,
940                        &end_pos,
941                        &MoveType::Castle {
942                            side: CastleType::KingSide,
943                        },
944                    ) {
945                        moves.push(
946                            Move::new(
947                                piece.clone(),
948                                pos,
949                                end_pos,
950                                MoveType::Castle {
951                                    side: CastleType::KingSide,
952                                },
953                                self.board.get_piece(&end_pos).map(|p| p.piece_type),
954                                self.get_castle_rook_pos(CastleType::KingSide),
955                                (false, false),
956                                false,
957                                false,
958                            )
959                            .unwrap(),
960                        );
961                    } else if self.is_legal(
962                        &piece,
963                        &pos,
964                        &end_pos,
965                        &MoveType::Castle {
966                            side: CastleType::QueenSide,
967                        },
968                    ) {
969                        moves.push(
970                            Move::new(
971                                piece.clone(),
972                                pos,
973                                end_pos,
974                                MoveType::Castle {
975                                    side: CastleType::QueenSide,
976                                },
977                                self.board.get_piece(&end_pos).map(|p| p.piece_type),
978                                self.get_castle_rook_pos(CastleType::QueenSide),
979                                (false, false),
980                                false,
981                                false,
982                            )
983                            .unwrap(),
984                        );
985                    }
986                }
987            }
988        }
989
990        moves
991    }
992
993    /// Returns the position of the rook involved in a castle move
994    ///
995    /// # Arguments
996    /// * `side`: The side of the castle move
997    ///
998    /// # Returns
999    /// An `Option<Position>` containing the position of the rook involved in the castle move
1000    ///
1001    /// # Example
1002    /// ```
1003    /// use chess_lab::logic::Game;
1004    /// use chess_lab::core::CastleType;
1005    ///
1006    /// let game = Game::default();
1007    /// let rook_pos = game.get_castle_rook_pos(CastleType::KingSide).unwrap();
1008    /// assert_eq!(rook_pos.to_string(), "h1");
1009    /// ```
1010    ///
1011    pub fn get_castle_rook_pos(&self, side: CastleType) -> Option<Position> {
1012        match side {
1013            CastleType::KingSide => {
1014                if self.is_white_turn && self.castling_rights & 0b1000 == 0 {
1015                    return None;
1016                }
1017                if !self.is_white_turn && self.castling_rights & 0b0010 == 0 {
1018                    return None;
1019                }
1020            }
1021            CastleType::QueenSide => {
1022                if self.is_white_turn && self.castling_rights & 0b0100 == 0 {
1023                    return None;
1024                }
1025                if !self.is_white_turn && self.castling_rights & 0b0001 == 0 {
1026                    return None;
1027                }
1028            }
1029        }
1030
1031        let color = if self.is_white_turn {
1032            Color::White
1033        } else {
1034            Color::Black
1035        };
1036
1037        let step = match side {
1038            CastleType::KingSide => 1,
1039            CastleType::QueenSide => -1,
1040        };
1041        let king_pos = self.board.find(PieceType::King, color)[0];
1042
1043        let mut col = king_pos.col as i8 + step;
1044        while col < 8 && col >= 0 {
1045            if let Some(piece) = self.board.get_piece(&Position {
1046                col: col as u8,
1047                row: king_pos.row,
1048            }) {
1049                if piece.piece_type == PieceType::Rook && piece.color == color {
1050                    return Some(Position {
1051                        col: col as u8,
1052                        row: king_pos.row,
1053                    });
1054                }
1055            }
1056            if step > 0 {
1057                col += step;
1058            } else {
1059                col -= -step;
1060            }
1061        }
1062        // This should never happen since the castle move should be illegal if there is no rook in the correct position
1063        unreachable!()
1064    }
1065
1066    /// Returns whether the king is in check
1067    ///
1068    /// # Returns
1069    /// Whether the king is in check
1070    ///
1071    /// # Example
1072    /// ```
1073    /// use chess_lab::logic::Game;
1074    ///
1075    /// let mut game = Game::default();
1076    ///
1077    /// game.move_piece("c4").unwrap();
1078    /// game.move_piece("d6").unwrap();
1079    /// game.move_piece("Qa4+").unwrap();
1080    ///
1081    /// assert!(game.check());
1082    /// ```
1083    ///
1084    pub fn check(&self) -> bool {
1085        if self.capture_king {
1086            return false;
1087        }
1088        let color = if self.is_white_turn {
1089            Color::White
1090        } else {
1091            Color::Black
1092        };
1093
1094        if self
1095            .board
1096            .is_attacked(self.board.find(PieceType::King, color)[0], color.opposite())
1097        {
1098            return true;
1099        }
1100        false
1101    }
1102
1103    /// Returns whether the king is in checkmate
1104    ///
1105    /// # Returns
1106    /// Whether the king is in checkmate
1107    ///
1108    /// # Example
1109    /// ```
1110    /// use chess_lab::logic::Game;
1111    ///
1112    /// let mut game = Game::default();
1113    ///
1114    /// game.move_piece("e4").unwrap();
1115    /// game.move_piece("e5").unwrap();
1116    /// game.move_piece("Qh5").unwrap();
1117    /// game.move_piece("Nc6").unwrap();
1118    /// game.move_piece("Bc4").unwrap();
1119    /// game.move_piece("Nf6").unwrap();
1120    /// game.move_piece("Qxf7#").unwrap();
1121    ///
1122    /// assert!(game.checkmate());
1123    /// ```
1124    ///
1125    pub fn checkmate(&self) -> bool {
1126        if self.capture_king {
1127            let color = if self.is_white_turn {
1128                Color::White
1129            } else {
1130                Color::Black
1131            };
1132            let kings = self.board.find(PieceType::King, color);
1133            return kings.is_empty();
1134        }
1135        if !self.check() {
1136            return false;
1137        }
1138
1139        !self.has_legal_moves()
1140    }
1141
1142    /// Returns whether the game is in stalemate
1143    ///
1144    /// # Returns
1145    /// Whether the game is in stalemate
1146    ///
1147    /// # Example
1148    /// ```
1149    /// use chess_lab::logic::Game;
1150    ///
1151    /// let game = Game::from_fen("8/8/8/8/8/4KQ2/8/4k3 b - - 0 1").unwrap();
1152    ///
1153    /// assert!(game.stalemate());
1154    /// ```
1155    ///
1156    pub fn stalemate(&self) -> bool {
1157        if self.capture_king {
1158            return false;
1159        }
1160        if self.check() {
1161            return false;
1162        }
1163
1164        !self.has_legal_moves()
1165    }
1166
1167    /// Returns whether the game is in insufficient material
1168    ///
1169    /// # Returns
1170    /// Whether the game is in insufficient material
1171    ///
1172    /// # Example
1173    /// ```
1174    /// use chess_lab::logic::Game;
1175    ///
1176    /// let game = Game::from_fen("8/8/8/8/8/4K3/8/4kB2 w - - 0 1").unwrap();
1177    /// assert!(game.insufficient_material());
1178    /// ```
1179    ///
1180    pub fn insufficient_material(&self) -> bool {
1181        let white_pieces = self.board.find_all(Color::White);
1182        let black_pieces = self.board.find_all(Color::Black);
1183
1184        if white_pieces.len() > 2 || black_pieces.len() > 2 {
1185            return false;
1186        }
1187
1188        let pieces = [white_pieces, black_pieces].concat();
1189        for pos in pieces {
1190            let piece = self.board.get_piece(&pos).unwrap();
1191            match piece.piece_type {
1192                PieceType::Pawn | PieceType::Rook | PieceType::Queen => return false,
1193                _ => {}
1194            }
1195        }
1196
1197        true
1198    }
1199
1200    ///
1201    /// # Arguments
1202    /// * `color`: The color of the player that resigned
1203    ///
1204    /// # Example
1205    /// ```
1206    /// use chess_lab::logic::{Game};
1207    /// use chess_lab::core::{Color, GameStatus, WinReason};
1208    ///
1209    /// let mut game = Game::default();
1210    /// game.resign(Color::White);
1211    ///
1212    /// assert_eq!(game.status, GameStatus::BlackWins(WinReason::Resignation));
1213    /// ```
1214    ///
1215    pub fn resign(&mut self, color: Color) {
1216        if self.status != GameStatus::InProgress {
1217            return;
1218        }
1219        self.status = if color == Color::White {
1220            GameStatus::BlackWins(WinReason::Resignation)
1221        } else {
1222            GameStatus::WhiteWins(WinReason::Resignation)
1223        };
1224    }
1225
1226    /// Ends the game and sets the winner to the opposite of the color that lost on time
1227    ///
1228    /// # Arguments
1229    /// * `color`: The color of the player that lost on time
1230    ///
1231    /// # Example
1232    /// ```
1233    /// use chess_lab::core::{Color, GameStatus, WinReason};
1234    /// use chess_lab::logic::Game;
1235    ///
1236    /// let mut game = Game::default();
1237    /// game.lost_on_time(Color::White);
1238    ///
1239    /// assert_eq!(game.status, GameStatus::BlackWins(WinReason::Time));
1240    /// ```
1241    ///
1242    pub fn lost_on_time(&mut self, color: Color) {
1243        if self.status != GameStatus::InProgress {
1244            return;
1245        }
1246        self.status = if color == Color::White {
1247            GameStatus::BlackWins(WinReason::Time)
1248        } else {
1249            GameStatus::WhiteWins(WinReason::Time)
1250        };
1251    }
1252
1253    /// Ends the game by a draw due to agreement
1254    ///
1255    /// # Example
1256    /// ```
1257    /// use chess_lab::core::{GameStatus, DrawReason};
1258    /// use chess_lab::logic::Game;
1259    ///
1260    /// let mut game = Game::default();
1261    /// game.draw_by_agreement();
1262    ///
1263    /// assert_eq!(game.status, GameStatus::Draw(DrawReason::Agreement));
1264    ///
1265    pub fn draw_by_agreement(&mut self) {
1266        if self.status != GameStatus::InProgress {
1267            return;
1268        }
1269        self.status = GameStatus::Draw(DrawReason::Agreement);
1270    }
1271
1272    /// Parses a move string
1273    ///
1274    /// # Arguments
1275    /// * `mov`: A move that holds the piece type, start and end position, the move type, the captured piece and the rook start position
1276    ///
1277    fn update_rules(&mut self, mut mov: Move) {
1278        self.is_white_turn = !self.is_white_turn;
1279
1280        mov.check = self.check();
1281        mov.checkmate = self.checkmate();
1282
1283        self.history.add_move(
1284            mov.clone(),
1285            self.halfmove_clock,
1286            self.fullmove_number,
1287            self.en_passant,
1288            self.castling_rights,
1289            self.status,
1290            self.prev_positions.clone(),
1291        );
1292
1293        if matches!(mov.move_type, MoveType::Castle { .. })
1294            || mov.piece.piece_type == PieceType::King
1295        {
1296            self.castling_rights &= match mov.piece.color {
1297                Color::White => 0b0011,
1298                Color::Black => 0b1100,
1299            };
1300        }
1301        if mov.piece.piece_type == PieceType::Rook {
1302            let king = self.board.find(PieceType::King, mov.piece.color)[0];
1303            match mov.piece.color {
1304                Color::White => {
1305                    if mov.from.col < king.col {
1306                        self.castling_rights &= 0b1011;
1307                    } else if mov.from.col > king.col {
1308                        self.castling_rights &= 0b0111;
1309                    }
1310                }
1311                Color::Black => {
1312                    if mov.from.col < king.col {
1313                        self.castling_rights &= 0b1110;
1314                    } else if mov.from.col > king.col {
1315                        self.castling_rights &= 0b1101;
1316                    }
1317                }
1318            }
1319        }
1320        if let Some(PieceType::Rook) = mov.captured_piece {
1321            let king = self.board.find(PieceType::King, mov.piece.color)[0];
1322            match mov.piece.color.opposite() {
1323                Color::White => {
1324                    if mov.to.col < king.col {
1325                        self.castling_rights &= 0b1011;
1326                    } else if mov.to.col > king.col {
1327                        self.castling_rights &= 0b0111;
1328                    }
1329                }
1330                Color::Black => {
1331                    if mov.to.col < king.col {
1332                        self.castling_rights &= 0b1110;
1333                    } else if mov.to.col > king.col {
1334                        self.castling_rights &= 0b1101;
1335                    }
1336                }
1337            }
1338        }
1339        if matches!(mov.move_type, MoveType::Normal { capture: true, .. })
1340            || mov.piece.piece_type == PieceType::Pawn
1341        {
1342            self.halfmove_clock = 0;
1343        } else {
1344            self.halfmove_clock += 1;
1345        }
1346        if mov.piece.piece_type == PieceType::Pawn && (&mov.from - &mov.to).1.abs() == 2 {
1347            let positions = self.board.find(PieceType::Pawn, mov.piece.color.opposite());
1348            let en_passant_pos = Position {
1349                col: mov.from.col,
1350                row: (mov.from.row + mov.to.row) / 2,
1351            };
1352
1353            let can_en_passant = positions.iter().any(|pos| {
1354                let piece = self.board.get_piece(&pos).unwrap();
1355                piece_movement(&piece, &pos, &en_passant_pos)
1356            });
1357
1358            if can_en_passant {
1359                self.en_passant = Some(en_passant_pos);
1360            } else {
1361                self.en_passant = None;
1362            }
1363        } else {
1364            self.en_passant = None;
1365        }
1366        if self.is_white_turn {
1367            self.fullmove_number += 1;
1368        }
1369
1370        let current_pos = self.get_fen_reduced();
1371        let posistions = *self.prev_positions.get(&current_pos).unwrap_or(&0);
1372
1373        self.prev_positions.insert(current_pos, posistions + 1);
1374
1375        if mov.checkmate {
1376            self.status = if self.is_white_turn {
1377                GameStatus::BlackWins(WinReason::Checkmate)
1378            } else {
1379                GameStatus::WhiteWins(WinReason::Checkmate)
1380            };
1381        } else if self.stalemate() {
1382            self.status = GameStatus::Draw(DrawReason::Stalemate);
1383        } else if self.insufficient_material() {
1384            self.status = GameStatus::Draw(DrawReason::InsufficientMaterial);
1385        } else if posistions == 2 {
1386            self.status = GameStatus::Draw(DrawReason::ThreefoldRepetition);
1387        } else if self.halfmove_clock >= 100 {
1388            self.status = GameStatus::Draw(DrawReason::FiftyMoveRule);
1389        } else {
1390            self.status = GameStatus::InProgress;
1391        };
1392
1393        self.history.game_over(self.status);
1394    }
1395
1396    /// Finds the position of the pieces that matches the given criteria to move
1397    ///
1398    /// # Arguments
1399    /// * `piece`: The type of piece to find
1400    /// * `color`: The color of the piece to find
1401    /// * `start_pos`: The criteria for the starting position of the piece to find
1402    /// * `end_pos`: The ending position of the piece to find
1403    /// * `move_type`: The type of move to find
1404    ///
1405    /// # Returns
1406    /// The position of the pieces on the board
1407    ///
1408    fn find_pieces(
1409        &self,
1410        piece: PieceType,
1411        color: Color,
1412        start_pos: (Option<u8>, Option<u8>),
1413        end_pos: &Position,
1414        move_type: &MoveType,
1415    ) -> Vec<Position> {
1416        let mut positions = match move_type {
1417            MoveType::Normal {
1418                capture: _,
1419                promotion: _,
1420            } => self.board.find(piece, color),
1421            MoveType::EnPassant => self.board.find(PieceType::Pawn, color),
1422            MoveType::Castle { side: _ } => self.board.find(PieceType::King, color),
1423        };
1424
1425        if start_pos != (None, None) {
1426            positions = positions
1427                .iter()
1428                .filter(|pos| -> bool {
1429                    let has_col = match start_pos {
1430                        (Some(col), _) => pos.col == col,
1431                        _ => true,
1432                    };
1433                    let has_row = match start_pos {
1434                        (_, Some(row)) => pos.row == row,
1435                        _ => true,
1436                    };
1437                    has_col && has_row
1438                })
1439                .cloned()
1440                .collect();
1441        }
1442
1443        let mut valid_positions = Vec::new();
1444        for pos in positions {
1445            if self.is_legal(
1446                &Piece {
1447                    color,
1448                    piece_type: piece,
1449                },
1450                &pos,
1451                &end_pos,
1452                &move_type,
1453            ) {
1454                valid_positions.push(pos);
1455            }
1456        }
1457
1458        return valid_positions;
1459    }
1460
1461    /// Checks if the move representation has to contain the column or row of the piece to move
1462    /// asuming that the move is legal
1463    ///
1464    /// # Arguments
1465    /// * `piece`: The type of piece to move
1466    /// * `color`: The color of the piece to move
1467    /// * `start_pos`: The starting position of the piece to move
1468    /// * `end_pos`: The ending position of the piece to move
1469    /// * `move_type`: The type of move to make
1470    ///
1471    /// # Returns
1472    /// A tuple containing two booleans:
1473    /// * The first boolean indicates if the column of the piece to move has to be included in the move representation
1474    /// * The second boolean indicates if the row of the piece to move has to be included in the move representation
1475    ///
1476    fn move_ambiguity(
1477        &self,
1478        piece: PieceType,
1479        color: Color,
1480        start_pos: (Option<u8>, Option<u8>),
1481        end_pos: &Position,
1482        move_type: &MoveType,
1483    ) -> (bool, bool) {
1484        match start_pos {
1485            (None, None) => (false, false),
1486            (Some(_), None) => {
1487                let positions = self.board.find(piece, color);
1488                let valid_positions = positions
1489                    .iter()
1490                    .filter(|pos| {
1491                        self.is_legal(
1492                            &Piece {
1493                                color,
1494                                piece_type: piece,
1495                            },
1496                            &pos,
1497                            &end_pos,
1498                            &move_type,
1499                        )
1500                    })
1501                    .count();
1502                (valid_positions > 1, false)
1503            }
1504            (None, Some(_)) => {
1505                let positions = self.board.find(piece, color);
1506                let valid_positions = positions
1507                    .iter()
1508                    .filter(|pos| {
1509                        self.is_legal(
1510                            &Piece {
1511                                color,
1512                                piece_type: piece,
1513                            },
1514                            &pos,
1515                            &end_pos,
1516                            &move_type,
1517                        )
1518                    })
1519                    .count();
1520                (false, valid_positions > 1)
1521            }
1522            (Some(col), Some(row)) => {
1523                let positions = self.board.find(piece, color);
1524                let col_ambiguity = positions
1525                    .iter()
1526                    .filter(|pos| pos.row == row)
1527                    .filter(|pos| {
1528                        self.is_legal(
1529                            &Piece {
1530                                color,
1531                                piece_type: piece,
1532                            },
1533                            &pos,
1534                            &end_pos,
1535                            &move_type,
1536                        )
1537                    })
1538                    .count()
1539                    > 1;
1540                let row_ambiguity = positions
1541                    .iter()
1542                    .filter(|pos| pos.col == col)
1543                    .filter(|pos| {
1544                        self.is_legal(
1545                            &Piece {
1546                                color,
1547                                piece_type: piece,
1548                            },
1549                            &pos,
1550                            &end_pos,
1551                            &move_type,
1552                        )
1553                    })
1554                    .count()
1555                    > 1;
1556                (col_ambiguity, row_ambiguity)
1557            }
1558        }
1559    }
1560
1561    /// Checks if castling is legal
1562    ///
1563    /// # Arguments
1564    /// * `piece`: The king piece to castle
1565    /// * `start_pos`: The starting position of the king piece
1566    /// * `end_pos`: The ending position of the king piece
1567    /// * `side`: The side to castle
1568    ///
1569    /// # Returns
1570    /// A boolean indicating if the castling is legal
1571    ///
1572    fn is_castle_legal(
1573        &self,
1574        piece: &Piece,
1575        start_pos: &Position,
1576        end_pos: &Position,
1577        side: &CastleType,
1578    ) -> bool {
1579        if piece.piece_type != PieceType::King || start_pos.row != end_pos.row {
1580            return false;
1581        }
1582
1583        match side {
1584            CastleType::KingSide => {
1585                if end_pos.col != 6 {
1586                    return false;
1587                }
1588                if piece.color == Color::White && self.castling_rights & 0b1000 == 0 {
1589                    return false;
1590                } else if piece.color == Color::Black && self.castling_rights & 0b0010 == 0 {
1591                    return false;
1592                }
1593            }
1594            CastleType::QueenSide => {
1595                if end_pos.col != 2 {
1596                    return false;
1597                }
1598                if piece.color == Color::White && self.castling_rights & 0b0100 == 0 {
1599                    return false;
1600                } else if piece.color == Color::Black && self.castling_rights & 0b0001 == 0 {
1601                    return false;
1602                }
1603            }
1604        }
1605        let col_range = if end_pos.col < start_pos.col {
1606            end_pos.col..=start_pos.col
1607        } else {
1608            start_pos.col..=end_pos.col
1609        };
1610        for col in col_range {
1611            let new_pos = Position::new(col, start_pos.row).unwrap();
1612            if (&new_pos != start_pos && self.board.is_ocupied(&new_pos))
1613                || self.board.is_attacked(
1614                    Position::new(col, start_pos.row).unwrap(),
1615                    piece.color.opposite(),
1616                )
1617            {
1618                return false;
1619            }
1620        }
1621        return true;
1622    }
1623
1624    /// Checks if there are legal moves for the current player
1625    ///
1626    /// # Returns
1627    /// A boolean indicating if there are legal moves for the current player
1628    ///
1629    fn has_legal_moves(&self) -> bool {
1630        let color = if self.is_white_turn {
1631            Color::White
1632        } else {
1633            Color::Black
1634        };
1635
1636        for piece_pos in self.board.find_all(color) {
1637            let piece = self.board.get_piece(&piece_pos).unwrap();
1638            for col in 0..8 {
1639                for row in 0..8 {
1640                    let end_pos = Position::new(col, row).unwrap();
1641
1642                    let mut board = self.board.clone();
1643                    if !board.can_move(&piece_pos, &end_pos).unwrap() {
1644                        continue;
1645                    }
1646
1647                    board.move_piece(&piece_pos, &end_pos).unwrap();
1648
1649                    let king = board.find(PieceType::King, piece.color)[0];
1650                    if !board.is_attacked(king, piece.color.opposite()) {
1651                        return true;
1652                    }
1653                }
1654            }
1655        }
1656        false
1657    }
1658
1659    /// Gives the FEN string of the position withouth the halfmove clock and fullmove number
1660    /// to be used as position identifier
1661    ///
1662    /// # Returns
1663    /// The FEN string of the position withouth the halfmove clock and fullmove number
1664    ///
1665    fn get_fen_reduced(&self) -> String {
1666        let fen = self.fen();
1667        let mut fen_parts: Vec<&str> = fen.split_whitespace().collect();
1668        fen_parts.pop();
1669        fen_parts.pop();
1670        fen_parts.join(" ")
1671    }
1672}
1673
1674impl fmt::Display for Game {
1675    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
1676        write!(f, "{}", self.fen())
1677    }
1678}
1679
1680#[cfg(test)]
1681mod tests {
1682
1683    use crate::core::{Color, MoveType, PieceType, Position};
1684
1685    use super::*;
1686
1687    #[test]
1688    fn test_fen() {
1689        let game = Game::default();
1690        assert_eq!(
1691            game.fen(),
1692            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
1693        );
1694    }
1695
1696    #[test]
1697    fn test_fmt() {
1698        let game = Game::default();
1699        assert_eq!(
1700            format!("{}", game),
1701            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
1702        );
1703    }
1704
1705    #[test]
1706    fn test_new_game() {
1707        let game = Game::new(
1708            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
1709            true,
1710        )
1711        .unwrap();
1712        assert_eq!(
1713            game.fen(),
1714            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
1715        );
1716    }
1717
1718    #[test]
1719    fn test_from_fen() {
1720        let game =
1721            Game::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
1722        assert_eq!(
1723            game.fen(),
1724            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
1725        );
1726    }
1727
1728    #[test]
1729    fn test_move_piece() {
1730        let mut game = Game::default();
1731        game.move_piece("e4").unwrap();
1732        assert_eq!(
1733            game.fen(),
1734            "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
1735        );
1736    }
1737
1738    #[test]
1739    fn test_redundant_move() {
1740        let mut game = Game::default();
1741        assert!(game.move_piece("Pe4").is_ok());
1742        game.move_piece("d5").unwrap();
1743        assert!(game.move_piece("e4d5").is_ok());
1744        game.undo();
1745        game.move_piece("e5").unwrap();
1746        game.move_piece("f5").unwrap();
1747        assert!(game.move_piece("Pe5f6").is_ok());
1748    }
1749
1750    #[test]
1751    fn test_invalid_move() {
1752        let mut game = Game::default();
1753        assert!(game.move_piece("abc").is_err());
1754    }
1755
1756    #[test]
1757    fn test_castle_kingside() {
1758        let mut game = Game::default();
1759        game.move_piece("e4").unwrap();
1760        game.move_piece("e5").unwrap();
1761        game.move_piece("Nf3").unwrap();
1762        game.move_piece("Nc6").unwrap();
1763        game.move_piece("Bb5").unwrap();
1764        game.move_piece("a6").unwrap();
1765        game.move_piece("O-O").unwrap();
1766        assert_eq!(
1767            game.fen(),
1768            "r1bqkbnr/1ppp1ppp/p1n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 1 4"
1769        );
1770    }
1771
1772    #[test]
1773    fn test_castle_queenside() {
1774        let mut game = Game::default();
1775        game.move_piece("d4").unwrap();
1776        game.move_piece("d5").unwrap();
1777        game.move_piece("Nc3").unwrap();
1778        game.move_piece("Nc6").unwrap();
1779        game.move_piece("Bf4").unwrap();
1780        game.move_piece("Bf5").unwrap();
1781        game.move_piece("Qd2").unwrap();
1782        game.move_piece("Qd7").unwrap();
1783        game.move_piece("O-O-O").unwrap();
1784        game.move_piece("O-O-O").unwrap();
1785        assert_eq!(
1786            game.fen(),
1787            "2kr1bnr/pppqpppp/2n5/3p1b2/3P1B2/2N5/PPPQPPPP/2KR1BNR w - - 8 6"
1788        );
1789    }
1790
1791    #[test]
1792    fn test_castle_through_check() {
1793        let mut game = Game::default();
1794        game.move_piece("e4").unwrap();
1795        game.move_piece("e5").unwrap();
1796        game.move_piece("Nf3").unwrap();
1797        game.move_piece("Nc6").unwrap();
1798        game.move_piece("Bb5").unwrap();
1799        game.move_piece("a6").unwrap();
1800        game.move_piece("Bxc6").unwrap();
1801        game.move_piece("dxc6").unwrap();
1802        game.move_piece("c3").unwrap();
1803        game.move_piece("Be6").unwrap();
1804        game.move_piece("d4").unwrap();
1805        game.move_piece("Bc4").unwrap();
1806        assert!(game.move_piece("O-O").is_err());
1807
1808        game = Game::default();
1809        game.move_piece("e4").unwrap();
1810        game.move_piece("c5").unwrap();
1811        game.move_piece("Nf3").unwrap();
1812        game.move_piece("d6").unwrap();
1813        game.move_piece("d4").unwrap();
1814        game.move_piece("cxd4").unwrap();
1815        game.move_piece("Nxd4").unwrap();
1816        game.move_piece("Nf6").unwrap();
1817        game.move_piece("Nc3").unwrap();
1818        game.move_piece("a6").unwrap();
1819        game.move_piece("Be3").unwrap();
1820        game.move_piece("e5").unwrap();
1821        game.move_piece("Nb3").unwrap();
1822        game.move_piece("Bg4").unwrap();
1823        game.move_piece("Qd2").unwrap();
1824        game.move_piece("Be7").unwrap();
1825        println!("{}", game);
1826        assert!(game.move_piece("O-O-O").is_err());
1827    }
1828
1829    #[test]
1830    fn test_castle_with_no_rights() {
1831        let mut game = Game::default();
1832        game.move_piece("e4").unwrap();
1833        game.move_piece("e5").unwrap();
1834        game.move_piece("Nf3").unwrap();
1835        game.move_piece("Nc6").unwrap();
1836        game.move_piece("Bb5").unwrap();
1837        game.move_piece("a6").unwrap();
1838        game.move_piece("Ke2").unwrap();
1839        game.move_piece("Nf6").unwrap();
1840        game.move_piece("Ke1").unwrap();
1841        game.move_piece("d6").unwrap();
1842        assert!(game.move_piece("O-O").is_err());
1843
1844        game = Game::default();
1845        game.move_piece("d4").unwrap();
1846        game.move_piece("d5").unwrap();
1847        game.move_piece("Nc3").unwrap();
1848        game.move_piece("Nc6").unwrap();
1849        game.move_piece("Bf4").unwrap();
1850        game.move_piece("Bf5").unwrap();
1851        game.move_piece("Qd2").unwrap();
1852        game.move_piece("Qd7").unwrap();
1853        game.move_piece("Kd1").unwrap();
1854        game.move_piece("Kd8").unwrap();
1855        game.move_piece("Ke1").unwrap();
1856        game.move_piece("Ke8").unwrap();
1857        assert!(game.move_piece("O-O-O").is_err());
1858    }
1859
1860    #[test]
1861    fn test_is_castle_legal_no_rights_kingside() {
1862        let mut game = Game::default();
1863        game.castling_rights = 0;
1864        let white_king = Piece::new(Color::White, PieceType::King);
1865        let black_king = Piece::new(Color::Black, PieceType::King);
1866
1867        let white_start = Position::from_string("e1").unwrap();
1868        let black_start = Position::from_string("e8").unwrap();
1869
1870        let white_end = Position::from_string("g1").unwrap();
1871        let black_end = Position::from_string("g8").unwrap();
1872
1873        assert!(!game.is_castle_legal(
1874            &white_king,
1875            &white_start,
1876            &white_end,
1877            &CastleType::KingSide
1878        ));
1879        assert!(!game.is_castle_legal(
1880            &black_king,
1881            &black_start,
1882            &black_end,
1883            &CastleType::KingSide
1884        ));
1885    }
1886
1887    #[test]
1888    fn test_is_castle_legal_no_rights_queenside() {
1889        let mut game = Game::default();
1890        game.castling_rights = 0;
1891        let white_king = Piece::new(Color::White, PieceType::King);
1892        let black_king = Piece::new(Color::Black, PieceType::King);
1893
1894        let white_start = Position::from_string("e1").unwrap();
1895        let black_start = Position::from_string("e8").unwrap();
1896
1897        let white_end = Position::from_string("c1").unwrap();
1898        let black_end = Position::from_string("c8").unwrap();
1899
1900        assert!(!game.is_castle_legal(
1901            &white_king,
1902            &white_start,
1903            &white_end,
1904            &CastleType::QueenSide
1905        ));
1906        assert!(!game.is_castle_legal(
1907            &black_king,
1908            &black_start,
1909            &black_end,
1910            &CastleType::QueenSide
1911        ));
1912    }
1913
1914    #[test]
1915    fn test_is_legal_invalid_promotion() {
1916        let game = Game::default();
1917
1918        assert!(!game.is_legal(
1919            &Piece::new(Color::White, PieceType::Pawn),
1920            &Position::from_string("e2").unwrap(),
1921            &Position::from_string("e4").unwrap(),
1922            &MoveType::Normal {
1923                capture: false,
1924                promotion: Some(PieceType::Queen)
1925            },
1926        ));
1927    }
1928
1929    #[test]
1930    fn test_is_legal_nonsense_move() {
1931        let game = Game::default();
1932        assert!(!game.is_legal(
1933            &Piece::new(Color::White, PieceType::Knight),
1934            &Position::from_string("e1").unwrap(),
1935            &Position::from_string("e1").unwrap(),
1936            &MoveType::Castle {
1937                side: CastleType::KingSide,
1938            },
1939        ))
1940    }
1941
1942    #[test]
1943    fn test_en_passant() {
1944        let mut game = Game::default();
1945        game.move_piece("e4").unwrap();
1946        game.move_piece("d5").unwrap();
1947        game.move_piece("e5").unwrap();
1948        game.move_piece("f5").unwrap();
1949        assert_eq!(
1950            game.fen(),
1951            "rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3"
1952        );
1953        game.move_piece("exf6").unwrap();
1954        assert_eq!(
1955            game.fen(),
1956            "rnbqkbnr/ppp1p1pp/5P2/3p4/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 3"
1957        );
1958    }
1959
1960    #[test]
1961    fn test_castle_rights() {
1962        let mut game = Game::default();
1963        game.move_piece("a3").unwrap();
1964        game.move_piece("a6").unwrap();
1965        game.move_piece("Ra2").unwrap();
1966        assert_eq!(
1967            game.fen(),
1968            "rnbqkbnr/1ppppppp/p7/8/8/P7/RPPPPPPP/1NBQKBNR b Kkq - 1 2"
1969        );
1970        game.move_piece("Ra7").unwrap();
1971        assert_eq!(
1972            game.fen(),
1973            "1nbqkbnr/rppppppp/p7/8/8/P7/RPPPPPPP/1NBQKBNR w Kk - 2 3"
1974        );
1975
1976        game.move_piece("h3").unwrap();
1977        game.move_piece("h6").unwrap();
1978        game.move_piece("Rh2").unwrap();
1979        assert_eq!(
1980            game.fen(),
1981            "1nbqkbnr/rpppppp1/p6p/8/8/P6P/RPPPPPPR/1NBQKBN1 b k - 1 4"
1982        );
1983        game.move_piece("Rh7").unwrap();
1984        assert_eq!(
1985            game.fen(),
1986            "1nbqkbn1/rppppppr/p6p/8/8/P6P/RPPPPPPR/1NBQKBN1 w - - 2 5"
1987        );
1988    }
1989
1990    #[test]
1991    fn test_promotion() {
1992        let mut game = Game::default();
1993        game.move_piece("e4").unwrap();
1994        game.move_piece("d5").unwrap();
1995        game.move_piece("exd5").unwrap();
1996        game.move_piece("c6").unwrap();
1997        game.move_piece("dxc6").unwrap();
1998        game.move_piece("a6").unwrap();
1999        game.move_piece("cxb7").unwrap();
2000        game.move_piece("a5").unwrap();
2001        game.move_piece("bxa8=Q").unwrap();
2002        assert_eq!(
2003            game.fen(),
2004            "Qnbqkbnr/4pppp/8/p7/8/8/PPPP1PPP/RNBQKBNR b KQk - 0 5"
2005        );
2006    }
2007
2008    #[test]
2009    fn test_promotion_invalid() {
2010        let mut game = Game::default();
2011        assert!(game.move_piece("e4=Q").is_err());
2012        assert!(game.move_piece("Ke4=Q").is_err());
2013    }
2014
2015    #[test]
2016    fn test_illegal_move() {
2017        let mut game = Game::default();
2018        assert!(game.move_piece("e5").is_err());
2019        game.move_piece("e4").unwrap();
2020        assert!(game.move_piece("e4").is_err());
2021        game.move_piece("e5").unwrap();
2022        assert!(game.move_piece("Nf6").is_err());
2023        assert!(game.move_piece("d4=Q").is_err());
2024    }
2025
2026    #[test]
2027    fn test_get_piece_at() {
2028        let game = Game::default();
2029        let piece = game
2030            .get_piece_at(Position::from_string("e2").unwrap())
2031            .unwrap();
2032        assert_eq!(piece.piece_type, PieceType::Pawn);
2033        assert_eq!(piece.color, Color::White);
2034
2035        assert!(game
2036            .get_piece_at(Position::from_string("e4").unwrap())
2037            .is_none());
2038    }
2039
2040    #[test]
2041    fn test_get_legal_moves() {
2042        let mut game = Game::default();
2043        let legal_moves = game.get_legal_moves(Position::from_string("e2").unwrap());
2044
2045        assert_eq!(legal_moves.len(), 2);
2046        assert!(legal_moves
2047            .iter()
2048            .any(|m| m.to == Position::from_string("e3").unwrap()));
2049        assert!(legal_moves
2050            .iter()
2051            .any(|m| m.to == Position::from_string("e4").unwrap()));
2052
2053        let legal_moves = game.get_legal_moves(Position::from_string("e1").unwrap());
2054        assert_eq!(legal_moves.len(), 0);
2055
2056        game.move_piece("e4").unwrap();
2057
2058        let legal_moves = game.get_legal_moves(Position::from_string("e7").unwrap());
2059
2060        assert_eq!(legal_moves.len(), 2);
2061        assert!(legal_moves
2062            .iter()
2063            .any(|m| m.to == Position::from_string("e6").unwrap()));
2064        assert!(legal_moves
2065            .iter()
2066            .any(|m| m.to == Position::from_string("e5").unwrap()));
2067    }
2068
2069    #[test]
2070    fn test_get_legal_moves_no_piece() {
2071        let game = Game::default();
2072        let legal_moves = game.get_legal_moves(Position::from_string("e4").unwrap());
2073        assert_eq!(legal_moves.len(), 0);
2074    }
2075
2076    #[test]
2077    fn test_get_legal_moves_oposite_color() {
2078        let game = Game::default();
2079        let legal_moves = game.get_legal_moves(Position::from_string("e7").unwrap());
2080        assert_eq!(legal_moves.len(), 0);
2081    }
2082
2083    #[test]
2084    fn test_get_legal_moves_castle() {
2085        let mut game = Game::default();
2086        game.move_piece("e4").unwrap();
2087        game.move_piece("e5").unwrap();
2088        game.move_piece("Nf3").unwrap();
2089        game.move_piece("Nc6").unwrap();
2090        game.move_piece("Bb5").unwrap();
2091        game.move_piece("a6").unwrap();
2092
2093        let legal_moves = game.get_legal_moves(Position::from_string("e1").unwrap());
2094        assert_eq!(legal_moves.len(), 3);
2095        assert!(legal_moves.iter().any(|mov| matches!(
2096            mov.move_type,
2097            MoveType::Castle {
2098                side: CastleType::KingSide
2099            }
2100        )));
2101        assert!(legal_moves
2102            .iter()
2103            .any(|mov| matches!(mov.rook_from, Some(Position { col: 7, row: 0 }))));
2104
2105        let mut game = Game::default();
2106        game.move_piece("d4").unwrap();
2107        game.move_piece("d5").unwrap();
2108        game.move_piece("Nc3").unwrap();
2109        game.move_piece("Nc6").unwrap();
2110        game.move_piece("Bf4").unwrap();
2111        game.move_piece("Bf5").unwrap();
2112        game.move_piece("Qd2").unwrap();
2113        game.move_piece("Qd7").unwrap();
2114
2115        let legal_moves = game.get_legal_moves(Position::from_string("e1").unwrap());
2116        assert_eq!(legal_moves.len(), 2);
2117        assert!(legal_moves.iter().any(|mov| matches!(
2118            mov.move_type,
2119            MoveType::Castle {
2120                side: CastleType::QueenSide
2121            }
2122        )));
2123        assert!(legal_moves
2124            .iter()
2125            .any(|mov| matches!(mov.rook_from, Some(Position { col: 0, row: 0 }))));
2126    }
2127
2128    #[test]
2129    fn test_get_legal_moves_en_passant() {
2130        let mut game = Game::default();
2131        game.move_piece("e4").unwrap();
2132        game.move_piece("d5").unwrap();
2133        game.move_piece("e5").unwrap();
2134        game.move_piece("f5").unwrap();
2135
2136        let legal_moves = game.get_legal_moves(Position::from_string("e5").unwrap());
2137        assert_eq!(legal_moves.len(), 2);
2138        assert!(legal_moves
2139            .iter()
2140            .any(|m| m.to == Position::from_string("e6").unwrap()));
2141        assert!(legal_moves
2142            .iter()
2143            .any(|m| m.to == Position::from_string("f6").unwrap()));
2144    }
2145
2146    #[test]
2147    fn test_get_legal_moves_promotion() {
2148        let mut game = Game::default();
2149        game.move_piece("e4").unwrap();
2150        game.move_piece("d5").unwrap();
2151        game.move_piece("exd5").unwrap();
2152        game.move_piece("c6").unwrap();
2153        game.move_piece("dxc6").unwrap();
2154        game.move_piece("a6").unwrap();
2155        game.move_piece("cxb7").unwrap();
2156        game.move_piece("a5").unwrap();
2157
2158        let legal_moves = game.get_legal_moves(Position::from_string("b7").unwrap());
2159        assert_eq!(legal_moves.len(), 2);
2160        assert_eq!(
2161            legal_moves.iter().map(|v| v.to).collect::<Vec<Position>>(),
2162            vec![
2163                Position::from_string("a8").unwrap(),
2164                Position::from_string("c8").unwrap()
2165            ]
2166        );
2167        assert!(matches!(
2168            legal_moves[0].move_type,
2169            MoveType::Normal {
2170                capture: true,
2171                promotion: Some(PieceType::Queen) // Asume that the promotion is always to queen for simplicity
2172            }
2173        ));
2174    }
2175
2176    #[test]
2177    fn test_ambiguity() {
2178        let mut game = Game::default();
2179        game.move_piece("Nf3").unwrap();
2180        game.move_piece("Nc6").unwrap();
2181        game.move_piece("Na3").unwrap();
2182        game.move_piece("Nb8").unwrap();
2183        game.move_piece("Nc4").unwrap();
2184        game.move_piece("Nc6").unwrap();
2185
2186        assert!(game.move_piece("Ne5").is_err());
2187    }
2188
2189    #[test]
2190    fn test_undo() {
2191        let mut game = Game::default();
2192        game.move_piece("e4").unwrap();
2193        game.undo();
2194        assert_eq!(
2195            game.fen(),
2196            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
2197        );
2198    }
2199
2200    #[test]
2201    fn test_undo_no_moves() {
2202        let mut game = Game::default();
2203        game.undo();
2204        assert_eq!(
2205            game.fen(),
2206            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
2207        );
2208    }
2209
2210    #[test]
2211    fn test_undo_castle_kingside() {
2212        let mut game = Game::default();
2213        game.move_piece("e4").unwrap();
2214        game.move_piece("e5").unwrap();
2215        game.move_piece("Nf3").unwrap();
2216        game.move_piece("Nc6").unwrap();
2217        game.move_piece("Bb5").unwrap();
2218        game.move_piece("a6").unwrap();
2219        game.move_piece("O-O").unwrap();
2220        game.undo();
2221        assert_eq!(
2222            game.fen(),
2223            "r1bqkbnr/1ppp1ppp/p1n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 4"
2224        );
2225    }
2226
2227    #[test]
2228    fn test_undo_castle_queenside() {
2229        let mut game = Game::default();
2230        game.move_piece("d4").unwrap();
2231        game.move_piece("d5").unwrap();
2232        game.move_piece("Nc3").unwrap();
2233        game.move_piece("Nc6").unwrap();
2234        game.move_piece("Bf4").unwrap();
2235        game.move_piece("Bf5").unwrap();
2236        game.move_piece("Qd2").unwrap();
2237        game.move_piece("Qd7").unwrap();
2238        game.move_piece("O-O-O").unwrap();
2239        game.undo();
2240        assert_eq!(
2241            game.fen(),
2242            "r3kbnr/pppqpppp/2n5/3p1b2/3P1B2/2N5/PPPQPPPP/R3KBNR w KQkq - 6 5"
2243        );
2244    }
2245
2246    #[test]
2247    fn test_undo_en_passant() {
2248        let mut game = Game::default();
2249        game.move_piece("e4").unwrap();
2250        game.move_piece("d5").unwrap();
2251        game.move_piece("e5").unwrap();
2252        game.move_piece("f5").unwrap();
2253        game.move_piece("exf6").unwrap();
2254        game.undo();
2255        assert_eq!(
2256            game.fen(),
2257            "rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3"
2258        );
2259    }
2260
2261    #[test]
2262    fn test_undo_promotion() {
2263        let mut game = Game::default();
2264        game.move_piece("e4").unwrap();
2265        game.move_piece("d5").unwrap();
2266        game.move_piece("exd5").unwrap();
2267        game.move_piece("c6").unwrap();
2268        game.move_piece("dxc6").unwrap();
2269        game.move_piece("a6").unwrap();
2270        game.move_piece("cxb7").unwrap();
2271        game.move_piece("a5").unwrap();
2272        game.move_piece("bxa8=Q").unwrap();
2273        game.undo();
2274        assert_eq!(
2275            game.fen(),
2276            "rnbqkbnr/1P2pppp/8/p7/8/8/PPPP1PPP/RNBQKBNR w KQkq - 0 5"
2277        );
2278    }
2279
2280    #[test]
2281    fn test_undo_castle_rights() {
2282        let mut game = Game::default();
2283        game.move_piece("a3").unwrap();
2284        game.move_piece("a6").unwrap();
2285        game.move_piece("Ra2").unwrap();
2286        game.move_piece("Ra7").unwrap();
2287        game.move_piece("h3").unwrap();
2288        game.move_piece("h6").unwrap();
2289        game.move_piece("Rh2").unwrap();
2290        game.move_piece("Rh7").unwrap();
2291        game.undo();
2292        assert_eq!(
2293            game.fen(),
2294            "1nbqkbnr/rpppppp1/p6p/8/8/P6P/RPPPPPPR/1NBQKBN1 b k - 1 4"
2295        );
2296    }
2297
2298    #[test]
2299    fn test_redo() {
2300        let mut game = Game::default();
2301        game.move_piece("e4").unwrap();
2302        game.undo();
2303        game.redo();
2304        assert_eq!(
2305            game.fen(),
2306            "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
2307        );
2308        game.undo();
2309    }
2310
2311    #[test]
2312    fn test_redo_no_moves() {
2313        let mut game = Game::default();
2314        game.redo();
2315        assert_eq!(
2316            game.fen(),
2317            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
2318        );
2319    }
2320
2321    #[test]
2322    fn test_redo_castle() {
2323        let mut game = Game::default();
2324        game.move_piece("e4").unwrap();
2325        game.move_piece("e5").unwrap();
2326        game.move_piece("Nf3").unwrap();
2327        game.move_piece("Nc6").unwrap();
2328        game.move_piece("Bb5").unwrap();
2329        game.move_piece("a6").unwrap();
2330        game.move_piece("O-O").unwrap();
2331        game.undo();
2332        game.redo();
2333        assert_eq!(
2334            game.fen(),
2335            "r1bqkbnr/1ppp1ppp/p1n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 1 4"
2336        );
2337    }
2338
2339    #[test]
2340    fn test_redo_en_passant() {
2341        let mut game = Game::default();
2342        game.move_piece("e4").unwrap();
2343        game.move_piece("d5").unwrap();
2344        game.move_piece("e5").unwrap();
2345        game.move_piece("f5").unwrap();
2346        game.move_piece("exf6").unwrap();
2347        game.undo();
2348        game.redo();
2349        assert_eq!(
2350            game.fen(),
2351            "rnbqkbnr/ppp1p1pp/5P2/3p4/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 3"
2352        );
2353    }
2354
2355    #[test]
2356    fn test_redo_promotion() {
2357        let mut game = Game::default();
2358        game.move_piece("e4").unwrap();
2359        game.move_piece("d5").unwrap();
2360        game.move_piece("exd5").unwrap();
2361        game.move_piece("c6").unwrap();
2362        game.move_piece("dxc6").unwrap();
2363        game.move_piece("a6").unwrap();
2364        game.move_piece("cxb7").unwrap();
2365        game.move_piece("a5").unwrap();
2366        game.move_piece("bxa8=Q").unwrap();
2367        game.undo();
2368        game.redo();
2369        assert_eq!(
2370            game.fen(),
2371            "Qnbqkbnr/4pppp/8/p7/8/8/PPPP1PPP/RNBQKBNR b KQk - 0 5"
2372        );
2373    }
2374
2375    #[test]
2376    fn test_redo_castle_rights() {
2377        let mut game = Game::default();
2378        game.move_piece("a3").unwrap();
2379        game.move_piece("a6").unwrap();
2380        game.move_piece("Ra2").unwrap();
2381        game.move_piece("Ra7").unwrap();
2382        game.move_piece("h3").unwrap();
2383        game.move_piece("h6").unwrap();
2384        game.move_piece("Rh2").unwrap();
2385        game.move_piece("Rh7").unwrap();
2386        game.undo();
2387        game.redo();
2388        assert_eq!(
2389            game.fen(),
2390            "1nbqkbn1/rppppppr/p6p/8/8/P6P/RPPPPPPR/1NBQKBN1 w - - 2 5"
2391        );
2392    }
2393
2394    #[test]
2395    fn test_redo_principal_line() {
2396        let mut game = Game::default();
2397        game.move_piece("e4").unwrap();
2398        game.undo();
2399        game.move_piece("d4").unwrap();
2400        game.undo();
2401
2402        assert_eq!(game.history.all_next_moves().len(), 2);
2403
2404        game.redo();
2405        assert_eq!(
2406            game.fen(),
2407            "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
2408        );
2409    }
2410
2411    #[test]
2412    fn test_multiple_redos() {
2413        let mut game = Game::default();
2414        game.move_piece("e4").unwrap();
2415        game.move_piece("e5").unwrap();
2416        game.move_piece("Nf3").unwrap();
2417        game.move_piece("Nc6").unwrap();
2418        game.move_piece("Bb5").unwrap();
2419        game.move_piece("a6").unwrap();
2420        game.move_piece("O-O").unwrap();
2421
2422        game.start();
2423
2424        game.redo();
2425        game.redo();
2426        game.redo();
2427        game.redo();
2428        game.redo();
2429        game.redo();
2430        game.redo();
2431
2432        assert_eq!(
2433            game.fen(),
2434            "r1bqkbnr/1ppp1ppp/p1n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 1 4"
2435        );
2436    }
2437
2438    #[test]
2439    fn test_auto_redo() {
2440        let mut game = Game::default();
2441        game.move_piece("e4").unwrap();
2442        game.move_piece("e5").unwrap();
2443        game.undo();
2444        game.undo();
2445        game.move_piece("e4").unwrap();
2446        game.redo();
2447
2448        assert_eq!(
2449            game.fen(),
2450            "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2"
2451        );
2452    }
2453
2454    #[test]
2455    fn test_play_other_move() {
2456        let mut game = Game::default();
2457        game.move_piece("e4").unwrap();
2458        game.undo();
2459        game.move_piece("d4").unwrap();
2460
2461        assert_eq!(
2462            game.fen(),
2463            "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1"
2464        );
2465
2466        let mut game = Game::default();
2467        game.move_piece("e4").unwrap();
2468        game.move_piece("e5").unwrap();
2469        game.move_piece("Nf3").unwrap();
2470        game.undo();
2471        game.move_piece("Nc3").unwrap();
2472
2473        assert_eq!(
2474            game.fen(),
2475            "rnbqkbnr/pppp1ppp/8/4p3/4P3/2N5/PPPP1PPP/R1BQKBNR b KQkq - 1 2"
2476        );
2477    }
2478
2479    #[test]
2480    fn test_redo_nth() {
2481        let mut game = Game::default();
2482        game.move_piece("e4").unwrap();
2483        game.undo();
2484        game.move_piece("d4").unwrap();
2485        game.undo();
2486        game.redo_nth(1);
2487        assert_eq!(
2488            game.fen(),
2489            "rnbqkbnr/pppppppp/8/8/3P4/8/PPP1PPPP/RNBQKBNR b KQkq - 0 1"
2490        );
2491    }
2492
2493    #[test]
2494    fn test_redo_nth_no_moves() {
2495        let mut game = Game::default();
2496        game.redo_nth(0);
2497        assert_eq!(
2498            game.fen(),
2499            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
2500        );
2501    }
2502
2503    #[test]
2504    fn test_start() {
2505        let mut game = Game::default();
2506        game.move_piece("e4").unwrap();
2507        game.move_piece("e5").unwrap();
2508        game.move_piece("Nf3").unwrap();
2509        game.move_piece("Nc6").unwrap();
2510        game.move_piece("Bb5").unwrap();
2511        game.move_piece("a6").unwrap();
2512        game.move_piece("O-O").unwrap();
2513
2514        game.start();
2515
2516        assert_eq!(
2517            game.fen(),
2518            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
2519        );
2520    }
2521
2522    #[test]
2523    fn test_end() {
2524        let mut game = Game::default();
2525        game.move_piece("e4").unwrap();
2526        game.move_piece("e5").unwrap();
2527        game.move_piece("Nf3").unwrap();
2528        game.move_piece("Nc6").unwrap();
2529        game.move_piece("Bb5").unwrap();
2530        game.move_piece("a6").unwrap();
2531        game.move_piece("O-O").unwrap();
2532
2533        game.start();
2534        game.end();
2535
2536        assert_eq!(
2537            game.fen(),
2538            "r1bqkbnr/1ppp1ppp/p1n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQ1RK1 b kq - 1 4"
2539        );
2540    }
2541
2542    #[test]
2543    fn test_pgn() {
2544        let mut game = Game::default();
2545        game.move_piece("e4").unwrap();
2546        game.move_piece("e5").unwrap();
2547        game.move_piece("Nf3").unwrap();
2548        game.move_piece("Nc6").unwrap();
2549        game.move_piece("Bb5").unwrap();
2550        game.move_piece("a6").unwrap();
2551        game.move_piece("Ba4").unwrap();
2552        game.move_piece("Nf6").unwrap();
2553        game.move_piece("O-O").unwrap();
2554        game.move_piece("Be7").unwrap();
2555        game.move_piece("Re1").unwrap();
2556        game.move_piece("b5").unwrap();
2557        game.move_piece("Bb3").unwrap();
2558        game.move_piece("O-O").unwrap();
2559        game.move_piece("c3").unwrap();
2560        game.move_piece("d5").unwrap();
2561        game.undo();
2562        game.undo();
2563        game.undo();
2564        game.undo();
2565        game.undo();
2566        game.move_piece("O-O").unwrap();
2567        game.move_piece("c3").unwrap();
2568        game.move_piece("b5").unwrap();
2569        game.move_piece("Bc2").unwrap();
2570
2571        let pgn = game.pgn();
2572        assert!(
2573            pgn.contains(
2574                "1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Ba4 Nf6 5. O-O Be7 6. Re1 b5 (6... O-O 7. c3 b5 8. Bc2) 7. Bb3 O-O 8. c3 d5"
2575            )
2576        );
2577    }
2578
2579    #[test]
2580    fn test_get_castle_rook_pos() {
2581        let mut game = Game::default();
2582        assert_eq!(
2583            game.get_castle_rook_pos(CastleType::KingSide),
2584            Some(Position::from_string("h1").unwrap())
2585        );
2586        assert_eq!(
2587            game.get_castle_rook_pos(CastleType::QueenSide),
2588            Some(Position::from_string("a1").unwrap())
2589        );
2590
2591        game.move_piece("e4").unwrap();
2592
2593        assert_eq!(
2594            game.get_castle_rook_pos(CastleType::KingSide),
2595            Some(Position::from_string("h8").unwrap())
2596        );
2597        assert_eq!(
2598            game.get_castle_rook_pos(CastleType::QueenSide),
2599            Some(Position::from_string("a8").unwrap())
2600        );
2601    }
2602
2603    #[test]
2604    fn test_get_castle_rook_pos_after_castle() {
2605        let mut game = Game::default();
2606        game.move_piece("e4").unwrap();
2607        game.move_piece("e5").unwrap();
2608        game.move_piece("Nf3").unwrap();
2609        game.move_piece("Nf6").unwrap();
2610        game.move_piece("Bb5").unwrap();
2611        game.move_piece("Bb4").unwrap();
2612        game.move_piece("O-O").unwrap();
2613
2614        assert_eq!(
2615            game.get_castle_rook_pos(CastleType::KingSide),
2616            Some(Position::from_string("h8").unwrap())
2617        );
2618        assert_eq!(
2619            game.get_castle_rook_pos(CastleType::QueenSide),
2620            Some(Position::from_string("a8").unwrap())
2621        );
2622
2623        game.move_piece("O-O").unwrap();
2624
2625        assert_eq!(game.get_castle_rook_pos(CastleType::KingSide), None);
2626        assert_eq!(game.get_castle_rook_pos(CastleType::QueenSide), None);
2627
2628        game.move_piece("a3").unwrap();
2629
2630        assert_eq!(game.get_castle_rook_pos(CastleType::KingSide), None);
2631        assert_eq!(game.get_castle_rook_pos(CastleType::QueenSide), None);
2632    }
2633
2634    #[test]
2635    fn test_check() {
2636        let mut game = Game::default();
2637        game.move_piece("c4").unwrap();
2638        game.move_piece("d6").unwrap();
2639        game.move_piece("Qa4+").unwrap();
2640        assert!(game.check());
2641    }
2642
2643    #[test]
2644    fn test_check_pgn() {
2645        let mut game = Game::default();
2646        game.move_piece("c4").unwrap();
2647        game.move_piece("d6").unwrap();
2648        game.move_piece("Qa4+").unwrap();
2649        assert!(game.pgn().contains("1. c4 d6 2. Qa4+"));
2650    }
2651
2652    #[test]
2653    fn test_checkmate() {
2654        let mut game = Game::default();
2655        game.move_piece("e4").unwrap();
2656        game.move_piece("e5").unwrap();
2657        game.move_piece("Qh5").unwrap();
2658        game.move_piece("Nc6").unwrap();
2659        game.move_piece("Bc4").unwrap();
2660        game.move_piece("Nf6").unwrap();
2661        game.move_piece("Qxf7#").unwrap();
2662        assert_eq!(game.status, GameStatus::WhiteWins(WinReason::Checkmate));
2663        assert!(game.checkmate());
2664
2665        game = Game::default();
2666        game.move_piece("f3").unwrap();
2667        game.move_piece("e5").unwrap();
2668        game.move_piece("g4").unwrap();
2669        game.move_piece("Qh4#").unwrap();
2670        assert_eq!(game.status, GameStatus::BlackWins(WinReason::Checkmate));
2671        assert!(game.checkmate());
2672    }
2673
2674    #[test]
2675    fn test_stalemate_pgn() {
2676        let mut game = Game::from_fen("6k1/8/6K1/5Q2/8/8/8/8 w - - 0 1").unwrap();
2677        game.move_piece("Qf6").unwrap();
2678        println!("{}", game.pgn());
2679        assert!(game.pgn().contains("[Result \"1/2-1/2\"]"));
2680        assert!(game.pgn().contains("1. Qf6 1/2-1/2"));
2681    }
2682
2683    #[test]
2684    fn test_checkmate_pgn() {
2685        let mut game = Game::default();
2686        game.move_piece("e4").unwrap();
2687        game.move_piece("e5").unwrap();
2688        game.move_piece("Qh5").unwrap();
2689        game.move_piece("Nc6").unwrap();
2690        game.move_piece("Bc4").unwrap();
2691        game.move_piece("Nf6").unwrap();
2692        game.move_piece("Qxf7#").unwrap();
2693        assert!(game
2694            .pgn()
2695            .contains("[Result \"1-0\"]\n1. e4 e5 2. Qh5 Nc6 3. Bc4 Nf6 4. Qxf7# 1-0\n"));
2696    }
2697
2698    #[test]
2699    fn test_fifty_move_rule() {
2700        let mut game = Game::from_fen("8/4P3/8/8/8/4K3/8/4k3 w - - 99 50").unwrap();
2701        game.move_piece("Kd3").unwrap();
2702        assert_eq!(game.status, GameStatus::Draw(DrawReason::FiftyMoveRule));
2703    }
2704
2705    #[test]
2706    fn test_stalemate() {
2707        let mut game = Game::from_fen("8/8/8/8/4Q3/4K3/8/4k3 w - - 0 1").unwrap();
2708        game.move_piece("Qd3").unwrap();
2709        assert!(game.stalemate());
2710        assert_eq!(game.status, GameStatus::Draw(DrawReason::Stalemate));
2711    }
2712
2713    #[test]
2714    fn test_insufficient_material() {
2715        let mut game = Game::from_fen("8/8/8/8/8/3pK3/8/4kB2 w - - 0 1").unwrap();
2716        game.move_piece("Bxd3").unwrap();
2717        assert!(game.insufficient_material());
2718        assert_eq!(
2719            game.status,
2720            GameStatus::Draw(DrawReason::InsufficientMaterial)
2721        );
2722    }
2723
2724    #[test]
2725    fn test_resign() {
2726        let mut game = Game::default();
2727        game.resign(Color::White);
2728        assert_eq!(game.status, GameStatus::BlackWins(WinReason::Resignation));
2729
2730        game = Game::default();
2731        game.resign(Color::Black);
2732        assert_eq!(game.status, GameStatus::WhiteWins(WinReason::Resignation));
2733        game.resign(Color::White);
2734        // this should have no effect
2735        assert_eq!(game.status, GameStatus::WhiteWins(WinReason::Resignation));
2736    }
2737
2738    #[test]
2739    fn test_lose_on_time() {
2740        let mut game = Game::default();
2741        game.lost_on_time(Color::White);
2742        assert_eq!(game.status, GameStatus::BlackWins(WinReason::Time));
2743
2744        game = Game::default();
2745        game.lost_on_time(Color::Black);
2746        assert_eq!(game.status, GameStatus::WhiteWins(WinReason::Time));
2747        game.lost_on_time(Color::White);
2748        // this should have no effect
2749        assert_eq!(game.status, GameStatus::WhiteWins(WinReason::Time));
2750    }
2751
2752    #[test]
2753    fn test_draw_by_agreement() {
2754        let mut game = Game::default();
2755        game.draw_by_agreement();
2756        assert_eq!(game.status, GameStatus::Draw(DrawReason::Agreement));
2757
2758        game = Game::default();
2759        game.resign(Color::White);
2760        // drawing a ended game should have no effect
2761        game.draw_by_agreement();
2762        assert_eq!(game.status, GameStatus::BlackWins(WinReason::Resignation));
2763    }
2764
2765    #[test]
2766    fn test_move_ambiguity() {
2767        let game =
2768            Game::from_fen("r3k1nr/ppq2ppp/2nbp3/3p3b/3P4/2PB1N1P/PP3PP1/RNBQR1K1 w kq - 3 10")
2769                .unwrap();
2770        assert_eq!(
2771            game.move_ambiguity(
2772                PieceType::Knight,
2773                Color::White,
2774                (Some(1), None),
2775                &Position::from_string("d2").unwrap(),
2776                &MoveType::Normal {
2777                    capture: false,
2778                    promotion: None
2779                }
2780            ),
2781            (true, false)
2782        );
2783    }
2784
2785    #[test]
2786    fn test_capture_king_game() {
2787        let mut game = Game::new(
2788            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
2789            true,
2790        )
2791        .unwrap();
2792
2793        game.move_piece("f3").unwrap();
2794        game.move_piece("e5").unwrap();
2795        game.move_piece("g4").unwrap();
2796        game.move_piece("Qh4").unwrap();
2797        assert!(!game.checkmate());
2798        game.move_piece("Kf2").unwrap();
2799        game.move_piece("Qxf2").unwrap();
2800        assert!(game.checkmate());
2801    }
2802}