vprytz_chess/
lib.rs

1//! Author: Vilhelm Prytz <vilhelm@prytznet.se> or <vprytz@kth.se>
2//!
3//! This is a chess library written in Rust.
4//! It is a work in progress and is not yet ready for use.
5//!
6//! # Usage
7//!
8//! # Functions
9//!
10//! | **Function**                                                                  | **Description**                                                                                                                                                                               |
11//! | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
12//! | `pub fn new() -> Game`                                                        | Initialises a new board with pieces.                                                                                                                                                          |
13//! | `pub fn make_move(&mut self, from: String, to: String) -> Option<GameState>`  | If the current game state is `InProgress` and the move is legal, move a piece and return the resulting state of the game.                                                                     |
14//! | **Not yet implemeted** `pub fn set_promotion(&mut self, piece: String) -> ()`                        | Set the piece type that a peasant becames following a promotion.                                                                                                                              |
15//! | `pub fn get_game_state(&self) -> GameState`                                   | Get the current game state.                                                                                                                                                                   |
16//! | `pub fn get_possible_moves(&self, position: String) -> Optional<Vec<String>>` | If a piece is standing on the given tile, return all possible new positions of that piece. Don't forget to the rules for check. _(optional)_ Don't forget to include en passent and castling. |
17//!
18//! # Generate this README
19//!
20//! You need [cargo-readme](https://github.com/livioribeiro/cargo-readme) to generate this README.
21//!
22//! ```bash
23//! cargo readme > README.md
24//! ```
25
26use std::fmt;
27
28const BOARD_SIZE: usize = 8;
29
30/// Possible states of the game is represented using this enum.
31#[derive(Copy, Clone, Debug, PartialEq)]
32pub enum GameState {
33    InProgress,
34    Check,
35    GameOver,
36}
37
38/// Possible colors for pieces is represented using this enum.
39#[derive(Copy, Clone, Debug, PartialEq)]
40pub enum Color {
41    White,
42    Black,
43}
44
45// Possible types of pieces is represented using this enum.
46#[derive(Copy, Clone, Debug, PartialEq)]
47pub enum PieceType {
48    King,
49    Queen,
50    Rook,
51    Bishop,
52    Knight,
53    Pawn,
54}
55
56// copyed example from https://users.rust-lang.org/t/how-can-i-implement-fmt-display-for-enum/24111/2
57impl fmt::Display for PieceType {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        match *self {
60            PieceType::King => write!(f, "K "),
61            PieceType::Queen => write!(f, "Q "),
62            PieceType::Rook => write!(f, "R "),
63            PieceType::Bishop => write!(f, "B "),
64            PieceType::Knight => write!(f, "Kn"),
65            PieceType::Pawn => write!(f, "P "),
66        }
67    }
68}
69
70/// Represents a piece on the board.
71/// A piece has a color, a type and a variable that tells if it has moved at least once or not.
72/// A piece can be moved by calling the make_move() function.
73/// # Examples
74/// ```
75/// use vprytz_chess::Piece;
76/// use vprytz_chess::Color;
77/// use vprytz_chess::PieceType;
78/// let mut piece = Piece {
79///     color: vprytz_chess::Color::White,
80///     piece: vprytz_chess::PieceType::King,
81///     untouched: true,
82/// };
83/// ```
84#[derive(Copy, Clone, Debug, PartialEq)]
85pub struct Piece {
86    pub color: Color,
87    pub piece: PieceType,
88    pub untouched: bool,
89}
90
91// copied base example from https://doc.rust-lang.org/rust-by-example/hello/print/print_display.html
92impl fmt::Display for Piece {
93    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94        write!(f, "{}", self.piece)
95    }
96}
97
98/// Represents a chess game, holding a board (2d array with all pieces) and the current state of the game.
99/// A new game can be created by calling the new() function.
100/// # Examples
101/// ```
102/// use vprytz_chess::Game;
103/// let mut game = Game::new();
104/// // call functions on game, to move pieces and so on
105/// ```
106pub struct Game {
107    state: GameState,
108    board: [[Option<Piece>; BOARD_SIZE]; BOARD_SIZE],
109}
110
111impl Game {
112    /// Initialises a new board with pieces.
113    /// # Examples
114    /// ```
115    /// use vprytz_chess::Game;
116    /// let mut game = Game::new();
117    /// ```
118    pub fn new() -> Game {
119        let mut game = Game {
120            state: GameState::InProgress,
121            board: [[None; BOARD_SIZE]; BOARD_SIZE],
122        };
123        // add pieces
124        game.setup_initial_board();
125
126        // return board
127        game
128    }
129
130    /// Sets up the initial board with pieces, called by new().
131    /// # Examples
132    /// ```
133    /// use vprytz_chess::Game;
134    /// let mut game = Game::new();
135    /// game.setup_initial_board(); // redundant since new() calls this function, but can be called again to "reset" board later on
136    /// ```
137    pub fn setup_initial_board(&mut self) -> () {
138        // calling this will also "reset board"
139        let white_pawn = Some(Piece {
140            color: Color::White,
141            piece: PieceType::Pawn,
142            untouched: true,
143        });
144        let black_pawn = Some(Piece {
145            color: Color::Black,
146            piece: PieceType::Pawn,
147            untouched: true,
148        });
149        let white_rook = Some(Piece {
150            color: Color::White,
151            piece: PieceType::Rook,
152            untouched: true,
153        });
154        let black_rook = Some(Piece {
155            color: Color::Black,
156            piece: PieceType::Rook,
157            untouched: true,
158        });
159        let white_knight = Some(Piece {
160            color: Color::White,
161            piece: PieceType::Knight,
162            untouched: true,
163        });
164        let black_knight = Some(Piece {
165            color: Color::Black,
166            piece: PieceType::Knight,
167            untouched: true,
168        });
169        let white_bishop = Some(Piece {
170            color: Color::White,
171            piece: PieceType::Bishop,
172            untouched: true,
173        });
174        let black_bishop = Some(Piece {
175            color: Color::Black,
176            piece: PieceType::Bishop,
177            untouched: true,
178        });
179        let white_queen = Some(Piece {
180            color: Color::White,
181            piece: PieceType::Queen,
182            untouched: true,
183        });
184        let black_queen = Some(Piece {
185            color: Color::Black,
186            piece: PieceType::Queen,
187            untouched: true,
188        });
189        let white_king = Some(Piece {
190            color: Color::White,
191            piece: PieceType::King,
192            untouched: true,
193        });
194        let black_king = Some(Piece {
195            color: Color::Black,
196            piece: PieceType::King,
197            untouched: true,
198        });
199        self.board[0] = [
200            black_rook,
201            black_knight,
202            black_bishop,
203            black_queen,
204            black_king,
205            black_bishop,
206            black_knight,
207            black_rook,
208        ];
209        self.board[1] = [black_pawn; BOARD_SIZE];
210
211        // empty pieces, this is here to clear any pieces that may have been moved here (reset board)
212        self.board[2] = [None; BOARD_SIZE];
213        self.board[3] = [None; BOARD_SIZE];
214        self.board[4] = [None; BOARD_SIZE];
215        self.board[5] = [None; BOARD_SIZE];
216
217        self.board[6] = [white_pawn; BOARD_SIZE];
218        self.board[7] = [
219            white_rook,
220            white_knight,
221            white_bishop,
222            white_queen,
223            white_king,
224            white_bishop,
225            white_knight,
226            white_rook,
227        ];
228    }
229
230    /// If the current game state is InProgress and the move is legal,
231    /// move a piece and return the resulting state of the game.
232    /// # Examples
233    /// ```
234    /// use vprytz_chess::Game;
235    /// let mut game = Game::new();
236    /// game.make_move("D2".to_string(), "D4".to_string()); // move white pawn at D2 to D4 (will only be allowed if move is legal, checked by get_possible_moves())
237    /// ```
238    pub fn make_move(&mut self, from: String, to: String) -> Option<GameState> {
239        // check if move is legal by checking if "to" position is in get_possible_moves()
240        let possible_moves = self.get_possible_moves(from.to_string());
241
242        // check that "to" is in possible_moves
243        // sourcde https://stackoverflow.com/questions/58368801/how-do-i-check-if-a-thing-is-in-a-vector#58368936
244        if possible_moves.unwrap().contains(&to) {
245            // move piece
246            let from_index = self.pos_to_index(from.to_string());
247            let to_index = self.pos_to_index(to.to_string());
248
249            let piece = self.board[from_index.0][from_index.1];
250
251            // set piece as touched
252            let mut piece = piece.unwrap();
253            piece.untouched = false;
254
255            self.board[from_index.0][from_index.1] = None;
256            self.board[to_index.0][to_index.1] = Some(piece);
257
258            // check if game is over
259            // if self.is_checkmate() {
260            //     self.state = GameState::GameOver;
261            // } else if self.is_check() {
262            //     self.state = GameState::Check;
263            // }
264
265            return Some(self.get_game_state());
266        } else {
267            return None;
268        }
269    }
270
271    /// Set the piece type that a peasant becames following a promotion.
272    pub fn set_promotion(&mut self, piece: String) -> () {
273        ()
274    }
275
276    /// Get the current game state.
277    /// # Examples
278    /// ```
279    /// use vprytz_chess::Game;
280    /// let mut game = Game::new();
281    /// game.get_game_state(); // returns GameState::InProgress
282    /// ```
283    pub fn get_game_state(&self) -> GameState {
284        self.state
285    }
286
287    /// If a piece is standing on the given tile, return all possible
288    /// new positions of that piece.
289    /// # Examples
290    /// ```
291    /// use vprytz_chess::Game;
292    /// let mut game = Game::new();
293    /// game.get_possible_moves("D2".to_string()); // returns all possible moves for white pawn at D2
294    /// ```
295    /// # TODO
296    /// - check if move is legal (e.g. if king is in check after move)
297    /// - check if pawn can be promoted
298    /// - check if pawn can be captured en passant
299    /// - check for all types of pieces (not yet done)
300    /// # Panics
301    /// Panics if the given position is not on the board.
302    /// # Errors
303    /// Returns None if there is no piece on the given position.
304    pub fn get_possible_moves(&self, postion: String) -> Option<Vec<String>> {
305        let pos = self.pos_to_index(postion);
306
307        // get piece at given position
308        let piece = self.board[pos.0][pos.1];
309
310        let op: i32 = match piece.unwrap().color {
311            Color::White => 1,
312            Color::Black => -1,
313        };
314
315        // different move sets for different PieceTypes
316        match piece {
317            //
318            // PAWN
319            //
320            Some(Piece {
321                piece: PieceType::Pawn,
322                ..
323            }) => {
324                let mut vec: Vec<String> = Vec::with_capacity(5);
325
326                // add possible moves only if they are empty
327                if self.board[(pos.0 as i32 - 1 * op) as usize][pos.1].is_none() {
328                    vec.push(self.index_to_pos(((pos.0 as i32 - 1 * op) as usize, pos.1)));
329                    // forward (up/down) one
330                }
331
332                if self.board[(pos.0 as i32 - 2 * op) as usize][pos.1].is_none()
333                    && self.board[(pos.0 as i32 - 1 * op) as usize][pos.1].is_none()
334                    && piece.unwrap().untouched
335                {
336                    vec.push(self.index_to_pos(((pos.0 as i32 - 2 * op) as usize, pos.1)));
337                    // forward (up/down) two (only if first move!)
338                }
339
340                // attack moves only if the specified positions is occupied by an enemy piece
341                // we check that there is something there and that the piece there actually has a different color
342                // than our piece
343                if self.board[(pos.0 as i32 - 1 * op) as usize][pos.1 + 1].is_some()
344                    && self.board[(pos.0 as i32 - 1 * op) as usize][pos.1 + 1]
345                        .unwrap()
346                        .color
347                        != piece.unwrap().color
348                {
349                    vec.push(self.index_to_pos(((pos.0 as i32 - 1 * op) as usize, pos.1 + 1)));
350                    // forward (up/down) one and right (attack right)
351                }
352
353                if self.board[(pos.0 as i32 - 1 * op) as usize][pos.1 - 1].is_some()
354                    && self.board[(pos.0 as i32 - 1 * op) as usize][pos.1 - 1]
355                        .unwrap()
356                        .color
357                        != piece.unwrap().color
358                {
359                    vec.push(self.index_to_pos(((pos.0 as i32 - 1 * op) as usize, pos.1 - 1)));
360                    // forward (up/down) one and left (attack left)
361                }
362
363                return Some(vec);
364            }
365            //
366            // ROOK
367            //
368            Some(Piece {
369                piece: PieceType::Rook,
370                ..
371            }) => {
372                let vec: Vec<String> = Vec::with_capacity(5);
373
374                // get all possible moves in all directions
375
376                return Some(vec);
377            }
378            //
379            // BISHOP
380            //
381            Some(Piece {
382                piece: PieceType::Bishop,
383                ..
384            }) => {
385                let vec: Vec<String> = Vec::with_capacity(5);
386
387                return Some(vec);
388            }
389            //
390            // KNIGHT
391            //
392            Some(Piece {
393                piece: PieceType::Knight,
394                ..
395            }) => {
396                let mut vec: Vec<String> = Vec::with_capacity(5);
397
398                // make list of positions to check
399                let positions = [
400                    // forward and left/right
401                    ((pos.0 as i32 - 2 * op), (pos.1 as i32 + 1)), // two pieces "forward" and one right
402                    ((pos.0 as i32 - 2 * op), (pos.1 as i32 - 1)), // two pieces "forward" and one left
403                    // backwards and left/right
404                    ((pos.0 as i32 + 2 * op), (pos.1 as i32 + 1)), // two pieces "backward" and one right
405                    ((pos.0 as i32 + 2 * op), (pos.1 as i32 - 1)), // two pieces "backward" and one left
406                    // left and up/down
407                    ((pos.0 as i32 + 1 * op), (pos.1 as i32 - 2)), // two pieces "left" and one "down"
408                    ((pos.0 as i32 - 1 * op), (pos.1 as i32 - 2)), // two pieces "left" and one "up"
409                    // right and up/down
410                    ((pos.0 as i32 + 1 * op), (pos.1 as i32 + 2)), // two pieces "right" and one "down"
411                    ((pos.0 as i32 - 1 * op), (pos.1 as i32 + 2)), // two pieces "right" and one "up"
412                ];
413
414                // check for each position that it is on the board and that it is either empty or occupied by an enemy piece
415                for pos in positions.iter() {
416                    if pos.0 < 8
417                        && pos.0 >= 0
418                        && pos.1 < 8
419                        && pos.1 >= 0
420                        && (self.board[pos.0 as usize][pos.1 as usize].is_none()
421                            || self.board[pos.0 as usize][pos.1 as usize].unwrap().color
422                                != piece.unwrap().color)
423                    {
424                        vec.push(self.index_to_pos((pos.0 as usize, pos.1 as usize)));
425                    }
426                }
427
428                return Some(vec);
429            }
430            //
431            // QUEEN
432            //
433            Some(Piece {
434                piece: PieceType::Queen,
435                ..
436            }) => {
437                let vec: Vec<String> = Vec::with_capacity(5);
438
439                return Some(vec);
440            }
441            //
442            // KING
443            //
444            Some(Piece {
445                piece: PieceType::King,
446                ..
447            }) => {
448                let mut vec: Vec<String> = Vec::with_capacity(5);
449
450                // make list of positions to check
451                let positions = [
452                    // one step, each direction (including diagonally)
453                    ((pos.0 as i32 - 1 * op), (pos.1 as i32 + 1)), // one square "forward" and one right
454                    ((pos.0 as i32 - 1 * op), (pos.1 as i32 - 1)), // one square "forward" and one left
455                    ((pos.0 as i32 + 1 * op), (pos.1 as i32 + 1)), // one square "backward" and one right
456                    ((pos.0 as i32 + 1 * op), (pos.1 as i32 - 1)), // one square "backward" and one left
457                    ((pos.0 as i32 + 1 * op), (pos.1 as i32)),     // one square "backward"
458                    ((pos.0 as i32 - 1 * op), (pos.1 as i32)),     // one square "forward"
459                    ((pos.0 as i32), (pos.1 as i32 + 1)),          // one square "right"
460                    ((pos.0 as i32), (pos.1 as i32 - 1)),          // one square "left"
461                ];
462
463                // TODO: check that any of the speicifed moves causes an enemy piece to be able to attack the king
464
465                // check for each position that it is on the board and that it is either empty or occupied by an enemy piece
466                for pos in positions.iter() {
467                    if pos.0 < 8
468                        && pos.0 >= 0
469                        && pos.1 < 8
470                        && pos.1 >= 0
471                        && (self.board[pos.0 as usize][pos.1 as usize].is_none()
472                            || self.board[pos.0 as usize][pos.1 as usize].unwrap().color
473                                != piece.unwrap().color)
474                    {
475                        vec.push(self.index_to_pos((pos.0 as usize, pos.1 as usize)));
476                    }
477                }
478
479                return Some(vec);
480            }
481            None => return None,
482        }
483    }
484
485    /// Converts a string position on the board to a tuple of the row and column (index for 2d array)
486    /// # Arguments
487    /// * `pos` - A string representing the position on the board
488    /// # Returns
489    /// * A tuple of the row and column (index for 2d array)
490    fn pos_to_index(&self, pos: String) -> (usize, usize) {
491        // convert pos to lowercase non-borrowed string and then chars
492        let pos = pos.to_lowercase();
493        let mut chars = pos.chars();
494
495        // when using "as usize", A will be 97, B will be 98 and so on ...
496        // meaning if we subtract 97 we will get the correct index
497        let y = chars.next().unwrap() as usize - 97;
498
499        // same here, but instead of subtracting 97 we subtract 49
500        // since 49 is the ascii value of 1
501        // our array increases index from top to bottom, so we need to
502        // include "7 -" since chess uses increasing from bottom to top
503        let x = 7 - (chars.next().unwrap() as usize - 49);
504
505        (x, y)
506    }
507
508    // convert index in 2d array to two letter position
509    fn index_to_pos(&self, index: (usize, usize)) -> String {
510        // basically the reverse of pos_to_index
511        // we convert the index to "ascii value" and then to char
512        // we use as u8 since usize cannot be converted to char directly
513        let y = (index.1 + 97) as u8 as char;
514        let x = (7 - index.0 + 49) as u8 as char;
515        format!("{}{}", y.to_uppercase(), x)
516    }
517}
518
519/// Implement print routine for Game.
520///
521/// Output example:
522/// |:----------------------:|
523/// | R  Kn B  K  Q  B  Kn R |
524/// | P  P  P  P  P  P  P  P |
525/// | *  *  *  *  *  *  *  * |
526/// | *  *  *  *  *  *  *  * |
527/// | *  *  *  *  *  *  *  * |
528/// | *  *  *  *  *  *  *  * |
529/// | P  P  P  P  P  P  P  P |
530/// | R  Kn B  K  Q  B  Kn R |
531/// |:----------------------:|
532impl fmt::Debug for Game {
533    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
534        /* build board representation string */
535        let mut board = String::new();
536
537        // add top border
538        board.push_str("\n|:----------------------:|");
539
540        // iterate over board and print each piece as letter representation
541        for row in self.board.iter() {
542            board.push_str("\n|");
543            for piece in row.iter() {
544                match piece {
545                    Some(p) => board.push_str(&format!(" {}", p)),
546                    None => board.push_str(" * "),
547                }
548            }
549            board.push_str("|");
550        }
551
552        // add bottom border
553        board.push_str("\n|:----------------------:|");
554
555        write!(f, "{}", board)
556    }
557}
558
559// --------------------------
560// ######### TESTS ##########
561// --------------------------
562
563#[cfg(test)]
564mod tests {
565    use super::Game;
566    use super::GameState;
567
568    // check test framework
569    #[test]
570    fn it_works() {
571        assert_eq!(2 + 2, 4);
572    }
573
574    // example test
575    // check that game state is in progress after initialisation
576    #[test]
577    fn game_in_progress_after_init() {
578        let game = Game::new();
579
580        println!("{:?}", game);
581
582        assert_eq!(game.get_game_state(), GameState::InProgress);
583    }
584
585    // test converting pos to index
586    #[test]
587    fn convert_pos_to_index() {
588        let game = Game::new();
589
590        assert_eq!(game.pos_to_index("a1".to_string()), (7, 0));
591        assert_eq!(game.pos_to_index("B1".to_string()), (7, 1));
592        assert_eq!(game.pos_to_index("A8".to_string()), (0, 0));
593        assert_eq!(game.pos_to_index("H8".to_string()), (0, 7));
594        assert_eq!(game.pos_to_index("H1".to_string()), (7, 7));
595    }
596
597    // test index to pos
598    #[test]
599    fn convert_index_to_pos() {
600        let game = Game::new();
601
602        assert_eq!(game.index_to_pos((0, 0)), "A8");
603        assert_eq!(game.index_to_pos((7, 0)), "A1");
604        assert_eq!(game.index_to_pos((0, 7)), "H8");
605        assert_eq!(game.index_to_pos((7, 7)), "H1");
606    }
607
608    // test some pawn
609    #[test]
610    fn test_pawn_moves() {
611        let mut game = Game::new();
612
613        // test pawn moves
614        // try white pawn
615        assert_eq!(
616            game.get_possible_moves("D2".to_string()).unwrap().sort(),
617            vec!["D3".to_string(), "D4".to_string(),].sort()
618        );
619        // try black pawn
620        assert_eq!(
621            game.get_possible_moves("D7".to_string()).unwrap().sort(),
622            vec!["D6".to_string(), "D5".to_string(),].sort()
623        );
624
625        // try white pawn at the very left
626        assert_eq!(
627            game.get_possible_moves("D2".to_string()).unwrap().sort(),
628            vec!["D3".to_string(), "D4".to_string(),].sort()
629        );
630
631        // try moving D2 pawn to D4
632        assert_eq!(
633            game.make_move("D2".to_string(), "D4".to_string()),
634            Some(GameState::InProgress)
635        );
636
637        println!("{:?}", game);
638
639        // check that we have right moves for this newly moved pawn
640        assert_eq!(
641            game.get_possible_moves("D4".to_string()).unwrap().sort(),
642            vec!["D5".to_string(), "D3".to_string(),].sort()
643        );
644        // then move a black pawn down, C7 to C5
645        assert_eq!(
646            game.make_move("C7".to_string(), "C5".to_string()),
647            Some(GameState::InProgress)
648        );
649        println!("{:?}", game);
650
651        // now check that the white pawn has right moves, that it can attack the black pawn
652        assert_eq!(
653            game.get_possible_moves("D4".to_string()).unwrap().sort(),
654            vec!["D5".to_string(), "D3".to_string(), "C5".to_string()].sort()
655        );
656        // then attack the black pawn
657        assert_eq!(
658            game.make_move("D4".to_string(), "C5".to_string()),
659            Some(GameState::InProgress)
660        );
661        println!("{:?}", game);
662    }
663    // test some knight moves
664    #[test]
665    fn test_knight_moves() {
666        let mut game = Game::new();
667
668        println!("{:?}", game);
669
670        assert_eq!(
671            game.get_possible_moves("B1".to_string()).unwrap().sort(),
672            vec!["A3".to_string(), "C3".to_string(),].sort()
673        );
674        // move B1 to C3
675        assert_eq!(
676            game.make_move("B1".to_string(), "C3".to_string()),
677            Some(GameState::InProgress)
678        );
679        assert_eq!(
680            game.get_possible_moves("C3".to_string()).unwrap().sort(),
681            vec![
682                "B5".to_string(),
683                "D5".to_string(),
684                "A4".to_string(),
685                "E4".to_string(),
686            ]
687            .sort()
688        );
689        println!("{:?}", game);
690        // move B7 to B5
691        assert_eq!(
692            game.make_move("B7".to_string(), "B5".to_string()),
693            Some(GameState::InProgress)
694        );
695        println!("{:?}", game);
696        // get the pawn with the knight by moving it to B5
697        assert_eq!(
698            game.make_move("C3".to_string(), "B5".to_string()),
699            Some(GameState::InProgress)
700        );
701        println!("{:?}", game);
702    }
703
704    // test some king moves
705    #[test]
706    fn test_king_moves() {
707        use super::Color;
708        use super::Piece;
709        use super::PieceType;
710
711        let mut game = Game::new();
712
713        // assert that king cannot move
714        assert_eq!(game.get_possible_moves("E1".to_string()), Some(vec![]));
715
716        // create fake king in middle of board
717        game.board[3][3] = Some(Piece {
718            piece: PieceType::King,
719            color: Color::White,
720            untouched: true,
721        });
722
723        // assert that this newly created (fake) king can move only one square in any direction
724        assert_eq!(
725            game.get_possible_moves("D5".to_string()).unwrap().sort(),
726            vec![
727                "D6".to_string(),
728                "D4".to_string(),
729                "C6".to_string(),
730                "C5".to_string(),
731                "C4".to_string(),
732                "E6".to_string(),
733                "E5".to_string(),
734                "E4".to_string(),
735            ]
736            .sort()
737        );
738
739        println!("{:?}", game);
740    }
741}