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}