chess/
game.rs

1use crate::board::{Board, BoardStatus};
2use crate::chess_move::ChessMove;
3use crate::color::Color;
4use crate::error::Error;
5use crate::movegen::MoveGen;
6use crate::piece::Piece;
7use std::str::FromStr;
8
9/// Contains all actions supported within the game
10#[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq)]
11pub enum Action {
12    MakeMove(ChessMove),
13    OfferDraw(Color),
14    AcceptDraw,
15    DeclareDraw,
16    Resign(Color),
17}
18
19/// What was the result of this game?
20#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
21pub enum GameResult {
22    WhiteCheckmates,
23    WhiteResigns,
24    BlackCheckmates,
25    BlackResigns,
26    Stalemate,
27    DrawAccepted,
28    DrawDeclared,
29}
30
31/// For UI/UCI Servers, store a game object which allows you to determine
32/// draw by 3 fold repitition, draw offers, resignations, and moves.
33///
34/// This structure is slow compared to using `Board` directly, so it is
35/// not recommended for engines.
36#[derive(Clone, Debug)]
37pub struct Game {
38    start_pos: Board,
39    moves: Vec<Action>,
40}
41
42impl Game {
43    /// Create a new `Game` with the initial position.
44    ///
45    /// ```
46    /// use chess::{Game, Board};
47    ///
48    /// let game = Game::new();
49    /// assert_eq!(game.current_position(), Board::default());
50    /// ```
51    pub fn new() -> Game {
52        Game {
53            start_pos: Board::default(),
54            moves: vec![],
55        }
56    }
57
58    /// Create a new `Game` with a specific starting position.
59    ///
60    /// ```
61    /// use chess::{Game, Board};
62    ///
63    /// let game = Game::new_with_board(Board::default());
64    /// assert_eq!(game.current_position(), Board::default());
65    /// ```
66    pub fn new_with_board(board: Board) -> Game {
67        Game {
68            start_pos: board,
69            moves: vec![],
70        }
71    }
72
73    /// Get all actions made in this game (moves, draw offers, resignations, etc.)
74    ///
75    /// ```
76    /// use chess::{Game, MoveGen, Color};
77    ///
78    /// let mut game = Game::new();
79    /// let mut movegen = MoveGen::new_legal(&game.current_position());
80    ///
81    /// game.make_move(movegen.next().expect("At least one valid move"));
82    /// game.resign(Color::Black);
83    /// assert_eq!(game.actions().len(), 2);
84    /// ```
85    pub fn actions(&self) -> &Vec<Action> {
86        &self.moves
87    }
88
89    /// What is the status of this game?
90    ///
91    /// ```
92    /// use chess::Game;
93    ///
94    /// let game = Game::new();
95    /// assert!(game.result().is_none());
96    /// ```
97    pub fn result(&self) -> Option<GameResult> {
98        match self.current_position().status() {
99            BoardStatus::Checkmate => {
100                if self.side_to_move() == Color::White {
101                    Some(GameResult::BlackCheckmates)
102                } else {
103                    Some(GameResult::WhiteCheckmates)
104                }
105            }
106            BoardStatus::Stalemate => Some(GameResult::Stalemate),
107            BoardStatus::Ongoing => {
108                if self.moves.len() == 0 {
109                    None
110                } else if self.moves[self.moves.len() - 1] == Action::AcceptDraw {
111                    Some(GameResult::DrawAccepted)
112                } else if self.moves[self.moves.len() - 1] == Action::DeclareDraw {
113                    Some(GameResult::DrawDeclared)
114                } else if self.moves[self.moves.len() - 1] == Action::Resign(Color::White) {
115                    Some(GameResult::WhiteResigns)
116                } else if self.moves[self.moves.len() - 1] == Action::Resign(Color::Black) {
117                    Some(GameResult::BlackResigns)
118                } else {
119                    None
120                }
121            }
122        }
123    }
124
125    /// Create a new `Game` object from an FEN string.
126    ///
127    /// ```
128    /// use chess::{Game, Board};
129    ///
130    /// // This is the better way:
131    /// # {
132    /// use std::str::FromStr;
133    /// let game: Game = Game::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").expect("Valid FEN");
134    /// let game2: Result<Game, _> = Game::from_str("Invalid FEN");
135    /// assert!(game2.is_err());
136    /// # }
137    ///
138    /// // This still works
139    /// # {
140    /// let game = Game::new_from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").expect("Valid FEN");
141    /// let game2 = Game::new_from_fen("Invalid FEN");
142    /// assert!(game2.is_none());
143    /// # }
144    /// ```
145    #[deprecated(since = "3.1.0", note = "Please use Game::from_str(fen)? instead.")]
146    pub fn new_from_fen(fen: &str) -> Option<Game> {
147        Game::from_str(fen).ok()
148    }
149
150    /// Get the current position on the board from the `Game` object.
151    ///
152    /// ```
153    /// use chess::{Game, Board};
154    ///
155    /// let game = Game::new();
156    /// assert_eq!(game.current_position(), Board::default());
157    /// ```
158    pub fn current_position(&self) -> Board {
159        let mut copy = self.start_pos;
160
161        for x in self.moves.iter() {
162            match *x {
163                Action::MakeMove(m) => {
164                    copy = copy.make_move_new(m);
165                }
166                _ => {}
167            }
168        }
169
170        copy
171    }
172
173    /// Determine if a player can legally declare a draw by 3-fold repetition or 50-move rule.
174    ///
175    /// ```
176    /// use chess::{Game, Square, ChessMove};
177    ///
178    /// let b1c3 = ChessMove::new(Square::B1, Square::C3, None);
179    /// let c3b1 = ChessMove::new(Square::C3, Square::B1, None);
180    ///
181    /// let b8c6 = ChessMove::new(Square::B8, Square::C6, None);
182    /// let c6b8 = ChessMove::new(Square::C6, Square::B8, None);
183    ///
184    /// let mut game = Game::new();
185    /// assert_eq!(game.can_declare_draw(), false);
186    ///
187    /// game.make_move(b1c3);
188    /// game.make_move(b8c6);
189    /// game.make_move(c3b1);
190    /// game.make_move(c6b8);
191    ///
192    /// assert_eq!(game.can_declare_draw(), false); // position has shown up twice
193    ///
194    /// game.make_move(b1c3);
195    /// game.make_move(b8c6);
196    /// game.make_move(c3b1);
197    /// game.make_move(c6b8);
198    /// assert_eq!(game.can_declare_draw(), true); // position has shown up three times
199    /// ```
200    pub fn can_declare_draw(&self) -> bool {
201        if self.result().is_some() {
202            return false;
203        }
204
205        let mut legal_moves_per_turn: Vec<(u64, Vec<ChessMove>)> = vec![];
206
207        let mut board = self.start_pos;
208        let mut reversible_moves = 0;
209
210        // Loop over each move, counting the reversible_moves for draw by 50 move rule,
211        // and filling a list of legal_moves_per_turn list for 3-fold repitition
212        legal_moves_per_turn.push((board.get_hash(), MoveGen::new_legal(&board).collect()));
213        for x in self.moves.iter() {
214            match *x {
215                Action::MakeMove(m) => {
216                    let white_castle_rights = board.castle_rights(Color::White);
217                    let black_castle_rights = board.castle_rights(Color::Black);
218                    if board.piece_on(m.get_source()) == Some(Piece::Pawn) {
219                        reversible_moves = 0;
220                        legal_moves_per_turn.clear();
221                    } else if board.piece_on(m.get_dest()).is_some() {
222                        reversible_moves = 0;
223                        legal_moves_per_turn.clear();
224                    } else {
225                        reversible_moves += 1;
226                    }
227                    board = board.make_move_new(m);
228
229                    if board.castle_rights(Color::White) != white_castle_rights
230                        || board.castle_rights(Color::Black) != black_castle_rights
231                    {
232                        reversible_moves = 0;
233                        legal_moves_per_turn.clear();
234                    }
235                    legal_moves_per_turn
236                        .push((board.get_hash(), MoveGen::new_legal(&board).collect()));
237                }
238                _ => {}
239            }
240        }
241
242        if reversible_moves >= 100 {
243            return true;
244        }
245
246        // Detect possible draw by 3 fold repitition
247        let last_moves = legal_moves_per_turn[legal_moves_per_turn.len() - 1].clone();
248
249        for i in 1..(legal_moves_per_turn.len() - 1) {
250            for j in 0..i {
251                if legal_moves_per_turn[i] == last_moves && legal_moves_per_turn[j] == last_moves {
252                    return true;
253                }
254            }
255        }
256
257        return false;
258    }
259
260    /// Declare a draw by 3-fold repitition or 50-move rule.
261    ///
262    /// ```
263    /// use chess::{Game, Square, ChessMove};
264    ///
265    /// let b1c3 = ChessMove::new(Square::B1, Square::C3, None);
266    /// let c3b1 = ChessMove::new(Square::C3, Square::B1, None);
267    ///
268    /// let b8c6 = ChessMove::new(Square::B8, Square::C6, None);
269    /// let c6b8 = ChessMove::new(Square::C6, Square::B8, None);
270    ///
271    /// let mut game = Game::new();
272    /// assert_eq!(game.can_declare_draw(), false);
273    ///
274    /// game.make_move(b1c3);
275    /// game.make_move(b8c6);
276    /// game.make_move(c3b1);
277    /// game.make_move(c6b8);
278    ///
279    /// assert_eq!(game.can_declare_draw(), false); // position has shown up twice
280    ///
281    /// game.make_move(b1c3);
282    /// game.make_move(b8c6);
283    /// game.make_move(c3b1);
284    /// game.make_move(c6b8);
285    /// assert_eq!(game.can_declare_draw(), true); // position has shown up three times
286    /// game.declare_draw();
287    /// ```
288    pub fn declare_draw(&mut self) -> bool {
289        if self.can_declare_draw() {
290            self.moves.push(Action::DeclareDraw);
291            true
292        } else {
293            false
294        }
295    }
296
297    /// Make a chess move on the board
298    ///
299    /// ```
300    /// use chess::{Game, MoveGen};
301    ///
302    /// let mut game = Game::new();
303    ///
304    /// let mut movegen = MoveGen::new_legal(&game.current_position());
305    ///
306    /// game.make_move(movegen.next().expect("At least one legal move"));
307    /// ```
308    pub fn make_move(&mut self, chess_move: ChessMove) -> bool {
309        if self.result().is_some() {
310            return false;
311        }
312        if self.current_position().legal(chess_move) {
313            self.moves.push(Action::MakeMove(chess_move));
314            true
315        } else {
316            false
317        }
318    }
319
320    /// Who's turn is it to move?
321    ///
322    /// ```
323    /// use chess::{Game, Color};
324    ///
325    /// let game = Game::new();
326    /// assert_eq!(game.side_to_move(), Color::White);
327    /// ```
328    pub fn side_to_move(&self) -> Color {
329        let move_count = self
330            .moves
331            .iter()
332            .filter(|m| match *m {
333                Action::MakeMove(_) => true,
334                _ => false,
335            })
336            .count()
337            + if self.start_pos.side_to_move() == Color::White {
338                0
339            } else {
340                1
341            };
342
343        if move_count % 2 == 0 {
344            Color::White
345        } else {
346            Color::Black
347        }
348    }
349
350    /// Offer a draw to my opponent.  `color` is the player who offered the draw.  The draw must be
351    /// accepted before my opponent moves.
352    ///
353    /// ```
354    /// use chess::{Game, Color};
355    ///
356    /// let mut game = Game::new();
357    /// game.offer_draw(Color::White);
358    /// ```
359    pub fn offer_draw(&mut self, color: Color) -> bool {
360        if self.result().is_some() {
361            return false;
362        }
363        self.moves.push(Action::OfferDraw(color));
364        return true;
365    }
366
367    /// Accept a draw offer from my opponent.
368    ///
369    /// ```
370    /// use chess::{Game, MoveGen, Color};
371    ///
372    /// let mut game = Game::new();
373    /// game.offer_draw(Color::Black);
374    /// assert_eq!(game.accept_draw(), true);
375    ///
376    /// let mut game2 = Game::new();
377    /// let mut movegen = MoveGen::new_legal(&game2.current_position());
378    /// game2.offer_draw(Color::Black);
379    /// game2.make_move(movegen.next().expect("At least one legal move"));
380    /// assert_eq!(game2.accept_draw(), false);
381    /// ```
382    pub fn accept_draw(&mut self) -> bool {
383        if self.result().is_some() {
384            return false;
385        }
386        if self.moves.len() > 0 {
387            if self.moves[self.moves.len() - 1] == Action::OfferDraw(Color::White)
388                || self.moves[self.moves.len() - 1] == Action::OfferDraw(Color::Black)
389            {
390                self.moves.push(Action::AcceptDraw);
391                return true;
392            }
393        }
394
395        if self.moves.len() > 1 {
396            if self.moves[self.moves.len() - 2] == Action::OfferDraw(!self.side_to_move()) {
397                self.moves.push(Action::AcceptDraw);
398                return true;
399            }
400        }
401
402        false
403    }
404
405    /// `color` resigns the game
406    ///
407    /// ```
408    /// use chess::{Game, Color};
409    ///
410    /// let mut game = Game::new();
411    /// game.resign(Color::White);
412    /// ```
413    pub fn resign(&mut self, color: Color) -> bool {
414        if self.result().is_some() {
415            return false;
416        }
417        self.moves.push(Action::Resign(color));
418        return true;
419    }
420}
421
422impl FromStr for Game {
423    type Err = Error;
424
425    fn from_str(fen: &str) -> Result<Self, Self::Err> {
426        Ok(Game::new_with_board(Board::from_str(fen)?))
427    }
428}
429
430#[cfg(test)]
431pub fn fake_pgn_parser(moves: &str) -> Game {
432    moves
433        .split_whitespace()
434        .filter(|s| !s.ends_with("."))
435        .fold(Game::new(), |mut g, m| {
436            g.make_move(ChessMove::from_san(&g.current_position(), m).expect("Valid SAN Move"));
437            g
438        })
439}
440
441#[test]
442pub fn test_can_declare_draw() {
443    let game = fake_pgn_parser(
444        "1. Nc3 d5 2. e3 Nc6 3. Nf3 Nf6 4. Bb5 a6 5. Bxc6+ bxc6 6. Ne5 Qd6 7. d4 Nd7
445                8. f4 Nxe5 9. dxe5 Qg6 10. O-O Bf5 11. e4 Bxe4 12. Nxe4 Qxe4 13. Re1 Qb4
446                14. e6 f6 15. Be3 g6 16. Qd4 Qxd4 17. Bxd4 Bh6 18. g3 g5 19. f5 g4 20. Rad1
447                Rg8 21. b3 Rb8 22. c4 dxc4 23. bxc4 Rd8 24. Kg2 Rc8 25. Bc5 Rg5 26. Rd7 Bf8
448                27. Rf1 a5 28. Kg1 a4 29. Bb4 Rh5 30. Rf4 Rg5 31. Rf1 Rh5 32. Rf4 Rg5 33.
449                Ba5",
450    );
451    assert!(!game.can_declare_draw());
452
453    // three fold
454    let game = fake_pgn_parser("1. Nc3 Nf6 2. Nb1 Ng8 3. Nc3 Nf6 4. Nb1 Ng8 5. Nc3 Nf6 6. Nb1 Ng8");
455    assert!(game.can_declare_draw());
456
457    // three fold (again)
458    let game = fake_pgn_parser("1. Nc3 Nf6 2. Nb1 Ng8 3. Nc3 Nf6 4. Nb1 Ng8 5. Nc3 Nf6 6. Nb1");
459    assert!(game.can_declare_draw());
460
461    // three fold, but with a move at the end that breaks the draw cycle
462    let game =
463        fake_pgn_parser("1. Nc3 Nf6 2. Nb1 Ng8 3. Nc3 Nf6 4. Nb1 Ng8 5. Nc3 Nf6 6. Nb1 Ng8 7. e4");
464    assert!(!game.can_declare_draw());
465
466    // three fold, but with a move at the end that breaks the draw cycle
467    let game =
468        fake_pgn_parser("1. Nc3 Nf6 2. Nb1 Ng8 3. Nc3 Nf6 4. Nb1 Ng8 5. Nc3 Nf6 6. Nb1 Ng8 7. e4");
469    assert!(!game.can_declare_draw());
470
471    // fifty move rule
472    let game = fake_pgn_parser("1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6 5. Nf3 O-O 6. Be2 e5 7. O-O Nc6 8. d5 Ne7 9. Nd2 a5 10. Rb1 Nd7 11. a3 f5 12. b4 Kh8 13. f3 Ng8 14. Qc2 Ngf6 15. Nb5 axb4 16. axb4 Nh5 17. g3 Ndf6 18. c5 Bd7 19. Rb3 Nxg3 20. hxg3 Nh5 21. f4 exf4 22. c6 bxc6 23. dxc6 Nxg3 24. Rxg3 fxg3 25. cxd7 g2 26. Rf3 Qxd7 27. Bb2 fxe4 28. Rxf8+ Rxf8 29. Bxg7+ Qxg7 30. Qxe4 Qf6 31. Nf3 Qf4 32. Qe7 Rf7 33. Qe6 Rf6 34. Qe8+ Rf8 35. Qe7 Rf7 36. Qe6 Rf6 37. Qb3 g5 38. Nxc7 g4 39. Nd5 Qc1+ 40. Qd1 Qxd1+ 41. Bxd1 Rf5 42. Ne3 Rf4 43. Ne1 Rxb4 44. Bxg4 h5 45. Bf3 d5 46. N3xg2 h4 47. Nd3 Ra4 48. Ngf4 Kg7 49. Kg2 Kf6 50. Bxd5 Ra5 51. Bc6 Ra6 52. Bb7 Ra3 53. Be4 Ra4 54. Bd5 Ra5 55. Bc6 Ra6 56. Bf3 Kg5 57. Bb7 Ra1 58. Bc8 Ra4 59. Kf3 Rc4 60. Bd7 Kf6 61. Kg4 Rd4 62. Bc6 Rd8 63. Kxh4 Rg8 64. Be4 Rg1 65. Nh5+ Ke6 66. Ng3 Kf6 67. Kg4 Ra1 68. Bd5 Ra5 69. Bf3 Ra1 70. Kf4 Ke6 71. Nc5+ Kd6 72. Nge4+ Ke7 73. Ke5 Rf1 74. Bg4 Rg1 75. Be6 Re1 76. Bc8 Rc1 77. Kd4 Rd1+ 78. Nd3 Kf7 79. Ke3 Ra1 80. Kf4 Ke7 81. Nb4 Rc1 82. Nd5+ Kf7 83. Bd7 Rf1+ 84. Ke5 Ra1 85. Ng5+ Kg6 86. Nf3 Kg7 87. Bg4 Kg6 88. Nf4+ Kg7 89. Nd4 Re1+ 90. Kf5 Rc1 91. Be2 Re1 92. Bh5 Ra1 93. Nfe6+ Kh6 94. Be8 Ra8 95. Bc6 Ra1 96. Kf6 Kh7 97. Ng5+ Kh8 98. Nde6 Ra6 99. Be8 Ra8 100. Bh5 Ra1 101. Bg6 Rf1+ 102. Ke7 Ra1 103. Nf7+ Kg8 104. Nh6+ Kh8 105. Nf5 Ra7+ 106. Kf6 Ra1 107. Ne3 Re1 108. Nd5 Rg1 109. Bf5 Rf1 110. Ndf4 Ra1 111. Ng6+ Kg8 112. Ne7+ Kh8 113. Ng5");
473    assert!(game.can_declare_draw());
474
475    let game = fake_pgn_parser("1. d4 Nf6 2. c4 g6 3. Nc3 Bg7 4. e4 d6 5. Nf3 O-O 6. Be2 e5 7. O-O Nc6 8. d5 Ne7 9. Nd2 a5 10. Rb1 Nd7 11. a3 f5 12. b4 Kh8 13. f3 Ng8 14. Qc2 Ngf6 15. Nb5 axb4 16. axb4 Nh5 17. g3 Ndf6 18. c5 Bd7 19. Rb3 Nxg3 20. hxg3 Nh5 21. f4 exf4 22. c6 bxc6 23. dxc6 Nxg3 24. Rxg3 fxg3 25. cxd7 g2 26. Rf3 Qxd7 27. Bb2 fxe4 28. Rxf8+ Rxf8 29. Bxg7+ Qxg7 30. Qxe4 Qf6 31. Nf3 Qf4 32. Qe7 Rf7 33. Qe6 Rf6 34. Qe8+ Rf8 35. Qe7 Rf7 36. Qe6 Rf6 37. Qb3 g5 38. Nxc7 g4 39. Nd5 Qc1+ 40. Qd1 Qxd1+ 41. Bxd1 Rf5 42. Ne3 Rf4 43. Ne1 Rxb4 44. Bxg4 h5 45. Bf3 d5 46. N3xg2 h4 47. Nd3 Ra4 48. Ngf4 Kg7 49. Kg2 Kf6 50. Bxd5 Ra5 51. Bc6 Ra6 52. Bb7 Ra3 53. Be4 Ra4 54. Bd5 Ra5 55. Bc6 Ra6 56. Bf3 Kg5 57. Bb7 Ra1 58. Bc8 Ra4 59. Kf3 Rc4 60. Bd7 Kf6 61. Kg4 Rd4 62. Bc6 Rd8 63. Kxh4 Rg8 64. Be4 Rg1 65. Nh5+ Ke6 66. Ng3 Kf6 67. Kg4 Ra1 68. Bd5 Ra5 69. Bf3 Ra1 70. Kf4 Ke6 71. Nc5+ Kd6 72. Nge4+ Ke7 73. Ke5 Rf1 74. Bg4 Rg1 75. Be6 Re1 76. Bc8 Rc1 77. Kd4 Rd1+ 78. Nd3 Kf7 79. Ke3 Ra1 80. Kf4 Ke7 81. Nb4 Rc1 82. Nd5+ Kf7 83. Bd7 Rf1+ 84. Ke5 Ra1 85. Ng5+ Kg6 86. Nf3 Kg7 87. Bg4 Kg6 88. Nf4+ Kg7 89. Nd4 Re1+ 90. Kf5 Rc1 91. Be2 Re1 92. Bh5 Ra1 93. Nfe6+ Kh6 94. Be8 Ra8 95. Bc6 Ra1 96. Kf6 Kh7 97. Ng5+ Kh8 98. Nde6 Ra6 99. Be8 Ra8 100. Bh5 Ra1 101. Bg6 Rf1+ 102. Ke7 Ra1 103. Nf7+ Kg8 104. Nh6+ Kh8 105. Nf5 Ra7+ 106. Kf6 Ra1 107. Ne3 Re1 108. Nd5 Rg1 109. Bf5 Rf1 110. Ndf4 Ra1 111. Ng6+ Kg8 112. Ne7+ Kh8");
476    assert!(!game.can_declare_draw());
477}