1mod board;
2mod castling;
3mod constants;
4mod errors;
5mod history;
6mod piece;
7mod play_move;
8mod square;
9mod utils;
10
11use board::*;
12use castling::*;
13use errors::*;
14use history::*;
15use piece::*;
16use play_move::*;
17use std::collections::HashMap;
18use std::ops::Range;
19use utils::*;
20
21pub use constants::{Color, BOARD_MAP, FILES};
22pub use piece::{Piece, PieceType};
23pub use play_move::Move;
24pub use square::{Square, SquareCoordinate, SquareCoordinateExt};
25pub use utils::{
26 convert_algebraic_notation_to_index, convert_index_to_algebraic_notation, is_valid,
27};
28
29use self::constants::{
30 BISHOP_DELTAS, BLACK_PAWN_DELTAS, BOARD_SIZE, COLOR_MASK, KING_DELTAS, KNIGHT_DELTAS,
31 QUEEN_DELTAS, ROOK_DELTAS, WHITE_PAWN_DELTAS,
32};
33
34#[derive(Clone, Debug)]
35pub struct Kings {
36 pub white: Option<SquareCoordinate>,
37 pub black: Option<SquareCoordinate>,
38}
39
40pub struct Chess {
41 pub board: Board,
42 pub turn: Color,
43
44 pub kings: Kings,
46 pub castling_rights: CastlingRights,
47 pub history: MoveHistory,
48 pub white_captures: Vec<Piece>,
49 pub black_captures: Vec<Piece>,
50
51 unique_positions: HashMap<String, u8>,
54 pub half_moves: u8,
55 pub full_moves: u8,
56 pub en_passant_sq: Option<SquareCoordinate>,
57}
58
59impl Chess {
60 pub fn new() -> Self {
61 Self {
62 board: Board::new(),
63 turn: Color::WHITE,
64 kings: Kings {
65 white: None,
66 black: None,
67 },
68 history: MoveHistory::new(),
69 castling_rights: CastlingRights::new(),
70 en_passant_sq: None,
71 white_captures: vec![],
72 black_captures: vec![],
73 full_moves: 0,
74 half_moves: 0,
75 unique_positions: HashMap::new(),
76 }
77 }
78
79 pub fn play_move(&mut self, m: Move) -> ChessResult<()> {
81 self.make_move(m)?;
83 self.change_turn();
84
85 Ok(())
86 }
87
88 pub fn make_move(&mut self, m: Move) -> ChessResult<()> {
89 let m = self.convert_to_internal_move(m)?;
90
91 let history_entry = HistoryEntry {
92 player_move: m.clone(),
93 turn: self.turn,
94 kings: self.kings.clone(),
95 castling_rights: self.castling_rights.clone(),
96 en_passant_sq: self.en_passant_sq,
97 };
98
99 self.half_moves += 1;
100
101 self.set(m.to_sq, m.from_piece.piece_type, m.from_piece.color)?;
102 self.remove(m.from_sq)?;
103
104 if m.move_type == MoveType::Capture {
105 if let Some(to_piece) = m.to_piece {
106 if to_piece.color == Color::BLACK {
107 self.white_captures.push(to_piece);
108 } else {
109 self.black_captures.push(to_piece);
110 }
111 }
112 }
113
114 if m.move_type == MoveType::EnPassantCapture {
115 if let Some(en_passant_sq) = self.en_passant_sq {
116 if m.from_piece.color == Color::WHITE {
117 self.white_captures.push(Piece {
118 piece_type: PieceType::PAWN,
119 color: Color::BLACK,
120 });
121 self.remove(en_passant_sq.below()?)?;
123 } else {
124 self.black_captures.push(Piece {
125 piece_type: PieceType::PAWN,
126 color: Color::WHITE,
127 });
128 self.remove(en_passant_sq.above()?)?;
130 }
131 }
132 }
133
134 if m.move_type == MoveType::EnPassantMove {
135 if m.from_piece.color == Color::WHITE {
136 self.en_passant_sq = Some(m.to_sq.below()?);
137 } else {
138 self.en_passant_sq = Some(m.to_sq.above()?);
139 }
140 } else {
141 self.en_passant_sq = None;
142 }
143
144 if m.move_type == MoveType::CastleKingside {
145 self.remove(m.to_sq.right()?)?;
146 self.set(m.to_sq.left()?, PieceType::ROOK, m.from_piece.color)?;
147 }
148
149 if m.move_type == MoveType::CastleQueenside {
150 self.remove(m.to_sq.subtract(2)?)?;
151 self.set(m.to_sq.right()?, PieceType::ROOK, m.from_piece.color)?;
152 }
153
154 if m.move_type == MoveType::Promotion {
155 let promotion_piece = m
156 .promotion_piece
157 .ok_or(ChessError::UnknownError(format!("yah yeet")))?;
158 self.set(m.to_sq, promotion_piece.piece_type, promotion_piece.color)?;
159 }
160
161 self.history.push(history_entry);
162 self.castling_rights.update(&self.kings, &self.board)?;
163
164 if m.move_type == MoveType::Capture || m.from_piece.piece_type == PieceType::PAWN {
166 self.half_moves = 0;
167 }
168
169 Ok(())
170 }
171
172 pub fn undo_move(&mut self) -> ChessResult<()> {
173 if let Some(old) = self.history.pop() {
174 self.turn = old.turn;
175 self.en_passant_sq = old.en_passant_sq;
176 self.kings = old.kings;
177 self.castling_rights = old.castling_rights;
178
179 let m = old.player_move;
180
181 self.set(m.from_sq, m.from_piece.piece_type, m.from_piece.color)?;
183 self.remove(m.to_sq)?;
184
185 self.half_moves = self.half_moves.saturating_sub(1);
186
187 if old.turn == Color::BLACK {
188 self.full_moves = self.full_moves.saturating_sub(1);
189 }
190
191 if m.move_type == MoveType::Capture || m.move_type == MoveType::EnPassantCapture {
192 if m.from_piece.color == Color::WHITE {
193 self.white_captures.pop();
194 } else {
195 self.black_captures.pop();
196 }
197 }
198
199 if m.move_type == MoveType::EnPassantCapture {
200 if m.from_piece.color == Color::WHITE {
202 self.set(m.to_sq.below()?, PieceType::PAWN, Color::BLACK)?;
203 } else {
204 self.set(m.to_sq.above()?, PieceType::PAWN, Color::WHITE)?;
205 }
206 }
207
208 if m.move_type == MoveType::CastleKingside {
209 self.remove(m.to_sq.left()?)?;
211 self.set(m.to_sq.right()?, PieceType::ROOK, m.from_piece.color)?;
212 }
213
214 if m.move_type == MoveType::CastleQueenside {
215 self.remove(m.to_sq.right()?)?;
217 self.set(m.to_sq.subtract(2)?, PieceType::ROOK, m.from_piece.color)?;
219 }
220
221 if m.move_type == MoveType::Capture || m.to_piece.is_some() {
222 if let Some(to_piece) = m.to_piece {
223 self.set(m.to_sq, to_piece.piece_type, to_piece.color)?;
225 }
226 }
227
228 if m.move_type == MoveType::Capture || m.from_piece.piece_type == PieceType::PAWN {
230 self.half_moves = 0;
231 }
232 }
233
234 Ok(())
235 }
236
237 pub fn moves_for_piece_type() {}
239
240 pub fn moves_for_square(&mut self, sq: SquareCoordinate) -> ChessResult<Vec<Move>> {
242 let piece = self.get(sq)?.ok_or(ChessError::UnknownError(
243 "Can't generate moves for empty square".to_string(),
244 ))?;
245
246 if piece.color != self.turn {
247 return Ok(vec![]);
248 }
249
250 let mut legal_moves = vec![];
251 let pseudo_legal_moves = match piece.piece_type {
252 PieceType::KING => self.king_moves(sq),
253 PieceType::QUEEN => self.sliding_moves(sq, QUEEN_DELTAS.to_vec()),
254 PieceType::ROOK => self.sliding_moves(sq, ROOK_DELTAS.to_vec()),
255 PieceType::BISHOP => self.sliding_moves(sq, BISHOP_DELTAS.to_vec()),
256 PieceType::KNIGHT => self.knight_moves(sq),
257 PieceType::PAWN => self.pawn_moves(sq),
258 }?;
259
260 for _move in pseudo_legal_moves {
261 self.make_move(_move)?;
262 if !self.in_check()? {
263 legal_moves.push(_move);
264 }
265
266 self.undo_move()?;
267 }
268
269 Ok(legal_moves)
270 }
271
272 pub fn moves(&mut self) -> ChessResult<Vec<Move>> {
274 let mut moves = vec![];
275
276 for idx in 0..BOARD_SIZE {
277 if let Ok(idx) = self.board.is_valid(idx) {
278 if let Some(piece) = self.get((idx as u8).to_coordinate())? {
279 if self.is_friendly(piece) {
280 let mut a = self.moves_for_square((idx as u8).to_coordinate())?;
281 moves.append(&mut a);
282 }
283 }
284 }
285 }
286
287 Ok(moves)
288 }
289
290 pub fn in_check(&self) -> ChessResult<bool> {
292 if self.turn == Color::WHITE {
293 if let Some(king) = self.kings.white {
294 return self.is_attacked(king);
295 }
296 } else {
297 if let Some(king) = self.kings.black {
298 return self.is_attacked(king);
299 }
300 }
301 Ok(false)
302 }
303
304 pub fn is_checkmate(&mut self) -> ChessResult<bool> {
305 let mut no_legal_moves = true;
306
307 for idx in 0..BOARD_SIZE {
308 let idx = match self.board.is_valid(idx) {
309 Ok(idx) => idx,
310 Err(_) => continue,
311 } as u8;
312
313 if let Some(piece) = self.get(idx.to_coordinate())? {
314 if self.is_friendly(piece) {
315 let moves = self.moves_for_square(idx.to_coordinate())?;
316
317 if moves.len() > 0 {
318 no_legal_moves = false;
319 }
320 }
321 }
322 }
323
324 Ok(self.in_check()? && no_legal_moves)
325 }
326
327 pub fn is_draw(&mut self) -> ChessResult<bool> {
328 Ok(self.is_stalemate()?
329 || self.is_threefold_repetition()?
330 || self.is_50_moves()?
331 || self.is_insufficient_material()?)
332 }
333
334 pub fn is_stalemate(&mut self) -> ChessResult<bool> {
335 let mut no_legal_moves = true;
336
337 for idx in 0..BOARD_SIZE {
338 let idx = match self.board.is_valid(idx) {
339 Ok(idx) => idx,
340 Err(_) => continue,
341 } as u8;
342
343 if let Some(piece) = self.get(idx.to_coordinate())? {
344 if self.is_friendly(piece) {
345 let moves = self.moves()?;
346
347 if moves.len() > 0 {
348 no_legal_moves = false;
349 }
350 }
351 }
352 }
353
354 Ok(!self.in_check()? && no_legal_moves)
355 }
356
357 pub fn is_insufficient_material(&self) -> ChessResult<bool> {
358 let mut friendly_knights = 0;
359 let mut friendly_bishops = 0;
360 let mut friendly_dark_bishops = 0;
361 let mut friendly_light_bishops = 0;
362 let mut enemy_dark_bishops = 0;
363 let mut enemy_light_bishops = 0;
364 let mut enemy_knights = 0;
365 let mut enemy_bishops = 0;
366
367 for idx in 0..BOARD_SIZE {
368 let idx = match self.board.is_valid(idx) {
369 Ok(idx) => idx,
370 Err(_) => continue,
371 } as u8;
372 let coord = idx.to_coordinate();
373
374 if let Some(piece) = self.get(coord)? {
381 let piece_type = piece.piece_type;
382
383 if piece_type == PieceType::PAWN
384 || piece_type == PieceType::ROOK
385 || piece_type == PieceType::QUEEN
386 {
387 return Ok(false);
388 }
389
390 if self.is_friendly(piece) {
391 if piece_type == PieceType::KNIGHT {
392 friendly_knights += 1;
393 }
394
395 if piece_type == PieceType::BISHOP {
396 if coord.is_dark_sq() {
398 friendly_dark_bishops += 1;
399
400 if enemy_light_bishops >= 1 {
401 return Ok(false);
402 }
403 }
404
405 if coord.is_light_sq() {
407 friendly_light_bishops += 1;
408
409 if enemy_dark_bishops >= 1 {
410 return Ok(false);
411 }
412 }
413
414 friendly_bishops += 1;
415 }
416 } else {
417 if piece_type == PieceType::KNIGHT {
418 enemy_knights += 1;
419 }
420
421 if piece_type == PieceType::BISHOP {
422 if coord.is_dark_sq() {
425 enemy_dark_bishops += 1;
426
427 if friendly_light_bishops >= 1 {
428 return Ok(false);
429 }
430 }
431
432 if coord.is_light_sq() {
434 enemy_light_bishops += 1;
435
436 if friendly_dark_bishops >= 1 {
437 return Ok(false);
438 }
439 }
440
441 enemy_bishops += 1;
442 }
443 }
444 }
445 }
446
447 if friendly_knights == 0
449 && friendly_bishops == 0
450 && enemy_knights == 0
451 && enemy_bishops == 0
452 {
453 return Ok(true);
454 }
455
456 if friendly_knights == 0 && enemy_knights == 0 {
457 if friendly_bishops == 2 || enemy_bishops == 2 {
458 return Ok(false);
459 }
460
461 return Ok(true);
462 }
463
464 if friendly_bishops == 0 && enemy_bishops == 0 {
465 if friendly_knights >= 1 && enemy_knights >= 1 {
466 return Ok(false);
467 }
468
469 return Ok(true);
470 }
471
472 Ok(false)
473 }
474
475 pub fn is_50_moves(&self) -> ChessResult<bool> {
476 Ok(self.half_moves >= 100)
477 }
478
479 pub fn is_threefold_repetition(&self) -> ChessResult<bool> {
480 let fen = self.get_fen()?;
481 let position = fen.split(" ").collect::<Vec<&str>>()[0];
482
483 if let Some(count) = self.unique_positions.get(position) {
484 Ok(*count >= 3)
485 } else {
486 Ok(false)
487 }
488 }
489
490 pub fn is_attacked(&self, from: SquareCoordinate) -> ChessResult<bool> {
492 let sliding_attack_deltas = vec![16, -16, 1, -1, 17, 15, -17, -15];
493 let knight_attack_deltas = vec![14, 31, 18, 33, -14, -31, -18, -33];
494
495 let from_idx = from.to_index();
496 let from_piece = self.get(from)?;
497
498 for delta in sliding_attack_deltas {
499 let mut to = from.to_index() as i16 + delta as i16;
500 loop {
501 if let Ok(_to) = utils::is_valid(to as usize) {
502 let to_sq = (_to as u8).to_coordinate();
503
504 if let Some(attacker) = self.get(to_sq)? {
505 if attacker.color == self.turn {
506 break;
507 }
508
509 if let Some(from_piece) = from_piece {
510 if from_piece.color == attacker.color {
511 break;
512 }
513 }
514
515 let diff = from_idx as i16 - to + 119;
516 let attack_bits_mask = constants::ATTACKS[diff as usize];
517
518 if attack_bits_mask != 0 {
519 if attacker.piece_type == PieceType::PAWN {
520 let with_color =
521 attacker.piece_type.to_value() | attacker.color.to_value();
522
523 if with_color & attack_bits_mask != 0
524 && attacker.color.to_value() == attack_bits_mask & COLOR_MASK
525 {
526 return Ok(true);
527 } else {
528 break;
529 }
530 } else {
531 if (attacker.piece_type.to_value() & attack_bits_mask) != 0 {
532 return Ok(true);
533 } else {
534 break;
535 }
536 }
537 }
538 }
539
540 to += delta as i16;
541 } else {
542 break;
543 }
544 }
545 }
546
547 for delta in knight_attack_deltas {
548 let to = from.to_index() as i16 + delta as i16;
549
550 if let Ok(_to) = utils::is_valid(to as usize) {
551 let to_sq = (_to as u8).to_coordinate();
552
553 if let Some(attacker) = self.get(to_sq)? {
554 if !self.is_friendly(attacker) && attacker.piece_type == PieceType::KNIGHT {
555 return Ok(true);
556 }
557 }
558 }
559 }
560
561 Ok(false)
562 }
563
564 pub fn change_turn(&mut self) {
565 if self.turn == Color::WHITE {
566 self.turn = Color::BLACK
567 } else {
568 self.turn = Color::WHITE
569 }
570 }
571
572 pub fn get(&self, sq: SquareCoordinate) -> ChessResult<Option<Piece>> {
576 Ok(self.board.get(sq)?)
577 }
578
579 pub fn set(
583 &mut self,
584 sq: SquareCoordinate,
585 piece: PieceType,
586 color: Color,
587 ) -> ChessResult<(Piece, usize)> {
588 let s = self.board.set(sq, piece, color)?;
589
590 if piece == PieceType::KING {
591 self.update_kings_positions(sq, color);
592 }
593
594 Ok(s)
595 }
596
597 pub fn remove(&mut self, sq: SquareCoordinate) -> ChessResult<()> {
598 Ok(self.board.remove(sq)?)
599 }
600
601 pub fn get_fen(&self) -> ChessResult<String> {
602 let mut fen = String::from("");
603
604 let turn = self.turn;
605 let en_passant_sq = if let Some(sq) = self.en_passant_sq {
606 sq.to_san()
607 } else {
608 "-".to_string()
609 };
610
611 let half_moves = self.half_moves;
612 let full_moves = self.full_moves;
613
614 let mut castling_rights = String::new();
615 if self.castling_rights.white.kingside {
616 castling_rights.push_str("K");
617 }
618
619 if self.castling_rights.white.queenside {
620 castling_rights.push_str("Q");
621 }
622
623 if self.castling_rights.black.kingside {
624 castling_rights.push_str("k");
625 }
626
627 if self.castling_rights.black.queenside {
628 castling_rights.push_str("q")
629 }
630
631 if castling_rights.is_empty() {
632 castling_rights.push_str("-");
633 }
634
635 let mut empty_square: u8 = 0;
636 for idx in 0..BOARD_SIZE {
637 let idx = match self.board.is_valid(idx) {
638 Ok(idx) => idx,
639 Err(_) => continue,
640 } as u8;
641
642 if let Some(piece) = self.get(idx.to_coordinate())? {
643 if empty_square != 0 {
644 fen.push_str(empty_square.to_string().as_str());
645 empty_square = 0;
646 }
647
648 if piece.color == Color::WHITE {
649 match piece.piece_type {
650 PieceType::PAWN => fen.push_str("P"),
651 PieceType::ROOK => fen.push_str("R"),
652 PieceType::KNIGHT => fen.push_str("N"),
653 PieceType::BISHOP => fen.push_str("B"),
654 PieceType::QUEEN => fen.push_str("Q"),
655 PieceType::KING => fen.push_str("K"),
656 }
658 } else {
659 match piece.piece_type {
660 PieceType::PAWN => fen.push_str("p"),
661 PieceType::ROOK => fen.push_str("r"),
662 PieceType::KNIGHT => fen.push_str("n"),
663 PieceType::BISHOP => fen.push_str("b"),
664 PieceType::QUEEN => fen.push_str("q"),
665 PieceType::KING => fen.push_str("k"),
666 }
668 }
669 } else {
670 empty_square += 1;
671 }
672
673 if (idx + 1) % 8 == 0 {
674 if empty_square != 0 {
675 fen.push_str(empty_square.to_string().as_str());
676 empty_square = 0;
677 }
678
679 if idx != 119 {
681 fen.push('/');
682 }
683 }
684 }
685
686 Ok(vec![
687 fen,
688 turn.to_string(),
689 castling_rights,
690 en_passant_sq,
691 half_moves.to_string(),
692 full_moves.to_string(),
693 ]
694 .join(" "))
695 }
696
697 pub fn load_fen(&mut self, fen: String) -> ChessResult<()> {
698 let fen_parts: Vec<&str> = fen.split(" ").collect();
699
700 let ranks: Vec<&str> = fen_parts[0].split("/").collect();
701
702 let mut idx: usize = 0;
703 for rank in ranks {
704 for piece in rank.chars() {
705 let coord = (BOARD_MAP[idx] as u8).to_coordinate();
706 match piece {
707 'p' => {
708 self.set(coord, PieceType::PAWN, Color::BLACK)?;
709 }
710 'r' => {
711 self.set(coord, PieceType::ROOK, Color::BLACK)?;
712 }
713 'n' => {
714 self.set(coord, PieceType::KNIGHT, Color::BLACK)?;
715 }
716 'b' => {
717 self.set(coord, PieceType::BISHOP, Color::BLACK)?;
718 }
719 'q' => {
720 self.set(coord, PieceType::QUEEN, Color::BLACK)?;
721 }
722 'k' => {
723 self.set(coord, PieceType::KING, Color::BLACK)?;
724 }
725 'P' => {
726 self.set(coord, PieceType::PAWN, Color::WHITE)?;
727 }
728 'R' => {
729 self.set(coord, PieceType::ROOK, Color::WHITE)?;
730 }
731 'N' => {
732 self.set(coord, PieceType::KNIGHT, Color::WHITE)?;
733 }
734 'B' => {
735 self.set(coord, PieceType::BISHOP, Color::WHITE)?;
736 }
737 'Q' => {
738 self.set(coord, PieceType::QUEEN, Color::WHITE)?;
739 }
740 'K' => {
741 self.set(coord, PieceType::KING, Color::WHITE)?;
742 }
743 '1'..='8' => idx += piece.to_digit(10).unwrap() as usize - 1,
744 _ => panic!("can't load fen pieces"),
745 }
746
747 idx += 1;
748 }
749 }
750
751 match fen_parts[1] {
753 "w" => self.set_turn(Color::WHITE),
754 "b" => self.set_turn(Color::BLACK),
755 _ => panic!("can't load fen turn"),
756 }
757
758 for castling_right in fen_parts[2].chars() {
760 match castling_right {
761 'K' => {
762 self.castling_rights.white.kingside = true;
763 }
764 'Q' => {
765 self.castling_rights.white.queenside = true;
766 }
767 'k' => {
768 self.castling_rights.black.kingside = true;
769 }
770 'q' => {
771 self.castling_rights.black.queenside = true;
772 }
773
774 '-' => {
775 self.castling_rights.white.kingside = false;
776 self.castling_rights.white.queenside = false;
777 self.castling_rights.black.kingside = false;
778 self.castling_rights.black.kingside = false;
779 }
780 _ => panic!("cant load fen castling rights"),
781 }
782 }
783
784 let square = fen_parts[3];
786
787 match square {
788 "-" => {
790 self.en_passant_sq = None;
791 }
792 _ => {
793 let idx = BOARD_MAP[convert_algebraic_notation_to_index(square) as usize] as u8;
794 self.en_passant_sq = Some(idx.to_coordinate());
795 }
796 }
797
798 self.half_moves = fen_parts[4].parse().unwrap();
799 self.full_moves = fen_parts[5].parse().unwrap();
800
801 *self
802 .unique_positions
803 .entry(fen_parts[0].to_string())
804 .or_insert(0) += 1;
805
806 Ok(())
807 }
808
809 pub fn perft(&mut self, depth: u8, yah: bool) -> ChessResult<u64> {
810 let mut nodes: u64 = 0;
811 let mut cnt = Ok(0);
812
813 if depth <= 0 {
814 return Ok(1);
815 }
816
817 let moves = self.moves()?;
818
819 for _move in moves {
820 let from = convert_index_to_algebraic_notation(_move.from.to_index() as u8);
821 let to = convert_index_to_algebraic_notation(_move.to.to_index() as u8);
822
823 self.make_move(_move)?;
824 self.change_turn();
825
826 cnt = self.perft(depth - 1, false);
827
828 if let Ok(cnt) = cnt {
829 nodes += cnt;
830
831 if yah {
832 if let Some(promotion_piece) = _move.promotion_piece {
833 match promotion_piece.piece_type {
834 PieceType::BISHOP => {
835 println!("{} {}", format!("{}{}b", from, to), cnt);
836 }
837 PieceType::KNIGHT => {
838 println!("{} {}", format!("{}{}n", from, to), cnt);
839 }
840 PieceType::ROOK => {
841 println!("{} {}", format!("{}{}r", from, to), cnt);
842 }
843 PieceType::QUEEN => {
844 println!("{} {}", format!("{}{}q", from, to), cnt);
845 }
846 _ => panic!("invalid promotion piece"),
847 }
848 } else {
849 println!("{} {}", format!("{}{}", from, to), cnt);
850 }
851 }
852 } else {
853 panic!(
854 "depth {} mvoe {:?} piece {:?} error {:?}",
855 depth,
856 _move,
857 self.get(_move.from),
858 cnt
859 );
860 }
861 self.undo_move()?;
862 }
863
864
865 return Ok(nodes);
866 }
867
868 pub fn clear(&mut self) {
869 *self = Self::new();
870 }
871
872 fn set_turn(&mut self, color: Color) {
873 self.turn = color;
874 }
875
876 fn castling_rights_for_turn(&self) -> (bool, bool) {
877 if self.turn == Color::WHITE {
878 (
879 self.castling_rights.white.kingside,
880 self.castling_rights.white.queenside,
881 )
882 } else {
883 (
884 self.castling_rights.black.kingside,
885 self.castling_rights.black.queenside,
886 )
887 }
888 }
889
890 fn pawn_moves(&self, from: SquareCoordinate) -> ChessResult<Vec<Move>> {
891 let mut moves: Vec<Move> = vec![];
892
893 let from_piece = self.get(from)?.ok_or(ChessError::UnknownError(
894 "can't generate move for empty piece".to_string(),
895 ))?;
896
897 let deltas = match from_piece.color {
898 Color::BLACK => BLACK_PAWN_DELTAS,
899 Color::WHITE => WHITE_PAWN_DELTAS,
900 };
901
902 let mut can_move_forward = true;
903
904 for delta in deltas {
905 let to = from.to_index() as i16 + delta as i16;
906 if let Ok(_to) = utils::is_valid(to as usize) {
907 let to_sq = (_to as u8).to_coordinate();
908 let rank = to_sq.rank();
909
910 if delta % 2 != 0 {
911 if let Some(attacker) = self.get(to_sq)? {
913 if !self.is_friendly(attacker) {
914 if rank == 1 || rank == 8 {
916 let color = from_piece.color;
917 let promotion_pieces = vec![
918 Piece {
919 piece_type: PieceType::BISHOP,
920 color,
921 },
922 Piece {
923 piece_type: PieceType::KNIGHT,
924 color,
925 },
926 Piece {
927 piece_type: PieceType::ROOK,
928 color,
929 },
930 Piece {
931 piece_type: PieceType::QUEEN,
932 color,
933 },
934 ];
935
936 for piece in promotion_pieces {
937 moves.push(Move {
938 from,
939 to: to_sq,
940 promotion_piece: Some(piece),
941 })
942 }
943 } else {
944 moves.push(Move {
945 from,
946 to: to_sq,
947 promotion_piece: None,
948 });
949 }
950 }
951 }
952
953 if self.en_passant_sq == Some(to_sq) {
955 moves.push(Move {
956 from,
957 to: to_sq,
958 promotion_piece: None,
959 });
960 }
961 } else {
962 if self.get(to_sq)?.is_some() {
963 can_move_forward = false;
964 }
965
966 if !can_move_forward {
967 continue;
968 }
969
970 let rank = from.rank();
971 if to.abs_diff(from.to_index() as i16) == 32 {
973 if from_piece.color == Color::WHITE && rank == 2 {
974 moves.push(Move {
975 from,
976 to: to_sq,
977 promotion_piece: None,
978 });
979 }
980
981 if from_piece.color == Color::BLACK && rank == 7 {
982 moves.push(Move {
983 from,
984 to: to_sq,
985 promotion_piece: None,
986 });
987 }
988 continue;
989 }
990
991 let rank = to_sq.rank();
992
993 if rank == 1 || rank == 8 {
994 let color = from_piece.color;
995 let promotion_pieces = vec![
996 Piece {
997 piece_type: PieceType::BISHOP,
998 color,
999 },
1000 Piece {
1001 piece_type: PieceType::KNIGHT,
1002 color,
1003 },
1004 Piece {
1005 piece_type: PieceType::ROOK,
1006 color,
1007 },
1008 Piece {
1009 piece_type: PieceType::QUEEN,
1010 color,
1011 },
1012 ];
1013
1014 for piece in promotion_pieces {
1015 moves.push(Move {
1016 from,
1017 to: to_sq,
1018 promotion_piece: Some(piece),
1019 })
1020 }
1021 } else {
1022 moves.push(Move {
1023 from,
1024 to: to_sq,
1025 promotion_piece: None,
1026 })
1027 }
1028 }
1029 } else {
1030 continue;
1031 }
1032 }
1033
1034 Ok(moves)
1035 }
1036
1037 fn king_moves(&mut self, from: SquareCoordinate) -> ChessResult<Vec<Move>> {
1038 self.castling_rights.update(&self.kings, &self.board)?;
1039
1040 let mut moves: Vec<Move> = vec![];
1041
1042 let deltas = KING_DELTAS.to_vec();
1043 let (can_castle_kingside, can_castle_queenside) = self.castling_rights_for_turn();
1044 let from_idx = from.to_index() as i8;
1045
1046 let is_in_check = self.in_check()?;
1047
1048 for delta in deltas {
1049 let to = from.to_index() as i16 + delta as i16;
1050
1051 if let Ok(_to) = utils::is_valid(to as usize) {
1052 let to_sq = (_to as u8).to_coordinate();
1053
1054 let diff = to - from_idx as i16;
1055 let is_castling = diff == 2 || diff == -2;
1056
1057 if diff == 2 && !can_castle_kingside {
1059 continue;
1060 }
1061
1062 if diff == -2 && !can_castle_queenside {
1064 continue;
1065 }
1066
1067 if let Some(piece) = self.get(to_sq)? {
1068 if self.is_friendly(piece) {
1070 continue;
1071 }
1072 }
1073
1074 if is_castling {
1075 let mut allow_castle = true;
1076 let range: Range<i8> = match diff {
1077 2 => Ok(0..2), -2 => Ok(-4..-1), _ => Err(ChessError::UnknownError("Illegal castle".to_string())),
1080 }?;
1081
1082 for offset in range {
1083 let offset = (offset + 1 + from_idx) as u8;
1084 let coord = offset.to_coordinate();
1085
1086 if self.get(coord)?.is_some() {
1088 allow_castle = false;
1089 break;
1090 }
1091 let a = 1;
1092 let attacked = self.is_attacked(coord)?;
1093 if attacked
1095 && (coord != SquareCoordinate::B1 && coord != SquareCoordinate::B8)
1096 {
1097 allow_castle = false;
1098 break;
1099 }
1100 }
1101
1102 if allow_castle && !is_in_check {
1103 moves.push(Move {
1104 from,
1105 to: to_sq,
1106 promotion_piece: None,
1107 });
1108 }
1109 } else {
1110 moves.push(Move {
1111 from,
1112 to: to_sq,
1113 promotion_piece: None,
1114 });
1115 }
1116 } else {
1117 continue;
1118 }
1119 }
1120
1121 Ok(moves)
1122 }
1123
1124 fn knight_moves(&self, from: SquareCoordinate) -> ChessResult<Vec<Move>> {
1125 let mut moves: Vec<Move> = vec![];
1126
1127 let deltas = KNIGHT_DELTAS.to_vec();
1128
1129 for delta in deltas {
1130 let to = from.to_index() as i16 + delta as i16;
1131 if let Ok(_to) = utils::is_valid(to as usize) {
1132 let to_sq = (_to as u8).to_coordinate();
1133
1134 if let Some(piece) = self.get(to_sq)? {
1135 if self.is_friendly(piece) {
1137 continue;
1138 }
1139 }
1140
1141 moves.push(Move {
1142 from,
1143 to: to_sq,
1144 promotion_piece: None,
1145 });
1146 } else {
1147 continue;
1148 }
1149 }
1150
1151 Ok(moves)
1152 }
1153
1154 fn sliding_moves(&self, from: SquareCoordinate, deltas: Vec<i8>) -> ChessResult<Vec<Move>> {
1155 let mut moves: Vec<Move> = vec![];
1156
1157 for delta in deltas {
1158 let mut to = from.to_index() as i16 + delta as i16;
1159
1160 loop {
1161 if let Ok(_to) = utils::is_valid(to as usize) {
1162 let to_sq = (_to as u8).to_coordinate();
1163
1164 if let Some(piece) = self.get(to_sq)? {
1165 if self.is_friendly(piece) {
1167 break;
1168 } else {
1169 moves.push(Move {
1171 from,
1172 to: to_sq,
1173 promotion_piece: None,
1174 });
1175 break;
1176 }
1177 } else {
1178 moves.push(Move {
1179 from,
1180 to: to_sq,
1181 promotion_piece: None,
1182 });
1183 }
1184
1185 to += delta as i16;
1187 } else {
1188 break;
1189 }
1190 }
1191 }
1192
1193 Ok(moves)
1194 }
1195
1196 fn convert_to_internal_move(&self, m: Move) -> ChessResult<InternalMove> {
1197 let from_piece = self
1198 .get(m.from)?
1199 .ok_or(ChessError::InvalidMove(m.from.to_index(), m.to.to_index()))?;
1200
1201 let to_piece = self.get(m.to)?;
1202
1203 let mut internal_move = InternalMove {
1205 move_type: MoveType::Normal,
1206 from_sq: m.from,
1207 from_piece,
1208 to_sq: m.to,
1209 to_piece,
1210 promotion_piece: None,
1211 };
1212
1213 if let Some(to_piece) = to_piece {
1215 if !self.is_friendly(to_piece) {
1216 internal_move.move_type = MoveType::Capture;
1218 internal_move.to_piece = Some(to_piece);
1219 }
1220 }
1221
1222 if from_piece.piece_type == PieceType::PAWN {
1223 if m.to.to_index().abs_diff(m.from.to_index()) == 32 {
1225 internal_move.move_type = MoveType::EnPassantMove;
1226 }
1227
1228 if let Some(en_passant_sq) = self.en_passant_sq {
1230 if m.to == en_passant_sq {
1231 internal_move.move_type = MoveType::EnPassantCapture;
1232 }
1233 }
1234
1235 let rank = m.to.rank();
1236 if (rank == 8 || rank == 1) {
1238 internal_move.move_type = MoveType::Promotion;
1239 internal_move.promotion_piece = m.promotion_piece;
1240 }
1241 }
1242
1243 if from_piece.piece_type == PieceType::KING {
1245 let diff = m.to.to_index() as i8 - m.from.to_index() as i8;
1246 if diff == 2 {
1247 internal_move.move_type = MoveType::CastleKingside;
1248 }
1249
1250 if diff == -2 {
1251 internal_move.move_type = MoveType::CastleQueenside;
1252 }
1253 }
1254
1255 Ok(internal_move)
1256 }
1257
1258 fn is_friendly(&self, piece: Piece) -> bool {
1259 self.turn == piece.color
1260 }
1261
1262 fn update_kings_positions(&mut self, new_sq: SquareCoordinate, color: Color) {
1263 if color == Color::WHITE {
1264 self.kings.white = Some(new_sq)
1265 } else {
1266 self.kings.black = Some(new_sq)
1267 }
1268 }
1269}
1270
1271#[cfg(test)]
1272mod tests {
1273 use super::{PieceType, SquareCoordinate as Square, *};
1274
1275 #[test]
1276 fn board_set_and_get_pieces() {
1277 let mut chess = Chess::new();
1278
1279 assert_eq!(
1280 chess.set(Square::__BAD_COORD, PieceType::KING, Color::WHITE),
1281 Err(ChessError::InvalidIndex(Square::__BAD_COORD.to_index()))
1282 );
1283
1284 assert_eq!(chess.get(Square::E1), Ok(None));
1285
1286 let _ = chess.set(Square::E1, PieceType::KING, Color::WHITE);
1288
1289 assert_eq!(
1290 chess.get(Square::E1),
1291 Ok(Some(Piece {
1292 piece_type: PieceType::KING,
1293 color: Color::WHITE
1294 }))
1295 );
1296
1297 let _ = chess.remove(Square::E1);
1298
1299 assert_eq!(chess.get(Square::E1), Ok(None));
1300 }
1301
1302 #[test]
1303 fn make_move() {
1304 let mut chess = Chess::new();
1305
1306 let _ = chess.set(Square::E8, PieceType::KING, Color::BLACK);
1308
1309 assert_eq!(
1310 chess.get(Square::E8),
1311 Ok(Some(Piece {
1312 piece_type: PieceType::KING,
1313 color: Color::BLACK
1314 }))
1315 );
1316
1317 assert_eq!(
1318 chess.make_move(Move {
1319 from: Square::E8,
1320 to: Square::E7,
1321 promotion_piece: None
1322 }),
1323 Ok(())
1324 );
1325
1326 assert_eq!(
1327 chess.make_move(Move {
1328 from: Square::E3,
1329 to: Square::E7,
1330 promotion_piece: None
1331 }),
1332 Err(ChessError::InvalidMove(
1333 Square::E3.to_index(),
1334 Square::E7.to_index()
1335 ))
1336 );
1337 }
1338
1339 #[test]
1340 fn en_passant_move() {
1341 let mut chess = Chess::new();
1342
1343 let _ = chess.set(Square::E2, PieceType::PAWN, Color::WHITE);
1345
1346 assert_eq!(
1347 chess.make_move(Move {
1348 from: Square::E2,
1349 to: Square::E4,
1350 promotion_piece: None
1351 }),
1352 Ok(())
1353 );
1354
1355 assert_eq!(chess.en_passant_sq, Some(Square::E3));
1356
1357 assert_eq!(
1358 chess.make_move(Move {
1359 from: Square::E4,
1360 to: Square::E5,
1361 promotion_piece: None
1362 }),
1363 Ok(())
1364 );
1365
1366 assert_eq!(chess.en_passant_sq, None);
1367 }
1368
1369 #[test]
1370 fn en_passant_capture() {
1371 let mut chess = Chess::new();
1372
1373 let _ = chess.set(Square::E5, PieceType::PAWN, Color::WHITE);
1374 let _ = chess.set(Square::D5, PieceType::PAWN, Color::BLACK);
1375
1376 chess.en_passant_sq = Some(Square::D6);
1377
1378 assert_eq!(
1379 chess.make_move(Move {
1380 from: Square::E5,
1381 to: Square::D6,
1382 promotion_piece: None
1383 }),
1384 Ok(())
1385 );
1386
1387 assert_eq!(chess.get(Square::D5), Ok(None));
1388 assert_eq!(chess.en_passant_sq, None);
1389 assert_eq!(
1390 chess.white_captures,
1391 vec![Piece {
1392 piece_type: PieceType::PAWN,
1393 color: Color::BLACK
1394 }]
1395 )
1396 }
1397
1398 #[test]
1399 fn undo_normal_move() {
1400 let mut chess = Chess::new();
1401
1402 let _ = chess.set(Square::E1, PieceType::KING, Color::WHITE);
1403
1404 let _ = chess.make_move(Move {
1405 from: Square::E1,
1406 to: Square::E2,
1407 promotion_piece: None,
1408 });
1409
1410 assert_eq!(chess.undo_move(), Ok(()));
1411
1412 assert_eq!(chess.get(Square::E2), Ok(None));
1413
1414 assert_eq!(
1415 chess.get(Square::E1),
1416 Ok(Some(Piece {
1417 piece_type: PieceType::KING,
1418 color: Color::WHITE,
1419 }))
1420 );
1421 }
1422
1423 #[test]
1424 fn undo_en_passant_capture() {
1425 let mut chess = Chess::new();
1426
1427 let _ = chess.set(Square::E5, PieceType::PAWN, Color::WHITE);
1428 let _ = chess.set(Square::D5, PieceType::PAWN, Color::BLACK);
1429
1430 chess.en_passant_sq = Some(Square::D6);
1431
1432 assert_eq!(
1433 chess.make_move(Move {
1434 from: Square::E5,
1435 to: Square::D6,
1436 promotion_piece: None
1437 }),
1438 Ok(())
1439 );
1440
1441 assert_eq!(chess.undo_move(), Ok(()));
1442
1443 assert_eq!(chess.get(Square::D6), Ok(None));
1444 assert_eq!(
1445 chess.get(Square::D5),
1446 Ok(Some(Piece {
1447 piece_type: PieceType::PAWN,
1448 color: Color::BLACK
1449 }))
1450 );
1451 assert_eq!(
1452 chess.get(Square::E5),
1453 Ok(Some(Piece {
1454 piece_type: PieceType::PAWN,
1455 color: Color::WHITE
1456 }))
1457 );
1458 }
1459
1460 #[test]
1461 fn undo_en_passant_move() {
1462 let mut chess = Chess::new();
1463
1464 let _ = chess.set(Square::E2, PieceType::PAWN, Color::WHITE);
1466
1467 assert_eq!(
1468 chess.make_move(Move {
1469 from: Square::E2,
1470 to: Square::E4,
1471 promotion_piece: None
1472 }),
1473 Ok(())
1474 );
1475
1476 assert_eq!(chess.en_passant_sq, Some(Square::E3));
1477
1478 assert_eq!(chess.undo_move(), Ok(()));
1479
1480 assert_eq!(chess.get(Square::E4), Ok(None));
1481
1482 assert_eq!(
1483 chess.get(Square::E2),
1484 Ok(Some(Piece {
1485 piece_type: PieceType::PAWN,
1486 color: Color::WHITE
1487 }))
1488 );
1489
1490 assert_eq!(chess.en_passant_sq, None);
1491 }
1492
1493 #[test]
1494 fn undo_capture() {
1495 let mut chess = Chess::new();
1496
1497 let _ = chess.set(Square::F1, PieceType::BISHOP, Color::WHITE);
1499 let _ = chess.set(Square::C4, PieceType::QUEEN, Color::BLACK);
1500
1501 assert_eq!(
1502 chess.make_move(Move {
1503 from: Square::F1,
1504 to: Square::C4,
1505 promotion_piece: None
1506 }),
1507 Ok(())
1508 );
1509
1510 assert_eq!(
1511 chess.white_captures,
1512 vec![Piece {
1513 piece_type: PieceType::QUEEN,
1514 color: Color::BLACK
1515 }]
1516 );
1517
1518 assert_eq!(
1519 chess.get(Square::C4),
1520 Ok(Some(Piece {
1521 piece_type: PieceType::BISHOP,
1522 color: Color::WHITE
1523 }))
1524 );
1525
1526 assert_eq!(chess.undo_move(), Ok(()));
1527
1528 assert_eq!(chess.white_captures, vec![]);
1529
1530 assert_eq!(
1531 chess.get(Square::F1),
1532 Ok(Some(Piece {
1533 piece_type: PieceType::BISHOP,
1534 color: Color::WHITE
1535 }))
1536 );
1537
1538 assert_eq!(
1539 chess.get(Square::C4),
1540 Ok(Some(Piece {
1541 piece_type: PieceType::QUEEN,
1542 color: Color::BLACK
1543 }))
1544 );
1545 }
1546
1547 #[test]
1548 fn undo_kingside_castle() {
1549 let mut chess = Chess::new();
1550
1551 let _ = chess.set(Square::E1, PieceType::KING, Color::WHITE);
1553 let _ = chess.set(Square::H1, PieceType::ROOK, Color::WHITE);
1554
1555 assert_eq!(
1556 chess.make_move(Move {
1557 from: Square::E1,
1558 to: Square::G1,
1559 promotion_piece: None
1560 }),
1561 Ok(())
1562 );
1563
1564 assert_eq!(chess.undo_move(), Ok(()));
1565
1566 assert_eq!(
1567 chess.get(Square::E1),
1568 Ok(Some(Piece {
1569 piece_type: PieceType::KING,
1570 color: Color::WHITE
1571 }))
1572 );
1573
1574 assert_eq!(
1575 chess.get(Square::H1),
1576 Ok(Some(Piece {
1577 piece_type: PieceType::ROOK,
1578 color: Color::WHITE
1579 }))
1580 );
1581
1582 assert_eq!(chess.get(Square::G1), Ok(None));
1583 assert_eq!(chess.get(Square::F1), Ok(None));
1584 assert_eq!(chess.castling_rights.white.kingside, true);
1585 assert_eq!(chess.castling_rights.white.queenside, true);
1586 }
1587
1588 #[test]
1589 fn undo_queenside_castle() {
1590 let mut chess = Chess::new();
1591
1592 let _ = chess.set(Square::E8, PieceType::KING, Color::BLACK);
1594 let _ = chess.set(Square::A8, PieceType::ROOK, Color::BLACK);
1595
1596 assert_eq!(
1597 chess.make_move(Move {
1598 from: Square::E8,
1599 to: Square::C8,
1600 promotion_piece: None
1601 }),
1602 Ok(())
1603 );
1604
1605 assert_eq!(chess.undo_move(), Ok(()));
1606
1607 assert_eq!(
1608 chess.get(Square::E8),
1609 Ok(Some(Piece {
1610 piece_type: PieceType::KING,
1611 color: Color::BLACK
1612 }))
1613 );
1614
1615 assert_eq!(
1616 chess.get(Square::A8),
1617 Ok(Some(Piece {
1618 piece_type: PieceType::ROOK,
1619 color: Color::BLACK
1620 }))
1621 );
1622
1623 assert_eq!(chess.get(Square::C8), Ok(None));
1624 assert_eq!(chess.get(Square::D8), Ok(None));
1625 assert_eq!(chess.castling_rights.black.kingside, true);
1626 assert_eq!(chess.castling_rights.black.queenside, true);
1627 }
1628
1629 #[test]
1630 fn undo_kingside_promotion() {
1631 let mut chess = Chess::new();
1632
1633 let _ = chess.set(Square::E7, PieceType::PAWN, Color::WHITE);
1635
1636 assert_eq!(
1637 chess.make_move(Move {
1638 from: Square::E7,
1639 to: Square::E8,
1640 promotion_piece: Some(Piece {
1641 piece_type: PieceType::QUEEN,
1642 color: Color::WHITE
1643 })
1644 }),
1645 Ok(())
1646 );
1647
1648 assert_eq!(chess.undo_move(), Ok(()));
1649
1650 assert_eq!(
1651 chess.get(Square::E7),
1652 Ok(Some(Piece {
1653 piece_type: PieceType::PAWN,
1654 color: Color::WHITE
1655 }))
1656 );
1657
1658 assert_eq!(chess.get(Square::E8), Ok(None));
1659 }
1660
1661 #[test]
1662 fn castle_kingside_successfully() {
1663 let mut chess = Chess::new();
1664
1665 let _ = chess.set(Square::E1, PieceType::KING, Color::WHITE);
1667 let _ = chess.set(Square::H1, PieceType::ROOK, Color::WHITE);
1668
1669 assert_eq!(
1670 chess.make_move(Move {
1671 from: Square::E1,
1672 to: Square::G1,
1673 promotion_piece: None
1674 }),
1675 Ok(())
1676 );
1677
1678 assert_eq!(chess.get(Square::H1), Ok(None));
1679
1680 assert_eq!(
1681 chess.get(Square::G1),
1682 Ok(Some(Piece {
1683 piece_type: PieceType::KING,
1684 color: Color::WHITE
1685 }))
1686 );
1687
1688 assert_eq!(
1689 chess.get(Square::F1),
1690 Ok(Some(Piece {
1691 piece_type: PieceType::ROOK,
1692 color: Color::WHITE
1693 }))
1694 );
1695
1696 assert_eq!(chess.castling_rights.white.kingside, false);
1697 assert_eq!(chess.castling_rights.white.queenside, false);
1698 }
1699
1700 #[test]
1701 fn castle_queenside_successfully() {
1702 let mut chess = Chess::new();
1703
1704 let _ = chess.set(Square::E8, PieceType::KING, Color::BLACK);
1706 let _ = chess.set(Square::A8, PieceType::ROOK, Color::BLACK);
1707
1708 assert_eq!(
1709 chess.make_move(Move {
1710 from: Square::E8,
1711 to: Square::C8,
1712 promotion_piece: None
1713 }),
1714 Ok(())
1715 );
1716
1717 assert_eq!(chess.get(Square::A8), Ok(None));
1718
1719 assert_eq!(
1720 chess.get(Square::C8),
1721 Ok(Some(Piece {
1722 piece_type: PieceType::KING,
1723 color: Color::BLACK
1724 }))
1725 );
1726
1727 assert_eq!(
1728 chess.get(Square::D8),
1729 Ok(Some(Piece {
1730 piece_type: PieceType::ROOK,
1731 color: Color::BLACK
1732 }))
1733 );
1734
1735 assert_eq!(chess.castling_rights.black.queenside, false);
1736 assert_eq!(chess.castling_rights.black.kingside, false);
1737 }
1738
1739 #[test]
1740 fn cannot_castle_if_rook_not_in_correct_square() {
1741 let mut chess = Chess::new();
1742
1743 let _ = chess.set(Square::E1, PieceType::KING, Color::WHITE);
1744 let _ = chess.set(Square::H1, PieceType::ROOK, Color::WHITE);
1745 let _ = chess.set(Square::A1, PieceType::ROOK, Color::WHITE);
1746
1747 let _ = chess.make_move(Move {
1748 from: Square::H1,
1749 to: Square::H2,
1750 promotion_piece: None,
1751 });
1752
1753 assert_eq!(chess.castling_rights.white.queenside, true);
1754 assert_eq!(chess.castling_rights.white.kingside, false);
1755
1756 let _ = chess.make_move(Move {
1757 from: Square::A1,
1758 to: Square::D1,
1759 promotion_piece: None,
1760 });
1761
1762 assert_eq!(chess.castling_rights.white.queenside, false);
1763 assert_eq!(chess.castling_rights.white.kingside, false);
1764 }
1765
1766 #[test]
1767 fn promotion() {
1768 let mut chess = Chess::new();
1769
1770 let _ = chess.set(Square::E7, PieceType::PAWN, Color::WHITE);
1772
1773 assert_eq!(
1774 chess.make_move(Move {
1775 from: Square::E7,
1776 to: Square::E8,
1777 promotion_piece: Some(Piece {
1778 piece_type: PieceType::QUEEN,
1779 color: Color::WHITE
1780 })
1781 }),
1782 Ok(())
1783 );
1784
1785 assert_eq!(
1786 chess.get(Square::E8),
1787 Ok(Some(Piece {
1788 piece_type: PieceType::QUEEN,
1789 color: Color::WHITE
1790 }))
1791 );
1792
1793 assert_eq!(chess.get(Square::E7), Ok(None))
1794 }
1795}