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