Skip to main content

chess_lab/variants/
chess960.rs

1use rand::Rng;
2
3use crate::{
4    core::{Color, GameStatus, Move, Piece, Position, Variant, VariantBuilder},
5    errors::{Chess960SPIDError, FenError, MoveError, PGNError},
6    logic::Game,
7    parsing::{
8        fen::{get_fen_from_chess960_spid, get_minified_fen},
9        pgn::{parse_multiple_pgn, parse_pgn},
10    },
11    utils::os::{read_file, write_file},
12};
13
14/// Chess960 is a variant of chess that uses the same rules as [standard chess](crate::variants::StandardChess), but the starting position of the pieces is randomized.
15///
16/// # Attributes
17/// * `game` - The [Game] struct that contains the current state of the game
18///
19#[derive(Debug, Clone)]
20pub struct Chess960 {
21    /// The [Game] struct that contains the current state of the game
22    game: Game,
23}
24
25impl Chess960 {
26    pub fn from_spid(spid: u16) -> Result<Chess960, Chess960SPIDError> {
27        let fen = get_fen_from_chess960_spid(spid)?;
28        let mut game = Game::from_fen(fen.as_str()).unwrap();
29        game.history.variant = Some("Chess960".to_string());
30        Ok(Chess960 { game })
31    }
32}
33
34impl Default for Chess960 {
35    /// Generates a random starting position for the pieces
36    ///
37    /// # Returns
38    /// A [Chess960] struct with a random starting position
39    ///
40    /// # Example
41    /// ```
42    /// # use chess_lab::variants::Chess960;
43    /// let variant = Chess960::default();
44    /// ```
45    ///
46    fn default() -> Chess960 {
47        let spid: u16 = rand::thread_rng().gen_range(0..960);
48
49        Chess960::from_spid(spid).unwrap()
50    }
51}
52
53impl VariantBuilder for Chess960 {
54    /// Returns the name of the [Variant]
55    ///
56    /// # Returns
57    /// A string with the name of the [Variant]
58    ///
59    /// # Example
60    /// ```
61    /// # use chess_lab::core::VariantBuilder;
62    /// # use chess_lab::variants::Chess960;
63    /// let name = Chess960::name();
64    /// assert_eq!(name, "Chess960");
65    /// ```
66    ///
67    fn name() -> &'static str {
68        "Chess960"
69    }
70
71    /// Returns a new instance of the variant from a [Game] struct
72    ///
73    /// # Arguments
74    /// * `game` - The [Game] struct that contains the current state of the game
75    ///
76    /// # Returns
77    /// A Chess960 struct with the game state
78    ///
79    /// # Example
80    /// ```
81    /// # use chess_lab::core::VariantBuilder;
82    /// # use chess_lab::variants::Chess960;
83    /// use chess_lab::logic::Game;
84    ///
85    /// let game = Game::default();
86    /// let variant = Chess960::new(game);
87    /// ```
88    ///
89    fn new(game: Game) -> Chess960 {
90        Chess960 { game }
91    }
92
93    /// Returns a new instance of the variant from a FEN string
94    ///
95    /// # Arguments
96    /// * `fen` - The FEN string that represents the game state
97    ///
98    /// # Returns
99    /// A `Result<Chess960, FenError>` object
100    /// * `Ok(Chess960)` - A [Chess960] struct with the game state
101    /// * `Err(FenError)` - An error that indicates that the FEN string is invalid
102    ///
103    /// # Example
104    /// ```
105    /// # use chess_lab::core::VariantBuilder;
106    /// # use chess_lab::variants::Chess960;
107    /// let fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
108    /// let variant = Chess960::from_fen(fen).unwrap();
109    /// ```
110    ///
111    fn from_fen(fen: &str) -> Result<Chess960, FenError> {
112        Ok(Chess960 {
113            game: Game::from_fen(fen)?,
114        })
115    }
116
117    /// Returns a new instance of the variant from a PGN string
118    ///
119    /// # Arguments
120    /// * `pgn` - The PGN string that represents the game state
121    ///
122    /// # Returns
123    /// A `Result<Chess960, PgnError>` object
124    /// * `Ok(Chess960)` - A [Chess960] struct with the game state
125    /// * `Err(PgnError)` - An error that indicates that the PGN string is invalid
126    ///
127    /// # Example
128    /// ```
129    /// # use chess_lab::core::VariantBuilder;
130    /// # use chess_lab::variants::Chess960;
131    /// let pgn = "[Variant \"Chess960\"]\n1. e4 e5 2. Nf3 Nc6";
132    /// let variant = Chess960::from_pgn(pgn).unwrap();
133    /// ```
134    ///
135    fn from_pgn(pgn: &str) -> Result<Chess960, PGNError> {
136        parse_pgn(pgn)
137    }
138
139    /// Loads a new instance of the variant from a PGN file
140    ///
141    /// # Arguments
142    /// * `path` - The path to the PGN file
143    ///
144    /// # Returns
145    /// A `Result<Chess960, PgnError>` object
146    /// * `Ok(Chess960)` - A Chess960 struct with the game state
147    /// * `Err(PgnError)` - An error that indicates that the PGN file is invalid
148    ///
149    /// # Example
150    /// ```
151    /// # use chess_lab::core::VariantBuilder;
152    /// # use chess_lab::variants::Chess960;
153    /// let path = "data/chess960/ex1.pgn";
154    /// let variant = Chess960::load(path).unwrap();
155    /// ```
156    ///
157    fn load(path: &str) -> Result<Chess960, PGNError> {
158        let pgn = read_file(path)?;
159        Chess960::from_pgn(&pgn)
160    }
161
162    /// Loads all the instances of the variant from a PGN file
163    ///
164    /// # Arguments
165    /// * `path` - The path to the PGN file
166    ///
167    /// # Returns
168    /// A `Result<Vec<Chess960>, PgnError>` object
169    /// * `Ok(Vec<Chess960>)` - A vector with all the Chess960 structs with the game state
170    /// * `Err(PgnError)` - An error that indicates that the PGN file is invalid
171    ///
172    /// # Example
173    /// ```
174    /// # use chess_lab::core::VariantBuilder;
175    /// # use chess_lab::variants::Chess960;
176    /// let path = "data/chess960/ex2.pgn";
177    /// let variants = Chess960::load_all(path).unwrap();
178    /// ```
179    ///
180    fn load_all(path: &str) -> Result<Vec<Chess960>, PGNError> {
181        let pgn = read_file(path)?;
182        parse_multiple_pgn(&pgn)
183    }
184}
185
186impl Variant for Chess960 {
187    /// Moves a piece on the board
188    ///
189    /// # Arguments
190    /// * `move_str` - The move string that represents the move to be made
191    ///
192    /// # Returns
193    /// A `Result<GameStatus, MoveError>` object
194    /// * `Ok(GameStatus)` - The status of the game after the move
195    /// * `Err(MoveError)` - An error that indicates that the move is invalid
196    ///
197    /// # Example
198    /// ```
199    /// # use chess_lab::core::Variant;
200    /// # use chess_lab::variants::Chess960;
201    /// let mut variant = Chess960::default();
202    /// variant.move_piece("e4").unwrap();
203    /// ```
204    ///
205    fn move_piece(&mut self, move_str: &str) -> Result<GameStatus, MoveError> {
206        self.game.move_piece(move_str)
207    }
208
209    /// Undoes the last [Move] made
210    ///
211    /// # Example
212    /// ```
213    /// # use chess_lab::core::Variant;
214    /// # use chess_lab::variants::Chess960;
215    /// let mut variant = Chess960::default();
216    /// variant.move_piece("e4").unwrap();
217    /// variant.undo();
218    /// ```
219    ///
220    fn undo(&mut self) {
221        self.game.undo()
222    }
223
224    /// Redoes the last [Move] that was undone
225    ///
226    /// # Example
227    /// ```
228    /// # use chess_lab::core::Variant;
229    /// # use chess_lab::variants::Chess960;
230    /// let mut variant = Chess960::default();
231    /// variant.move_piece("e4").unwrap();
232    /// variant.undo();
233    /// variant.redo();
234    /// ```
235    ///
236    fn redo(&mut self) {
237        self.game.redo()
238    }
239
240    /// Returns the PGN string of the game
241    ///
242    /// # Returns
243    /// A string with the PGN of the game
244    ///
245    /// # Example
246    /// ```
247    /// # use chess_lab::core::Variant;
248    /// # use chess_lab::variants::Chess960;
249    /// let variant = Chess960::default();
250    /// let pgn = variant.pgn();
251    /// ```
252    ///
253    fn pgn(&self) -> String {
254        self.game.pgn()
255    }
256
257    /// Returns the FEN string of the [Game]
258    ///
259    /// # Returns
260    /// A string with the FEN of the [Game]
261    ///
262    /// # Example
263    /// ```
264    /// # use chess_lab::core::Variant;
265    /// # use chess_lab::variants::Chess960;
266    ///
267    /// let variant = Chess960::default();
268    /// let fen = variant.fen();
269    /// ```
270    ///
271    fn fen(&self) -> String {
272        self.game.fen()
273    }
274
275    /// Returns the piece at a given position
276    ///
277    /// # Arguments
278    /// * `pos` - The position to get the piece from
279    ///
280    /// # Returns
281    /// The piece at the given position, if there is one
282    ///
283    /// # Example
284    /// ```
285    /// # use chess_lab::core::Variant;
286    /// # use chess_lab::variants::Chess960;
287    /// use chess_lab::core::Position;
288    ///
289    /// let variant = Chess960::default();
290    /// let piece = variant.get_piece_at(Position::from_string("e2").unwrap());
291    /// assert!(piece.is_some());
292    /// assert_eq!(piece.unwrap().to_string(), "P");
293    /// ```
294    ///
295    fn get_piece_at(&self, pos: Position) -> Option<Piece> {
296        self.game.get_piece_at(pos)
297    }
298
299    /// Returns the legal moves of a piece at a given position
300    ///
301    /// # Arguments
302    /// * `pos` - The position to get the legal moves from
303    ///
304    /// # Returns
305    /// A vector with the legal moves of the piece at the given position
306    ///
307    /// # Example
308    /// ```
309    /// # use chess_lab::core::Variant;
310    /// # use chess_lab::variants::Chess960;
311    /// use chess_lab::core::Position;
312    ///
313    /// let variant = Chess960::default();
314    /// let legal_moves = variant.get_legal_moves(Position::from_string("e2").unwrap());
315    /// assert!(legal_moves.iter().any(|m| m.to_string() == "e4"));
316    /// ```
317    ///
318    fn get_legal_moves(&self, pos: Position) -> Vec<Move> {
319        self.game.get_legal_moves(pos)
320    }
321
322    /// Saves the PGN string of the [Game] to a file
323    ///
324    /// # Arguments
325    /// * `path` - The path to the file
326    /// * `overwrite` - A boolean that indicates if the file should be overwritten
327    ///
328    /// # Returns
329    /// A `Result<(), std::io::Error>` object
330    /// * `Ok(())` - The PGN was saved successfully
331    /// * `Err(std::io::Error)` - An error that indicates that the PGN could not be saved
332    ///
333    /// # Example
334    /// ```
335    /// # use chess_lab::core::Variant;
336    /// # use chess_lab::variants::Chess960;
337    ///
338    /// let variant = Chess960::default();
339    /// variant.save("data/chess960/ex.pgn", true).unwrap();
340    /// # std::fs::remove_file("data/chess960/ex.pgn").unwrap();
341    /// ```
342    ///
343    fn save(&self, path: &str, overwrite: bool) -> Result<(), std::io::Error> {
344        write_file(path, self.pgn().as_str(), !overwrite)?;
345        Ok(())
346    }
347
348    /// Resigns the [Game] for a [Color]
349    ///
350    /// # Arguments
351    /// * `color` - The color that resigns
352    ///
353    /// # Example
354    /// ```
355    /// # use chess_lab::core::Variant;
356    /// # use chess_lab::variants::Chess960;
357    /// use chess_lab::core::Color;
358    ///
359    /// let mut variant = Chess960::default();
360    /// variant.resign(Color::White);
361    /// ```
362    ///
363    fn resign(&mut self, color: Color) {
364        self.game.resign(color)
365    }
366
367    /// Sets the [Game] as a draw
368    ///
369    /// # Example
370    /// ```
371    /// # use chess_lab::core::Variant;
372    /// # use chess_lab::variants::Chess960;
373    /// let mut variant = Chess960::default();
374    /// variant.draw();
375    /// ```
376    ///
377    fn draw(&mut self) {
378        self.game.draw_by_agreement()
379    }
380
381    /// Sets the game lost in time for a [Color]
382    ///
383    /// # Arguments
384    /// * `color` - The [Color] that lost in time
385    ///
386    /// # Example
387    /// ```
388    /// # use chess_lab::core::Variant;
389    /// # use chess_lab::variants::Chess960;
390    /// use chess_lab::core::Color;
391    ///
392    /// let mut variant = Chess960::default();
393    /// variant.lost_on_time(Color::White);
394    /// ```
395    ///
396    fn lost_on_time(&mut self, color: Color) {
397        self.game.lost_on_time(color)
398    }
399
400    /// Returns the minified FEN string of the [Game]
401    ///
402    /// # Returns
403    /// A string with the minified FEN of the [Game]
404    ///
405    /// # Example
406    /// ```
407    /// # use chess_lab::core::Variant;
408    /// # use chess_lab::variants::Chess960;
409    /// let variant = Chess960::default();
410    /// let minified_fen = variant.get_minified_fen();
411    /// ```
412    ///
413    fn get_minified_fen(&self) -> String {
414        get_minified_fen(&self.fen())
415    }
416
417    /// Returns the last [Move] of the [Game]
418    ///
419    /// # Returns
420    /// The last [Move] of the [Game], if there is one
421    ///
422    /// # Examples
423    /// ```
424    /// # use chess_lab::core::Variant;
425    /// # use chess_lab::variants::StandardChess;
426    /// let mut game = StandardChess::default();
427    /// game.move_piece("e4").unwrap();
428    /// let last_move = game.get_last_move();
429    /// assert_eq!(last_move.unwrap().to_string(), "e4");
430    /// ```
431    ///
432    fn get_last_move(&self) -> Option<crate::core::Move> {
433        self.game.get_last_move()
434    }
435
436    /// Returns whether it is white's turn to move
437    ///
438    /// # Returns
439    /// Whether it is white's turn to move
440    ///
441    /// # Examples
442    /// ```
443    /// # use chess_lab::core::Variant;
444    /// # use chess_lab::variants::Chess960;
445    /// let game = Chess960::default();
446    /// let color = game.is_white_turn();
447    /// ```
448    ///
449    fn is_white_turn(&self) -> bool {
450        self.game.is_white_turn
451    }
452
453    /// Returns the halfmove clock of the [Game]
454    ///
455    /// # Returns
456    /// The halfmove clock of the [Game]
457    ///
458    /// # Examples
459    /// ```
460    /// # use chess_lab::core::Variant;
461    /// # use chess_lab::variants::Chess960;
462    /// let game = Chess960::default();
463    /// let halfmove_clock = game.get_halfmove_clock();
464    /// ```
465    ///
466    fn get_halfmove_clock(&self) -> u32 {
467        self.game.halfmove_clock
468    }
469
470    /// Returns the fullmove number of the [Game]
471    ///
472    /// # Returns
473    /// The fullmove number of the [Game]
474    ///
475    /// # Examples
476    /// ```
477    /// # use chess_lab::core::Variant;
478    /// # use chess_lab::variants::Chess960;
479    /// let game = Chess960::default();
480    /// let fullmove_number = game.get_fullmove_number();
481    /// ```
482    ///
483    fn get_fullmove_number(&self) -> u32 {
484        self.game.fullmove_number
485    }
486
487    /// Returns the current castling rights of the [Game]
488    ///
489    /// # Returns
490    /// The current castling rights of the [Game]
491    ///
492    /// # Examples
493    /// ```
494    /// # use chess_lab::core::Variant;
495    /// # use chess_lab::variants::Chess960;
496    /// let game = Chess960::default();
497    /// let castling_rights = game.get_castling_rights();
498    /// ```
499    ///
500    fn get_castling_rights(&self) -> String {
501        let mut castling_rights = String::new();
502
503        if self.game.castling_rights == 0 {
504            castling_rights.push('-');
505        } else {
506            if self.game.castling_rights & 0b1000 != 0 {
507                castling_rights.push('K');
508            }
509            if self.game.castling_rights & 0b0100 != 0 {
510                castling_rights.push('Q');
511            }
512            if self.game.castling_rights & 0b0010 != 0 {
513                castling_rights.push('k');
514            }
515            if self.game.castling_rights & 0b0001 != 0 {
516                castling_rights.push('q');
517            }
518        }
519        castling_rights
520    }
521
522    /// Returns the en passant square of the [Game]
523    ///
524    /// # Returns
525    /// The en passant square of the [Game]
526    ///
527    /// # Examples
528    /// ```
529    /// # use chess_lab::core::Variant;
530    /// # use chess_lab::variants::Chess960;
531    /// let game = Chess960::default();
532    /// let en_passant = game.get_en_passant();
533    /// ```
534    ///
535    fn get_en_passant(&self) -> Option<Position> {
536        self.game.en_passant
537    }
538
539    /// Returns the starting FEN of the [Game]
540    ///
541    /// # Returns
542    /// A copy of the starting FEN of the [Game]
543    ///
544    /// # Examples
545    /// ```
546    /// # use chess_lab::core::Variant;
547    /// # use chess_lab::variants::Chess960;
548    /// let game = Chess960::default();
549    /// let starting_fen = game.get_starting_fen();
550    /// ```
551    ///
552    fn get_starting_fen(&self) -> String {
553        self.game.starting_fen.clone()
554    }
555
556    /// Returns the status of the [Game]
557    ///
558    /// # Returns
559    /// The status of the [Game]
560    ///
561    /// # Examples
562    /// ```
563    /// # use chess_lab::core::Variant;
564    /// # use chess_lab::variants::Chess960;
565    ///
566    /// let game = Chess960::default();
567    /// let status = game.get_status();
568    /// ```
569    ///
570    fn get_status(&self) -> GameStatus {
571        self.game.status
572    }
573}
574
575#[cfg(test)]
576mod tests {
577    use crate::core::WinReason;
578
579    use super::*;
580
581    #[test]
582    fn test_standard_chess_name() {
583        assert_eq!(Chess960::name(), "Chess960");
584    }
585
586    #[test]
587    fn test_default() {
588        let variant = Chess960::default();
589
590        let minified_fen = variant.get_minified_fen();
591
592        let parts: Vec<&str> = minified_fen.split('/').collect();
593
594        assert!(parts[0].chars().all(|c| c.is_lowercase()));
595        assert!(parts[7].chars().all(|c| c.is_uppercase()));
596        assert_eq!(parts[0], parts[7].to_lowercase());
597
598        for piece in ['r', 'n', 'b', 'q', 'k'] {
599            let count = parts[0].chars().filter(|&c| c == piece).count();
600            match piece {
601                'r' => assert_eq!(count, 2),
602                'n' => assert_eq!(count, 2),
603                'b' => assert_eq!(count, 2),
604                'q' => assert_eq!(count, 1),
605                'k' => assert_eq!(count, 1),
606                _ => (),
607            }
608        }
609
610        let mut castle_chars = vec!['r', 'k', 'r'];
611        for char in parts[0].chars() {
612            if !castle_chars.is_empty() && char == castle_chars[0] {
613                castle_chars.remove(0);
614            }
615        }
616        assert!(castle_chars.is_empty());
617    }
618
619    #[test]
620    fn test_new() {
621        let game = Game::default();
622        let variant = Chess960::new(game.clone());
623        assert_eq!(variant.fen(), game.fen());
624    }
625
626    #[test]
627    fn test_from_fen() {
628        let fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
629        let variant = Chess960::from_fen(fen).unwrap();
630        assert_eq!(variant.fen(), fen);
631    }
632
633    #[test]
634    fn test_from_pgn() {
635        let pgn = "[Variant \"Chess960\"]\n
636            [FEN \"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1\"]\n
637            1. e4 e5 2. Nf3 Nc6 3. Bb5 a6";
638        let variant = Chess960::from_pgn(pgn).unwrap();
639        assert!(variant.pgn().contains("1. e4 e5 2. Nf3 Nc6 3. Bb5 a6"));
640    }
641
642    #[test]
643    fn test_load() {
644        let path = "data/chess960/ex1.pgn";
645        let variant = Chess960::load(path).unwrap();
646        assert!(variant.pgn().contains("1. e4 c6 2. d4 d5 3. exd5 cxd5"));
647    }
648
649    #[test]
650    fn test_load_all() {
651        let path = "data/chess960/ex2.pgn";
652        let variants = Chess960::load_all(path).unwrap();
653        assert_eq!(variants.len(), 3);
654    }
655
656    #[test]
657    fn test_move_piece() {
658        let mut variant =
659            Chess960::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
660        let status = variant.move_piece("e4").unwrap();
661        assert_eq!(status, GameStatus::InProgress);
662        assert_eq!(
663            variant.fen(),
664            "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
665        );
666    }
667
668    #[test]
669    fn test_get_piece_at() {
670        let variant =
671            Chess960::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
672        let piece = variant.get_piece_at(Position::from_string("e2").unwrap());
673        assert!(piece.is_some());
674        assert_eq!(piece.unwrap().to_string(), "P");
675    }
676
677    #[test]
678    fn test_get_legal_moves() {
679        let variant =
680            Chess960::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
681        let legal_moves = variant.get_legal_moves(Position::from_string("e2").unwrap());
682        assert!(legal_moves.iter().any(|m| m.to_string() == "e4"));
683    }
684
685    #[test]
686    fn test_undo_redo() {
687        let mut variant =
688            Chess960::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
689        variant.move_piece("e4").unwrap();
690        variant.undo();
691        assert_eq!(
692            variant.fen(),
693            "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
694        );
695        variant.redo();
696        assert_eq!(
697            variant.fen(),
698            "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
699        );
700    }
701
702    #[test]
703    fn test_save() {
704        let mut variant = Chess960::default();
705        let path = "data/chess960/test_save.pgn";
706
707        variant.move_piece("e4").unwrap();
708        variant.save(path, true).unwrap();
709
710        let loaded_variant = Chess960::load(path).unwrap();
711        assert_eq!(variant.fen(), loaded_variant.fen());
712
713        // Clean up
714        std::fs::remove_file(path).unwrap();
715    }
716
717    #[test]
718    fn test_resign() {
719        let mut variant = Chess960::default();
720
721        variant.resign(Color::White);
722        assert_eq!(
723            variant.get_status(),
724            GameStatus::BlackWins(WinReason::Resignation)
725        );
726    }
727
728    #[test]
729    fn test_draw() {
730        let mut variant = Chess960::default();
731
732        variant.draw();
733        assert_eq!(
734            variant.get_status(),
735            GameStatus::Draw(crate::core::DrawReason::Agreement)
736        );
737    }
738
739    #[test]
740    fn test_lost_on_time() {
741        let mut variant = Chess960::default();
742
743        variant.lost_on_time(Color::Black);
744        assert_eq!(variant.get_status(), GameStatus::WhiteWins(WinReason::Time));
745    }
746
747    #[test]
748    fn test_minified_fen() {
749        let variant =
750            Chess960::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
751        let minified_fen = variant.get_minified_fen();
752        assert_eq!(minified_fen, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR");
753    }
754
755    #[test]
756    fn test_get_last_move() {
757        let mut variant = Chess960::default();
758        assert_eq!(variant.get_last_move(), None);
759
760        variant.move_piece("e4").unwrap();
761        let last_move = variant.get_last_move();
762        assert!(last_move.is_some());
763        assert_eq!(last_move.unwrap().to_string(), "e4");
764    }
765
766    #[test]
767    fn test_is_white_turn() {
768        let mut variant = Chess960::default();
769        assert!(variant.is_white_turn());
770
771        variant.move_piece("e4").unwrap();
772        assert!(!variant.is_white_turn());
773    }
774
775    #[test]
776    fn test_get_castling_rights() {
777        let mut variant = Chess960::default();
778        let castling_rights = variant.get_castling_rights();
779        assert_eq!(castling_rights, "KQkq");
780
781        variant =
782            Chess960::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1").unwrap();
783        let castling_rights = variant.get_castling_rights();
784        assert_eq!(castling_rights, "-");
785    }
786
787    #[test]
788    fn test_get_en_passant() {
789        let mut variant = Chess960::default();
790        assert_eq!(variant.get_en_passant(), None);
791
792        variant.move_piece("e4").unwrap();
793        variant.move_piece("d5").unwrap();
794        variant.move_piece("e5").unwrap();
795        variant.move_piece("f5").unwrap();
796        assert_eq!(
797            variant.get_en_passant().unwrap(),
798            Position::new(5, 5).unwrap()
799        );
800    }
801
802    #[test]
803    fn test_get_halfmove_clock() {
804        let variant = Chess960::default();
805        assert_eq!(variant.get_halfmove_clock(), 0);
806    }
807
808    #[test]
809    fn test_get_fullmove_number() {
810        let variant = Chess960::default();
811        assert_eq!(variant.get_fullmove_number(), 1);
812    }
813
814    #[test]
815    fn test_get_starting_fen() {
816        let variant = Chess960::default();
817        let starting_fen = variant.get_starting_fen();
818
819        assert!(starting_fen.contains("w KQkq - 0 1"));
820    }
821
822    #[test]
823    fn test_get_status() {
824        let variant = Chess960::default();
825        assert_eq!(variant.get_status(), GameStatus::InProgress);
826    }
827}