1mod chess_move;
2mod piece;
3mod square;
4
5pub use chess_move::ChessMove;
6use chess_move::{CastlingType, SanMove};
7pub use piece::Piece;
8use std::collections::HashMap;
9
10pub(crate) use square::*;
11
12#[derive(Debug)]
13pub struct Chessboard {
14 white: u64,
15 black: u64,
16 static_white_attack_mask: u64,
17 static_black_attack_mask: u64,
18
19 pieces: [u64; 6],
20 pseudo_legal_moves: HashMap<u64, u64>,
21 dynamic_piece_squares: Vec<usize>,
22 legal_moves: HashMap<u64, u64>,
23
24 turn: bool,
25 castle_rights: [bool; 4],
27 en_passant_square: Option<u64>,
28 half_move: u32,
29 full_move: u32,
30
31 board_repetitions: HashMap<String, usize>,
32
33 pub history: Vec<ChessMove>,
34}
35
36impl Chessboard {
37 pub fn new() -> Self {
45 let mut board = Self::load_fen(START_FEN);
46
47 board.generate_legal_moves();
48
49 board
50 }
51
52 pub fn from_fen(fen: &str) -> Self {
60 let mut board = Self::load_fen(fen);
61
62 board.generate_legal_moves();
63
64 board
65 }
66
67 fn get_color(&self, color: bool) -> u64 {
68 match color {
69 true => self.white,
70 false => self.black,
71 }
72 }
73
74 fn all(&self) -> u64 {
75 self.white | self.black
76 }
77
78 fn generate_pawn_moves(&self, square: u64) -> u64 {
79 let color = self.white & square != 0;
80
81 match color {
82 true => {
83 let mut mask = 0;
84 if square << 7 & self.black != 0 {
85 mask |= square << 7;
86 }
87
88 if square << 9 & self.black != 0 {
89 mask |= square << 9;
90 }
91
92 if square << 8 & self.all() == 0 {
93 mask |= square << 8;
94 if square & RANK_2 != 0 && square << 16 & self.all() == 0 {
95 mask |= square << 16;
96 }
97 }
98
99 if let Some(en_passant_square) = self.en_passant_square {
100 if square << 7 == en_passant_square || square << 9 == en_passant_square {
101 mask |= en_passant_square;
102 }
103 }
104
105 mask
106 }
107 false => {
108 let mut mask = 0;
109
110 if square >> 7 & self.white != 0 {
111 mask |= square >> 7;
112 }
113
114 if square >> 9 & self.white != 0 {
115 mask |= square >> 9;
116 }
117 if square >> 8 & self.all() == 0 {
118 mask |= square >> 8;
119 if square & RANK_7 != 0 && square >> 16 & self.all() == 0 {
120 mask |= square >> 16;
121 }
122 }
123
124 if let Some(en_passant_square) = self.en_passant_square {
125 if square >> 7 == en_passant_square || square >> 9 == en_passant_square {
126 mask |= en_passant_square;
127 }
128 }
129
130 mask
131 }
132 }
133 }
134
135 fn generate_knight_moves(&self, square: u64) -> u64 {
136 let mut mask = 0;
137
138 if square & FILE_A == 0 && square & (RANK_8 | RANK_7) == 0 {
140 mask |= square << 15;
141 }
142
143 if square & FILE_H == 0 && square & (RANK_8 | RANK_7) == 0 {
145 mask |= square << 17;
146 }
147
148 if square & FILE_A == 0 && square & (RANK_1 | RANK_2) == 0 {
150 mask |= square >> 17;
151 }
152
153 if square & FILE_H == 0 && square & (RANK_1 | RANK_2) == 0 {
155 mask |= square >> 15;
156 }
157
158 if square & (FILE_A | FILE_B) == 0 && square & RANK_8 == 0 {
160 mask |= square << 6;
161 }
162
163 if square & (FILE_A | FILE_B) == 0 && square & RANK_1 == 0 {
165 mask |= square >> 10;
166 }
167
168 if square & (FILE_H | FILE_G) == 0 && square & RANK_8 == 0 {
170 mask |= square << 10;
171 }
172
173 if square & (FILE_H | FILE_G) == 0 && square & RANK_1 == 0 {
175 mask |= square >> 6;
176 }
177
178 mask
179 }
180
181 fn generate_rook_moves(&self, square: u64, board: u64) -> u64 {
182 let mut mask = 0;
183
184 let mut up = square;
185 while up & RANK_8 == 0 {
186 up <<= 8;
187 mask |= up;
188 if up & board != 0 {
189 break;
190 }
191 }
192
193 let mut down = square;
194 while down & RANK_1 == 0 {
195 down >>= 8;
196 mask |= down;
197 if down & board != 0 {
198 break;
199 }
200 }
201
202 let mut left = square;
203 while left & FILE_A == 0 {
204 left >>= 1;
205 mask |= left;
206 if left & board != 0 {
207 break;
208 }
209 }
210
211 let mut right = square;
212 while right & FILE_H == 0 {
213 right <<= 1;
214 mask |= right;
215 if right & board != 0 {
216 break;
217 }
218 }
219
220 mask
221 }
222
223 fn generate_bishop_moves(&self, square: u64, board: u64) -> u64 {
224 let mut mask = 0;
225
226 let mut up_left = square;
227 while up_left & (RANK_8 | FILE_A) == 0 {
228 up_left <<= 7;
229 mask |= up_left;
230 if up_left & board != 0 {
231 break;
232 }
233 }
234
235 let mut up_right = square;
236 while up_right & (RANK_8 | FILE_H) == 0 {
237 up_right <<= 9;
238 mask |= up_right;
239 if up_right & board != 0 {
240 break;
241 }
242 }
243 let mut down_left = square;
244 while down_left & (RANK_1 | FILE_A) == 0 {
245 down_left >>= 9;
246 mask |= down_left;
247 if down_left & board != 0 {
248 break;
249 }
250 }
251 let mut down_right = square;
252 while down_right & (RANK_1 | FILE_H) == 0 {
253 down_right >>= 7;
254 mask |= down_right;
255 if down_right & board != 0 {
256 break;
257 }
258 }
259
260 mask
261 }
262
263 fn generate_king_moves(&self, square: u64) -> u64 {
264 let mut mask = 0;
265
266 if square & RANK_8 == 0 {
267 mask |= square << 8;
268 }
269
270 if square & RANK_1 == 0 {
271 mask |= square >> 8;
272 }
273
274 if square & FILE_A == 0 {
275 mask |= square << 1;
276 }
277
278 if square & FILE_H == 0 {
279 mask |= square >> 1;
280 }
281
282 if square & (RANK_8 | FILE_A) == 0 {
283 mask |= square << 9;
284 }
285
286 if square & (RANK_8 | FILE_H) == 0 {
287 mask |= square << 7;
288 }
289
290 if square & (RANK_1 | FILE_A) == 0 {
291 mask |= square >> 7;
292 }
293
294 if square & (RANK_1 | FILE_H) == 0 {
295 mask |= square >> 9;
296 }
297
298 mask
299 }
300
301 fn generate_queen_moves(&self, square: u64, board: u64) -> u64 {
302 self.generate_rook_moves(square, board) | self.generate_bishop_moves(square, board)
303 }
304
305 fn generate_pseudo_legal_moves(&mut self) {
306 self.static_white_attack_mask = 0;
307 self.static_black_attack_mask = 0;
308 self.dynamic_piece_squares.clear();
309
310 for i in 0..64 {
311 let square: u64 = 1 << i;
312
313 if self.all() & square != 0 {
314 let color = self.white & square != 0;
315 let piece = self.get_piece(square);
316 match piece {
317 Piece::PAWN => {
318 match color {
319 true => {
320 self.static_white_attack_mask |= square << 7;
321 self.static_white_attack_mask |= square << 9;
322 }
323 false => {
324 self.static_black_attack_mask |= square >> 7;
325 self.static_black_attack_mask |= square >> 9;
326 }
327 }
328 self.pseudo_legal_moves
329 .insert(square, self.generate_pawn_moves(square));
330 }
331
332 Piece::BISHOP | Piece::ROOK | Piece::QUEEN => {
333 let mask = match piece {
334 Piece::BISHOP => self.generate_bishop_moves(square, self.all()),
335 Piece::ROOK => self.generate_rook_moves(square, self.all()),
336 Piece::QUEEN => self.generate_queen_moves(square, self.all()),
337 _ => unreachable!(),
338 };
339
340 self.dynamic_piece_squares.push(i);
341 self.pseudo_legal_moves
342 .insert(square, mask & !self.get_color(color));
343 }
344 Piece::KNIGHT => {
345 let mask = self.generate_knight_moves(square);
346
347 match color {
348 true => {
349 self.static_white_attack_mask |= mask;
350 }
351 false => {
352 self.static_black_attack_mask |= mask;
353 }
354 }
355 self.pseudo_legal_moves
356 .insert(square, mask & !self.get_color(color));
357 }
358 Piece::KING => {
359 let mut mask = self.generate_king_moves(square);
360 match color {
361 true => {
362 self.static_white_attack_mask |= mask;
363
364 if self.turn {
365 if self.castle_rights[0]
366 && self.all() & WHITE_KING_SIDE_CASTLE == 0
367 {
368 mask |= 1 << Square::G1 as u64;
369 }
370
371 if self.castle_rights[1]
372 && self.all() & WHITE_QUEEN_SIDE_CASTLE == 0
373 {
374 mask |= 1 << Square::C1 as u64;
375 }
376 }
377 }
378 false => {
379 self.static_black_attack_mask |= mask;
380
381 if !self.turn {
382 if self.castle_rights[2]
383 && self.all() & BLACK_KING_SIDE_CASTLE == 0
384 {
385 mask |= 1 << Square::G8 as u64;
386 }
387
388 if self.castle_rights[3]
389 && self.all() & BLACK_QUEEN_SIDE_CASTLE == 0
390 {
391 mask |= 1 << Square::C8 as u64;
392 }
393 }
394 }
395 }
396
397 self.pseudo_legal_moves
398 .insert(square, mask & !self.get_color(color));
399 }
400 _ => {
401 self.pseudo_legal_moves.insert(square, 0);
402 }
403 }
404 } else {
405 self.pseudo_legal_moves.insert(square, 0);
406 }
407 }
408 }
409
410 fn generate_legal_moves(&mut self) {
411 self.generate_pseudo_legal_moves();
412
413 for (&square, &moves) in self.pseudo_legal_moves.iter() {
414 let current_square: u64 = square.into();
415 let color = self.white & current_square != 0;
416 let piece = self.get_piece(current_square);
417 if current_square & self.get_color(self.turn) == 0 {
418 self.legal_moves.insert(square, 0);
419 continue;
420 }
421
422 let mut legal_moves = 0;
423 let current_board_without_piece = self.all() & !current_square;
424
425 for potential_square in Self::get_squares(moves) {
426 let board = current_board_without_piece | potential_square;
427 let enemy_attack_mask = self.get_attack_mask(color, board);
428
429 match piece {
430 Piece::KING => match color {
431 true => {
432 let g1: u64 = Square::G1.into();
433 let c1: u64 = Square::C1.into();
434 if potential_square & g1 != 0 {
435 if enemy_attack_mask & WHITE_KING_SIDE_CASTLE == 0 {
436 legal_moves |= potential_square;
437 }
438 } else if potential_square & c1 != 0 {
439 if enemy_attack_mask & WHITE_QUEEN_SIDE_CASTLE == 0 {
440 legal_moves |= potential_square;
441 }
442 } else {
443 if enemy_attack_mask & potential_square == 0 {
444 legal_moves |= potential_square;
445 }
446 }
447 }
448 false => {
449 let g8: u64 = Square::G8.into();
450 let c8: u64 = Square::C8.into();
451 if potential_square & g8 != 0 {
452 if enemy_attack_mask & BLACK_KING_SIDE_CASTLE == 0 {
453 legal_moves |= potential_square;
454 }
455 } else if potential_square & c8 != 0 {
456 if enemy_attack_mask & BLACK_QUEEN_SIDE_CASTLE == 0 {
457 legal_moves |= potential_square;
458 }
459 } else {
460 if enemy_attack_mask & potential_square == 0 {
461 legal_moves |= potential_square;
462 }
463 }
464 }
465 },
466 _ => {
467 let king = self.pieces[Piece::KING as usize] & self.get_color(color);
468 match color {
469 true => {
470 if enemy_attack_mask & king == 0 {
471 legal_moves |= potential_square;
472 }
473 }
474 false => {
475 if enemy_attack_mask & king == 0 {
476 legal_moves |= potential_square;
477 }
478 }
479 }
480 }
481 };
482 }
483 self.legal_moves.insert(square, legal_moves);
484 }
485 }
486
487 fn get_piece(&self, square: u64) -> Piece {
488 if self.pieces[Piece::PAWN as usize] & square != 0 {
489 return Piece::PAWN;
490 }
491
492 if self.pieces[Piece::KNIGHT as usize] & square != 0 {
493 return Piece::KNIGHT;
494 }
495
496 if self.pieces[Piece::BISHOP as usize] & square != 0 {
497 return Piece::BISHOP;
498 }
499
500 if self.pieces[Piece::ROOK as usize] & square != 0 {
501 return Piece::ROOK;
502 }
503
504 if self.pieces[Piece::QUEEN as usize] & square != 0 {
505 return Piece::QUEEN;
506 }
507
508 if self.pieces[Piece::KING as usize] & square != 0 {
509 return Piece::KING;
510 }
511
512 Piece::UNKNOWN
513 }
514
515 fn load_fen(fen: &str) -> Self {
516 let mut white = 0;
517 let mut black = 0;
518 let mut pieces = [0, 0, 0, 0, 0, 0];
519 let static_white_attack_mask = 0;
520 let static_black_attack_mask = 0;
521 let mut castle_rights = [false; 4];
522 let mut turn = true;
523 let mut en_passant_square = None;
524 let mut half_move = 0;
525 let mut full_move = 0;
526
527 for (index, rank) in fen.split('/').enumerate() {
528 let mut file: u64 = 0;
529 if index == 7 {
530 let parts: Vec<&str> = rank.split(' ').collect();
531 turn = parts[1] == "w";
532 for castle_right in parts[2].chars() {
533 match castle_right {
534 'K' => castle_rights[0] = true,
535 'Q' => castle_rights[1] = true,
536 'k' => castle_rights[2] = true,
537 'q' => castle_rights[3] = true,
538 _ => {}
539 }
540 }
541 if parts[3] != "-" {
542 en_passant_square = Some(Square::from(parts[3]) as u64);
543 }
544
545 half_move = parts[4].parse().unwrap();
546 full_move = parts[5].parse().unwrap();
547 }
548 for c in rank.chars() {
549 if file > 7 {
550 break;
551 }
552 if c.is_digit(10) {
553 file += c.to_digit(10).unwrap() as u64;
554 } else {
555 let square = 1 << 56 - (index as u64) * 8 + file;
556 let color = c.is_uppercase();
557 match c.to_ascii_lowercase() {
558 'p' => {
559 pieces[Piece::PAWN as usize] |= square;
560 match color {
561 true => white |= square,
562 false => black |= square,
563 }
564 }
565 'n' => {
566 pieces[Piece::KNIGHT as usize] |= square;
567 match color {
568 true => white |= square,
569 false => black |= square,
570 }
571 }
572 'b' => {
573 pieces[Piece::BISHOP as usize] |= square;
574 match color {
575 true => white |= square,
576 false => black |= square,
577 }
578 }
579 'r' => {
580 pieces[Piece::ROOK as usize] |= square;
581 match color {
582 true => white |= square,
583 false => black |= square,
584 }
585 }
586 'q' => {
587 pieces[Piece::QUEEN as usize] |= square;
588 match color {
589 true => white |= square,
590 false => black |= square,
591 }
592 }
593 'k' => {
594 pieces[Piece::KING as usize] |= square;
595 match color {
596 true => white |= square,
597 false => black |= square,
598 }
599 }
600 _ => {}
601 }
602 file += 1;
603 }
604 }
605 }
606
607 Self {
608 white,
609 static_white_attack_mask,
610 black,
611 static_black_attack_mask,
612 pieces,
613 legal_moves: HashMap::new(),
614 pseudo_legal_moves: HashMap::new(),
615 dynamic_piece_squares: Vec::new(),
616 castle_rights,
617 turn,
618 en_passant_square,
619 half_move,
620 full_move,
621 history: Vec::new(),
622 board_repetitions: HashMap::from([(
623 fen.split_whitespace().next().unwrap().to_string(),
624 1,
625 )]),
626 }
627 }
628
629 pub fn get_fen(&self) -> String {
637 let mut fen = String::new();
638 for rank in 0..8 {
639 let mut empty = 0;
640 for file in 0..8 {
641 let square = 1 << 56 - rank as u64 * 8 + file;
642 let color = self.white & square != 0;
643 match self.get_piece(square) {
644 Piece::PAWN => {
645 if empty > 0 {
646 fen.push_str(&empty.to_string());
647 empty = 0;
648 }
649 match color {
650 true => fen.push('P'),
651 false => fen.push('p'),
652 }
653 }
654 Piece::KNIGHT => {
655 if empty > 0 {
656 fen.push_str(&empty.to_string());
657 empty = 0;
658 }
659 match color {
660 true => fen.push('N'),
661 false => fen.push('n'),
662 }
663 }
664 Piece::BISHOP => {
665 if empty > 0 {
666 fen.push_str(&empty.to_string());
667 empty = 0;
668 }
669 match color {
670 true => fen.push('B'),
671 false => fen.push('b'),
672 }
673 }
674 Piece::ROOK => {
675 if empty > 0 {
676 fen.push_str(&empty.to_string());
677 empty = 0;
678 }
679 match color {
680 true => fen.push('R'),
681 false => fen.push('r'),
682 }
683 }
684 Piece::QUEEN => {
685 if empty > 0 {
686 fen.push_str(&empty.to_string());
687 empty = 0;
688 }
689 match color {
690 true => fen.push('Q'),
691 false => fen.push('q'),
692 }
693 }
694 Piece::KING => {
695 if empty > 0 {
696 fen.push_str(&empty.to_string());
697 empty = 0;
698 }
699 match color {
700 true => fen.push('K'),
701 false => fen.push('k'),
702 }
703 }
704 _ => {
705 empty += 1;
706 }
707 }
708 }
709 if empty > 0 {
710 fen.push_str(&empty.to_string());
711 }
712 if rank < 7 {
713 fen.push('/');
714 }
715 }
716
717 let turn = if self.turn { "w" } else { "b" };
718
719 let mut castle_rights = String::new();
720
721 if self.castle_rights[0] {
722 castle_rights.push('K');
723 }
724 if self.castle_rights[1] {
725 castle_rights.push('Q');
726 }
727 if self.castle_rights[2] {
728 castle_rights.push('k');
729 }
730 if self.castle_rights[3] {
731 castle_rights.push('q');
732 }
733
734 if castle_rights.is_empty() {
735 castle_rights = "-".to_string();
736 }
737
738 let en_passant_square = match self.en_passant_square {
739 Some(square) => Square::from(square).to_string(),
740 None => "-".to_string(),
741 };
742
743 format!(
744 "{} {} {} {} {} {}",
745 fen, turn, castle_rights, en_passant_square, self.half_move, self.full_move
746 )
747 }
748
749 pub fn is_checked(&self, color: bool) -> bool {
757 let king = self.pieces[Piece::KING as usize] & self.get_color(color);
758 let enemy_attack_mask = self.get_attack_mask(color, self.all());
759
760 enemy_attack_mask & king != 0
761 }
762
763 pub fn is_mate(&self, color: bool) -> bool {
771 self.is_checked(color) && !self.has_moves()
772 }
773
774 pub fn is_stalemate(&self, color: bool) -> bool {
782 !self.is_checked(color) && !self.has_moves()
783 }
784
785 pub fn is_fifty_moves(&self) -> bool {
793 self.half_move >= 100
794 }
795
796 pub fn is_threefold_repetition(&self) -> bool {
804 let mut max = 0;
805
806 for (_, &count) in self.board_repetitions.iter() {
807 if count > max {
808 max = count;
809 }
810 }
811
812 max >= 3
813 }
814
815 fn has_moves(&self) -> bool {
816 for &legal_moves in self.legal_moves.values() {
817 if legal_moves != 0 {
818 return true;
819 }
820 }
821 false
822 }
823
824 fn get_attack_mask(&self, color: bool, board: u64) -> u64 {
825 let mut enemy_attack_mask = match color {
826 true => self.static_black_attack_mask,
827 false => self.static_white_attack_mask,
828 };
829
830 for &dynamic_piece in self.dynamic_piece_squares.iter() {
831 let dynamic_piece_square = 1 << dynamic_piece;
832 let piece = self.get_piece(dynamic_piece_square);
833 let dynamic_piece_color = self.white & dynamic_piece_square != 0;
834
835 if dynamic_piece_color == color {
836 continue;
837 }
838
839 match piece {
840 Piece::BISHOP => {
841 enemy_attack_mask |= self.generate_bishop_moves(dynamic_piece_square, board);
842 }
843 Piece::ROOK => {
844 enemy_attack_mask |= self.generate_rook_moves(dynamic_piece_square, board);
845 }
846 Piece::QUEEN => {
847 enemy_attack_mask |= self.generate_queen_moves(dynamic_piece_square, board);
848 }
849 _ => {}
850 }
851 }
852
853 enemy_attack_mask
854 }
855
856 pub fn move_to(&mut self, san: &str) {
864 let mut san = SanMove::parse(san);
865
866 let mut has_moved = false;
867 if let Ok(ref mut valid_san) = san {
868 if let Some(castling) = valid_san.castling {
869 match castling {
870 CastlingType::KingSide => match self.turn {
871 true => {
872 if let Some(legal_moves) = self.legal_moves.get(&Square::E1.into()) {
873 let g1: u64 = Square::G1.into();
874
875 if legal_moves & g1 != 0 {
876 self.half_move += 1;
877 let e1: u64 = Square::E1.into();
878
879 self.pieces[Piece::KING as usize] ^= e1;
880 self.pieces[Piece::KING as usize] |= g1;
881 self.white ^= e1;
882 self.white |= g1;
883
884 let f1: u64 = Square::F1.into();
885 let h1: u64 = Square::H1.into();
886
887 self.pieces[Piece::ROOK as usize] ^= h1;
888 self.pieces[Piece::ROOK as usize] |= f1;
889 self.white ^= h1;
890 self.white |= f1;
891
892 self.turn = !self.turn;
893 self.castle_rights[0] = false;
894 self.castle_rights[1] = false;
895 has_moved = true;
896 }
897 }
898 }
899 false => {
900 if let Some(legal_moves) = self.legal_moves.get(&Square::E8.into()) {
901 let g8: u64 = Square::G8.into();
902
903 if legal_moves & g8 != 0 {
904 self.half_move += 1;
905 let e8: u64 = Square::E8.into();
906
907 self.pieces[Piece::KING as usize] ^= e8;
908 self.pieces[Piece::KING as usize] |= g8;
909 self.black ^= e8;
910 self.black |= g8;
911
912 let f8: u64 = Square::F8.into();
913 let h8: u64 = Square::H8.into();
914
915 self.pieces[Piece::ROOK as usize] ^= h8;
916 self.pieces[Piece::ROOK as usize] |= f8;
917 self.black ^= h8;
918 self.black |= f8;
919
920 self.turn = !self.turn;
921 self.full_move += 1;
922 self.castle_rights[2] = false;
923 self.castle_rights[3] = false;
924 has_moved = true;
925 }
926 }
927 }
928 },
929 CastlingType::QueenSide => match self.turn {
930 true => {
931 if let Some(legal_moves) = self.legal_moves.get(&Square::E1.into()) {
932 let c1: u64 = Square::C1.into();
933
934 if legal_moves & c1 != 0 {
935 self.half_move += 1;
936 let e1: u64 = Square::E1.into();
937
938 self.pieces[Piece::KING as usize] ^= e1;
939 self.pieces[Piece::KING as usize] |= c1;
940 self.white ^= e1;
941 self.white |= c1;
942
943 let a1: u64 = Square::A1.into();
944 let d1: u64 = Square::D1.into();
945
946 self.pieces[Piece::ROOK as usize] ^= a1;
947 self.pieces[Piece::ROOK as usize] |= d1;
948 self.white ^= a1;
949 self.white |= d1;
950
951 self.turn = !self.turn;
952 self.castle_rights[0] = false;
953 self.castle_rights[1] = false;
954 has_moved = true;
955 }
956 }
957 }
958 false => {
959 if let Some(legal_moves) = self.legal_moves.get(&Square::E8.into()) {
960 let c8: u64 = Square::C8.into();
961
962 if legal_moves & c8 != 0 {
963 self.half_move += 1;
964 let e8: u64 = Square::E8.into();
965
966 self.pieces[Piece::KING as usize] ^= e8;
967 self.pieces[Piece::KING as usize] |= c8;
968 self.black ^= e8;
969 self.black |= c8;
970
971 let d8: u64 = Square::D8.into();
972 let a8: u64 = Square::A8.into();
973
974 self.pieces[Piece::ROOK as usize] ^= a8;
975 self.pieces[Piece::ROOK as usize] |= d8;
976 self.black ^= a8;
977 self.black |= d8;
978
979 self.turn = !self.turn;
980 self.full_move += 1;
981 self.castle_rights[2] = false;
982 self.castle_rights[3] = false;
983 has_moved = true;
984 }
985 }
986 }
987 },
988 }
989 } else if let Some(promotion_piece) = valid_san.promotion {
990 'search: for (&from_square, &legal_moves) in self.legal_moves.iter() {
991 let piece = self.get_piece(from_square);
992 let color = self.white & from_square != 0;
993
994 if self.turn != color {
995 continue;
996 }
997
998 match piece {
999 Piece::PAWN => {
1000 for valid_square in Chessboard::get_squares(legal_moves) {
1001 if valid_square & valid_san.to != 0 {
1002 self.half_move = 0;
1003
1004 let before = self.get_fen();
1005 let mut captured = None;
1006
1007 self.pieces[piece as usize] ^= from_square;
1008
1009 match color {
1010 true => {
1011 self.white ^= from_square;
1012 self.white |= valid_square;
1013
1014 if self.black & valid_square != 0 {
1015 let captured_piece = self.get_piece(valid_square);
1016 self.pieces[captured_piece as usize] ^=
1017 valid_square;
1018 self.black ^= valid_square;
1019 captured = Some(captured_piece);
1020 }
1021 }
1022 false => {
1023 self.black ^= from_square;
1024 self.black |= valid_square;
1025
1026 if self.white & valid_square != 0 {
1027 let captured_piece = self.get_piece(valid_square);
1028 self.pieces[captured_piece as usize] ^=
1029 valid_square;
1030 self.white ^= valid_square;
1031 captured = Some(captured_piece);
1032 }
1033 }
1034 }
1035
1036 self.pieces[promotion_piece as usize] |= valid_square;
1037
1038 let after = self.get_fen();
1039
1040 self.board_repetitions
1041 .entry(after.clone())
1042 .and_modify(|count| *count += 1)
1043 .or_insert(1);
1044
1045 self.history.push(ChessMove::new(
1046 valid_san,
1047 self.turn,
1048 before,
1049 after,
1050 from_square,
1051 captured,
1052 ));
1053
1054 self.turn = !self.turn;
1055 has_moved = true;
1056 break 'search;
1057 }
1058 }
1059 }
1060 _ => {
1061 continue;
1062 }
1063 }
1064 }
1065 } else {
1066 'search: for (&from_square, &legal_moves) in self.legal_moves.iter() {
1067 let piece = self.get_piece(from_square);
1068
1069 if (piece != valid_san.piece)
1070 || (valid_san.from > 0 && from_square & valid_san.from == 0)
1071 {
1072 continue;
1073 }
1074
1075 for valid_square in Chessboard::get_squares(legal_moves) {
1076 if valid_square & valid_san.to != 0 {
1077 let before = self.get_fen();
1078 let mut captured = None;
1079
1080 if let Some(en_passant_square) = self.en_passant_square.take() {
1081 if valid_square == en_passant_square && piece == Piece::PAWN {
1082 self.pieces[Piece::PAWN as usize] ^= en_passant_square;
1083 match self.turn {
1084 true => {
1085 self.black ^= en_passant_square >> 8;
1086 self.pieces[Piece::PAWN as usize] ^=
1087 en_passant_square >> 8;
1088 }
1089 false => {
1090 self.white ^= en_passant_square << 8;
1091 self.pieces[Piece::PAWN as usize] ^=
1092 en_passant_square << 8;
1093 }
1094 }
1095 }
1096 }
1097
1098 match piece {
1099 Piece::KING => match self.turn {
1100 true => {
1101 self.castle_rights[0] = false;
1102 self.castle_rights[1] = false;
1103 }
1104 false => {
1105 self.castle_rights[2] = false;
1106 self.castle_rights[3] = false;
1107 }
1108 },
1109 Piece::ROOK => match self.turn {
1110 true => {
1111 let h1: u64 = Square::H1.into();
1112 let a1: u64 = Square::A1.into();
1113 if from_square & h1 != 0 {
1114 self.castle_rights[0] = false;
1115 } else if from_square & a1 != 0 {
1116 self.castle_rights[1] = false;
1117 }
1118 }
1119 false => {
1120 let h8: u64 = Square::H8.into();
1121 let a8: u64 = Square::A8.into();
1122 if from_square & h8 != 0 {
1123 self.castle_rights[2] = false;
1124 } else if from_square & a8 != 0 {
1125 self.castle_rights[3] = false;
1126 }
1127 }
1128 },
1129 Piece::PAWN => match self.turn {
1130 true => {
1131 if from_square & RANK_2 != 0 {
1132 if valid_square & RANK_4 != 0 {
1133 let black_pawns =
1134 self.pieces[Piece::PAWN as usize] & self.black;
1135 if valid_square & FILE_A == 0
1136 && black_pawns & (valid_square >> 1) != 0
1137 {
1138 self.en_passant_square =
1139 Some(valid_square >> 8);
1140 }
1141
1142 if valid_square & FILE_H == 0
1143 && black_pawns & (valid_square << 1) != 0
1144 {
1145 self.en_passant_square =
1146 Some(valid_square >> 8);
1147 }
1148 }
1149 }
1150 }
1151 false => {
1152 if from_square & RANK_7 != 0 {
1153 if valid_square & RANK_5 != 0 {
1154 let white_pawns =
1155 self.pieces[Piece::PAWN as usize] & self.white;
1156 if valid_square & FILE_A == 0
1157 && white_pawns & valid_square >> 1 != 0
1158 {
1159 self.en_passant_square =
1160 Some(valid_square << 8);
1161 }
1162
1163 if valid_square & FILE_H == 0
1164 && white_pawns & valid_square << 1 != 0
1165 {
1166 self.en_passant_square =
1167 Some(valid_square << 8);
1168 }
1169 }
1170 }
1171 }
1172 },
1173 _ => {}
1174 }
1175
1176 self.pieces[piece as usize] ^= from_square;
1177
1178 match self.turn {
1179 true => {
1180 self.white ^= from_square;
1181 self.white |= valid_square;
1182
1183 if self.black & valid_square != 0 {
1184 let captured_piece = self.get_piece(valid_square);
1185 self.pieces[captured_piece as usize] ^= valid_square;
1186 self.black ^= valid_square;
1187 self.half_move = 0;
1188 captured = Some(captured_piece);
1189 } else {
1190 match piece {
1191 Piece::PAWN => {
1192 self.half_move = 0;
1193 }
1194 _ => {
1195 self.half_move += 1;
1196 }
1197 }
1198 }
1199 }
1200 false => {
1201 self.black ^= from_square;
1202 self.black |= valid_square;
1203
1204 if self.white & valid_square != 0 {
1205 let captured_piece = self.get_piece(valid_square);
1206 self.pieces[captured_piece as usize] ^= valid_square;
1207 self.white ^= valid_square;
1208 self.half_move = 0;
1209 captured = Some(captured_piece);
1210 } else {
1211 match piece {
1212 Piece::PAWN => {
1213 self.half_move = 0;
1214 }
1215 _ => {
1216 self.half_move += 1;
1217 }
1218 }
1219 }
1220
1221 self.full_move += 1;
1222 }
1223 }
1224
1225 self.pieces[piece as usize] |= valid_square;
1226 let after = self.get_fen();
1227
1228 self.board_repetitions
1229 .entry(after.split_whitespace().next().unwrap().to_string())
1230 .and_modify(|count| *count += 1)
1231 .or_insert(1);
1232
1233 self.history.push(ChessMove::new(
1234 valid_san,
1235 self.turn,
1236 before,
1237 after,
1238 from_square,
1239 captured,
1240 ));
1241
1242 self.turn = !self.turn;
1243 has_moved = true;
1244 break 'search;
1245 }
1246 }
1247 }
1248 }
1249 if has_moved {
1250 self.generate_legal_moves();
1251 } else {
1252 println!(
1253 "Invalid move {} to {}",
1254 valid_san.piece,
1255 Square::from(valid_san.to)
1256 );
1257 }
1258 }
1259 }
1260
1261 fn get_squares(bitboard: u64) -> Vec<u64> {
1262 let mut squares = Vec::new();
1263 for i in 0..64 {
1264 let square = 1 << i;
1265 if bitboard & square != 0 {
1266 squares.push(square);
1267 }
1268 }
1269 squares
1270 }
1271
1272 pub fn ascii(&self) -> String {
1280 let mut board = String::new();
1281 for rank in 0..8 {
1282 for file in 0..8 {
1283 let square = 1 << 56 - rank as u64 * 8 + file;
1284 let piece = self.get_piece(square);
1285 let color = self.white & square != 0;
1286 match piece {
1287 Piece::PAWN => match color {
1288 true => {
1289 board.push('P');
1290 }
1291 false => {
1292 board.push('p');
1293 }
1294 },
1295 Piece::KNIGHT => match color {
1296 true => {
1297 board.push('N');
1298 }
1299 false => {
1300 board.push('n');
1301 }
1302 },
1303 Piece::BISHOP => match color {
1304 true => {
1305 board.push('B');
1306 }
1307 false => {
1308 board.push('b');
1309 }
1310 },
1311 Piece::ROOK => match color {
1312 true => {
1313 board.push('R');
1314 }
1315 false => {
1316 board.push('r');
1317 }
1318 },
1319 Piece::QUEEN => match color {
1320 true => {
1321 board.push('Q');
1322 }
1323 false => {
1324 board.push('q');
1325 }
1326 },
1327 Piece::KING => match color {
1328 true => {
1329 board.push('K');
1330 }
1331 false => {
1332 board.push('k');
1333 }
1334 },
1335 _ => {
1336 board.push('.');
1337 }
1338 }
1339 }
1340 board.push('\n');
1341 }
1342 board
1343 }
1344}
1345
1346impl std::fmt::Display for Chessboard {
1347 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1348 write!(f, "{}", self.ascii())
1349 }
1350}
1351
1352#[cfg(test)]
1353mod tests {
1354 use super::*;
1355
1356 #[test]
1357 fn test_generate_moves() {
1358 let board = Chessboard::from_fen("8/4PnK1/4P3/2p3p1/1p2BPk1/P7/2pR2PB/5n2 w - - 0 1");
1359
1360 for (&square, &valid_move) in board.legal_moves.iter() {
1361 let board_square: u64 = square.into();
1362 let color: bool = board.white & board_square != 0;
1363 if color != board.turn {
1364 continue;
1365 }
1366 println!(
1367 "piece : {}, square:{}",
1368 board.get_piece(square.into()),
1369 square
1370 );
1371 for square in Chessboard::get_squares(valid_move) {
1372 println!("valid_move:{}", square);
1373 }
1374 }
1375 }
1376
1377 #[test]
1378 fn get_fen_works() {
1379 let fen = "8/PK4N1/P1p2rn1/7p/1P1B3P/2P5/p1NR4/5k2 w - - 0 1";
1380 let board = Chessboard::from_fen(fen);
1381
1382 assert_eq!(board.get_fen(), fen);
1383 }
1384
1385 #[test]
1386 fn test_mate() {
1387 let board =
1388 Chessboard::from_fen("r1bqkbnr/pppp1Qpp/8/4p3/1nB1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 4");
1389
1390 assert_eq!(board.is_mate(false), true);
1391 }
1392
1393 #[test]
1394 fn test_move_to() {
1395 let mut board = Chessboard::new();
1396
1397 board.move_to("f4");
1398 board.move_to("f5");
1399 board.move_to("Nf3");
1400 board.move_to("Nc6");
1401 board.move_to("e4");
1402 board.move_to("e5");
1403 board.move_to("Qe2");
1404 board.move_to("Bb4");
1405 board.move_to("d4");
1406 board.move_to("c3");
1407 println!("{}", board.get_fen());
1408 }
1409
1410 #[test]
1411 fn test_random_board() {
1412 let mut board =
1413 Chessboard::from_fen("3B4/R2p1P2/3p2p1/5NP1/2K1n3/1Q6/1p3R1p/2n1k3 w - - 0 1");
1414
1415 println!("{}", board.ascii());
1416
1417 board.move_to("Qe3");
1418 board.move_to("Kd1");
1419 board.move_to("Rf1");
1420 board.move_to("Kc2");
1421 board.move_to("Qe4");
1422 board.move_to("Kd2");
1423 board.move_to("Ba5");
1424
1425 println!("{}", board.get_fen());
1426 println!("{}", board.ascii());
1427
1428 assert!(board.is_mate(false));
1429 }
1430
1431 #[test]
1432 fn castle_white_queen_side() {
1433 let fen = "rnb1kbnr/pp2pppp/8/1q6/8/8/P3PPPP/R3K1NR w KQkq - 0 1";
1434 let mut board = Chessboard::from_fen(fen);
1435
1436 board.move_to("O-O-O");
1437
1438 assert_eq!(
1439 board.get_fen(),
1440 "rnb1kbnr/pp2pppp/8/1q6/8/8/P3PPPP/2KR2NR b kq - 1 1"
1441 );
1442 }
1443
1444 #[test]
1445 fn castle_white_king_side() {
1446 let fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPP2P/RNBQK2R w KQkq - 0 1";
1447 let mut board = Chessboard::from_fen(fen);
1448
1449 board.move_to("O-O");
1450
1451 assert_eq!(
1452 board.get_fen(),
1453 "rnbqkbnr/pppppppp/8/8/8/8/PPPPP2P/RNBQ1RK1 b kq - 1 1"
1454 );
1455 }
1456
1457 #[test]
1458 fn castle_black_queen_side() {
1459 let fen = "r3kbnr/p3pppp/8/8/1Q6/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1";
1460 let mut board = Chessboard::from_fen(fen);
1461
1462 board.move_to("O-O-O");
1463
1464 assert_eq!(
1465 board.get_fen(),
1466 "2kr1bnr/p3pppp/8/8/1Q6/8/PPPPPPPP/RNBQKBNR w KQ - 1 2"
1467 );
1468 }
1469
1470 #[test]
1471 fn castle_black_king_side() {
1472 let fen = "rnbqk2r/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1";
1473 let mut board = Chessboard::from_fen(fen);
1474
1475 board.move_to("O-O");
1476
1477 assert_eq!(
1478 board.get_fen(),
1479 "rnbq1rk1/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQ - 1 2"
1480 );
1481 }
1482
1483 #[test]
1484 fn test_en_passant() {
1485 let fen = "rnbqkbnr/pppp1ppp/8/8/4p3/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
1486
1487 let mut board = Chessboard::from_fen(fen);
1488
1489 board.move_to("d4");
1490 board.move_to("d3");
1491
1492 println!("{}", board.ascii());
1493 println!("{:?}", board.history);
1494
1495 assert_eq!(
1496 board.get_fen(),
1497 "rnbqkbnr/pppp1ppp/8/8/8/3p4/PPP1PPPP/RNBQKBNR w KQkq - 0 2"
1498 );
1499 }
1500
1501 #[test]
1502 fn test_three_fold() {
1503 let mut board = Chessboard::new();
1504
1505 board.move_to("Nf3");
1506 board.move_to("Nf6");
1507
1508 board.move_to("Ng1");
1509 board.move_to("Ng8");
1510
1511 board.move_to("Nf3");
1512 board.move_to("Nf6");
1513
1514 board.move_to("Ng1");
1515 board.move_to("Ng8");
1516
1517 assert!(board.is_threefold_repetition());
1518 assert_eq!(
1519 board.get_fen(),
1520 "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 8 5"
1521 );
1522 }
1523
1524 #[test]
1525 fn test_promotion() {
1526 let fen = "2b3k1/3PR3/8/8/8/8/8/6K1 w - - 0 1";
1527 let mut board = Chessboard::from_fen(fen);
1528
1529 println!("{}", board.ascii());
1530
1531 board.move_to("dxc8=Q#");
1532
1533 println!("{}", board.ascii());
1534
1535 assert_eq!(board.get_fen(), "2Q3k1/4R3/8/8/8/8/8/6K1 b - - 0 1");
1536
1537 assert!(board.is_mate(false));
1538 }
1539}