chess/board.rs
1use crate::bitboard::{BitBoard, EMPTY};
2use crate::board_builder::BoardBuilder;
3use crate::castle_rights::CastleRights;
4use crate::chess_move::ChessMove;
5use crate::color::{Color, ALL_COLORS, NUM_COLORS};
6use crate::error::Error;
7use crate::file::File;
8use crate::magic::{
9 between, get_adjacent_files, get_bishop_rays, get_castle_moves, get_file, get_king_moves,
10 get_knight_moves, get_pawn_attacks, get_pawn_dest_double_moves, get_pawn_source_double_moves,
11 get_rank, get_rook_rays,
12};
13use crate::movegen::*;
14use crate::piece::{Piece, ALL_PIECES, NUM_PIECES};
15use crate::square::{Square, ALL_SQUARES};
16use crate::zobrist::Zobrist;
17use std::convert::{TryFrom, TryInto};
18use std::fmt;
19use std::hash::{Hash, Hasher};
20use std::mem;
21use std::str::FromStr;
22
23/// A representation of a chess board. That's why you're here, right?
24#[derive(Copy, Clone, PartialEq, Eq, Debug)]
25pub struct Board {
26 pieces: [BitBoard; NUM_PIECES],
27 color_combined: [BitBoard; NUM_COLORS],
28 combined: BitBoard,
29 side_to_move: Color,
30 castle_rights: [CastleRights; NUM_COLORS],
31 pinned: BitBoard,
32 checkers: BitBoard,
33 hash: u64,
34 en_passant: Option<Square>,
35}
36
37/// What is the status of this game?
38#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
39pub enum BoardStatus {
40 Ongoing,
41 Stalemate,
42 Checkmate,
43}
44
45/// Construct the initial position.
46impl Default for Board {
47 #[inline]
48 fn default() -> Board {
49 Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
50 .expect("Valid Position")
51 }
52}
53
54impl Hash for Board {
55 fn hash<H: Hasher>(&self, state: &mut H) {
56 self.hash.hash(state);
57 }
58}
59
60impl Board {
61 /// Construct a new `Board` that is completely empty.
62 /// Note: This does NOT give you the initial position. Just a blank slate.
63 fn new() -> Board {
64 Board {
65 pieces: [EMPTY; NUM_PIECES],
66 color_combined: [EMPTY; NUM_COLORS],
67 combined: EMPTY,
68 side_to_move: Color::White,
69 castle_rights: [CastleRights::NoRights; NUM_COLORS],
70 pinned: EMPTY,
71 checkers: EMPTY,
72 hash: 0,
73 en_passant: None,
74 }
75 }
76
77 /// Construct a board from a FEN string.
78 ///
79 /// ```
80 /// use chess::Board;
81 /// use std::str::FromStr;
82 /// # use chess::Error;
83 ///
84 /// # fn main() -> Result<(), Error> {
85 ///
86 /// // This is no longer supported
87 /// let init_position = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1".to_owned()).expect("Valid FEN");
88 /// assert_eq!(init_position, Board::default());
89 ///
90 /// // This is the new way
91 /// let init_position_2 = Board::from_str("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")?;
92 /// assert_eq!(init_position_2, Board::default());
93 /// # Ok(())
94 /// # }
95 /// ```
96 #[deprecated(since = "3.1.0", note = "please use `Board::from_str(fen)?` instead")]
97 #[inline]
98 pub fn from_fen(fen: String) -> Option<Board> {
99 Board::from_str(&fen).ok()
100 }
101
102 #[deprecated(
103 since = "3.0.0",
104 note = "please use the MoveGen structure instead. It is faster and more idiomatic."
105 )]
106 #[inline]
107 pub fn enumerate_moves(&self, moves: &mut [ChessMove; 256]) -> usize {
108 let movegen = MoveGen::new_legal(self);
109 let mut size = 0;
110 for m in movegen {
111 moves[size] = m;
112 size += 1;
113 }
114 size
115 }
116
117 /// Is this game Ongoing, is it Stalemate, or is it Checkmate?
118 ///
119 /// ```
120 /// use chess::{Board, BoardStatus, Square, ChessMove};
121 ///
122 /// let mut board = Board::default();
123 ///
124 /// assert_eq!(board.status(), BoardStatus::Ongoing);
125 ///
126 /// board = board.make_move_new(ChessMove::new(Square::E2,
127 /// Square::E4,
128 /// None));
129 ///
130 /// assert_eq!(board.status(), BoardStatus::Ongoing);
131 ///
132 /// board = board.make_move_new(ChessMove::new(Square::F7,
133 /// Square::F6,
134 /// None));
135 ///
136 /// assert_eq!(board.status(), BoardStatus::Ongoing);
137 ///
138 /// board = board.make_move_new(ChessMove::new(Square::D2,
139 /// Square::D4,
140 /// None));
141 ///
142 /// assert_eq!(board.status(), BoardStatus::Ongoing);
143 ///
144 /// board = board.make_move_new(ChessMove::new(Square::G7,
145 /// Square::G5,
146 /// None));
147 ///
148 /// assert_eq!(board.status(), BoardStatus::Ongoing);
149 ///
150 /// board = board.make_move_new(ChessMove::new(Square::D1,
151 /// Square::H5,
152 /// None));
153 ///
154 /// assert_eq!(board.status(), BoardStatus::Checkmate);
155 /// ```
156 #[inline]
157 pub fn status(&self) -> BoardStatus {
158 let moves = MoveGen::new_legal(&self).len();
159 match moves {
160 0 => {
161 if self.checkers == EMPTY {
162 BoardStatus::Stalemate
163 } else {
164 BoardStatus::Checkmate
165 }
166 }
167 _ => BoardStatus::Ongoing,
168 }
169 }
170
171 /// Grab the "combined" `BitBoard`. This is a `BitBoard` with every piece.
172 ///
173 /// ```
174 /// use chess::{Board, BitBoard, Rank, get_rank};
175 ///
176 /// let board = Board::default();
177 ///
178 /// let combined_should_be = get_rank(Rank::First) |
179 /// get_rank(Rank::Second) |
180 /// get_rank(Rank::Seventh) |
181 /// get_rank(Rank::Eighth);
182 ///
183 /// assert_eq!(*board.combined(), combined_should_be);
184 /// ```
185 #[inline]
186 pub fn combined(&self) -> &BitBoard {
187 &self.combined
188 }
189
190 /// Grab the "color combined" `BitBoard`. This is a `BitBoard` with every piece of a particular
191 /// color.
192 ///
193 /// ```
194 /// use chess::{Board, BitBoard, Rank, get_rank, Color};
195 ///
196 /// let board = Board::default();
197 ///
198 /// let white_pieces = get_rank(Rank::First) |
199 /// get_rank(Rank::Second);
200 ///
201 /// let black_pieces = get_rank(Rank::Seventh) |
202 /// get_rank(Rank::Eighth);
203 ///
204 /// assert_eq!(*board.color_combined(Color::White), white_pieces);
205 /// assert_eq!(*board.color_combined(Color::Black), black_pieces);
206 /// ```
207 #[inline]
208 pub fn color_combined(&self, color: Color) -> &BitBoard {
209 unsafe { self.color_combined.get_unchecked(color.to_index()) }
210 }
211
212 /// Give me the `Square` the `color` king is on.
213 ///
214 /// ```
215 /// use chess::{Board, Square, Color};
216 ///
217 /// let board = Board::default();
218 ///
219 /// assert_eq!(board.king_square(Color::White), Square::E1);
220 /// assert_eq!(board.king_square(Color::Black), Square::E8);
221 /// ```
222 #[inline]
223 pub fn king_square(&self, color: Color) -> Square {
224 (self.pieces(Piece::King) & self.color_combined(color)).to_square()
225 }
226
227 /// Grab the "pieces" `BitBoard`. This is a `BitBoard` with every piece of a particular type.
228 ///
229 /// ```
230 /// use chess::{Board, BitBoard, Piece, Square};
231 ///
232 /// // The rooks should be in each corner of the board
233 /// let rooks = BitBoard::from_square(Square::A1) |
234 /// BitBoard::from_square(Square::H1) |
235 /// BitBoard::from_square(Square::A8) |
236 /// BitBoard::from_square(Square::H8);
237 ///
238 /// let board = Board::default();
239 ///
240 /// assert_eq!(*board.pieces(Piece::Rook), rooks);
241 /// ```
242 #[inline]
243 pub fn pieces(&self, piece: Piece) -> &BitBoard {
244 unsafe { self.pieces.get_unchecked(piece.to_index()) }
245 }
246
247 /// Grab the `CastleRights` for a particular side.
248 ///
249 /// ```
250 /// use chess::{Board, Square, CastleRights, Color, ChessMove};
251 ///
252 /// let move1 = ChessMove::new(Square::A2,
253 /// Square::A4,
254 /// None);
255 ///
256 /// let move2 = ChessMove::new(Square::E7,
257 /// Square::E5,
258 /// None);
259 ///
260 /// let move3 = ChessMove::new(Square::A1,
261 /// Square::A2,
262 /// None);
263 ///
264 /// let move4 = ChessMove::new(Square::E8,
265 /// Square::E7,
266 /// None);
267 ///
268 /// let mut board = Board::default();
269 ///
270 /// assert_eq!(board.castle_rights(Color::White), CastleRights::Both);
271 /// assert_eq!(board.castle_rights(Color::Black), CastleRights::Both);
272 ///
273 /// board = board.make_move_new(move1)
274 /// .make_move_new(move2)
275 /// .make_move_new(move3)
276 /// .make_move_new(move4);
277 ///
278 /// assert_eq!(board.castle_rights(Color::White), CastleRights::KingSide);
279 /// assert_eq!(board.castle_rights(Color::Black), CastleRights::NoRights);
280 /// ```
281 #[inline]
282 pub fn castle_rights(&self, color: Color) -> CastleRights {
283 unsafe { *self.castle_rights.get_unchecked(color.to_index()) }
284 }
285
286 /// Add castle rights for a particular side. Note: this can create an invalid position.
287 #[deprecated(
288 since = "3.1.0",
289 note = "When doing board setup, use the BoardBuilder structure. It ensures you don't end up with an invalid position."
290 )]
291 #[inline]
292 pub fn add_castle_rights(&mut self, color: Color, add: CastleRights) {
293 unsafe {
294 *self.castle_rights.get_unchecked_mut(color.to_index()) =
295 self.castle_rights(color).add(add);
296 }
297 }
298
299 /// Remove castle rights for a particular side.
300 ///
301 /// ```
302 /// use chess::{Board, CastleRights, Color};
303 ///
304 /// let mut board = Board::default();
305 /// assert_eq!(board.castle_rights(Color::White), CastleRights::Both);
306 ///
307 /// board.remove_castle_rights(Color::White, CastleRights::KingSide);
308 /// assert_eq!(board.castle_rights(Color::White), CastleRights::QueenSide);
309 /// ```
310 #[deprecated(
311 since = "3.1.0",
312 note = "When doing board setup, use the BoardBuilder structure. It ensures you don't end up with an invalid position."
313 )]
314 #[inline]
315 pub fn remove_castle_rights(&mut self, color: Color, remove: CastleRights) {
316 unsafe {
317 *self.castle_rights.get_unchecked_mut(color.to_index()) =
318 self.castle_rights(color).remove(remove);
319 }
320 }
321
322 /// Who's turn is it?
323 ///
324 /// ```
325 /// use chess::{Board, Color};
326 ///
327 /// let mut board = Board::default();
328 /// assert_eq!(board.side_to_move(), Color::White);
329 /// ```
330 #[inline]
331 pub fn side_to_move(&self) -> Color {
332 self.side_to_move
333 }
334
335 /// Grab my `CastleRights`.
336 ///
337 /// ```
338 /// use chess::{Board, Color, CastleRights};
339 ///
340 /// let mut board = Board::default();
341 /// board.remove_castle_rights(Color::White, CastleRights::KingSide);
342 /// board.remove_castle_rights(Color::Black, CastleRights::QueenSide);
343 ///
344 /// assert_eq!(board.my_castle_rights(), board.castle_rights(Color::White));
345 /// ```
346 #[inline]
347 pub fn my_castle_rights(&self) -> CastleRights {
348 self.castle_rights(self.side_to_move())
349 }
350
351 /// Add to my `CastleRights`. Note: This can make the position invalid.
352 #[deprecated(
353 since = "3.1.0",
354 note = "When doing board setup, use the BoardBuilder structure. It ensures you don't end up with an invalid position."
355 )]
356 #[inline]
357 pub fn add_my_castle_rights(&mut self, add: CastleRights) {
358 let color = self.side_to_move();
359 #[allow(deprecated)]
360 self.add_castle_rights(color, add);
361 }
362
363 /// Remove some of my `CastleRights`.
364 ///
365 /// ```
366 /// use chess::{Board, CastleRights};
367 ///
368 /// let mut board = Board::default();
369 /// assert_eq!(board.my_castle_rights(), CastleRights::Both);
370 ///
371 /// board.remove_my_castle_rights(CastleRights::KingSide);
372 /// assert_eq!(board.my_castle_rights(), CastleRights::QueenSide);
373 /// ```
374 #[deprecated(
375 since = "3.1.0",
376 note = "When doing board setup, use the BoardBuilder structure. It ensures you don't end up with an invalid position."
377 )]
378 #[inline]
379 pub fn remove_my_castle_rights(&mut self, remove: CastleRights) {
380 let color = self.side_to_move();
381 #[allow(deprecated)]
382 self.remove_castle_rights(color, remove);
383 }
384
385 /// My opponents `CastleRights`.
386 ///
387 /// ```
388 /// use chess::{Board, Color, CastleRights};
389 ///
390 /// let mut board = Board::default();
391 /// board.remove_castle_rights(Color::White, CastleRights::KingSide);
392 /// board.remove_castle_rights(Color::Black, CastleRights::QueenSide);
393 ///
394 /// assert_eq!(board.their_castle_rights(), board.castle_rights(Color::Black));
395 /// ```
396 #[inline]
397 pub fn their_castle_rights(&self) -> CastleRights {
398 self.castle_rights(!self.side_to_move())
399 }
400
401 /// Add to my opponents `CastleRights`. Note: This can make the position invalid.
402 #[deprecated(
403 since = "3.1.0",
404 note = "When doing board setup, use the BoardBuilder structure. It ensures you don't end up with an invalid position."
405 )]
406 #[inline]
407 pub fn add_their_castle_rights(&mut self, add: CastleRights) {
408 let color = !self.side_to_move();
409 #[allow(deprecated)]
410 self.add_castle_rights(color, add)
411 }
412
413 /// Remove some of my opponents `CastleRights`.
414 ///
415 /// ```
416 /// use chess::{Board, CastleRights};
417 ///
418 /// let mut board = Board::default();
419 /// assert_eq!(board.their_castle_rights(), CastleRights::Both);
420 ///
421 /// board.remove_their_castle_rights(CastleRights::KingSide);
422 /// assert_eq!(board.their_castle_rights(), CastleRights::QueenSide);
423 /// ```
424 #[deprecated(
425 since = "3.1.0",
426 note = "When doing board setup, use the BoardBuilder structure. It ensures you don't end up with an invalid position."
427 )]
428 #[inline]
429 pub fn remove_their_castle_rights(&mut self, remove: CastleRights) {
430 let color = !self.side_to_move();
431 #[allow(deprecated)]
432 self.remove_castle_rights(color, remove);
433 }
434
435 /// Add or remove a piece from the bitboards in this struct.
436 fn xor(&mut self, piece: Piece, bb: BitBoard, color: Color) {
437 unsafe {
438 *self.pieces.get_unchecked_mut(piece.to_index()) ^= bb;
439 *self.color_combined.get_unchecked_mut(color.to_index()) ^= bb;
440 self.combined ^= bb;
441 self.hash ^= Zobrist::piece(piece, bb.to_square(), color);
442 }
443 }
444
445 /// For a chess UI: set a piece on a particular square.
446 ///
447 /// ```
448 /// use chess::{Board, Piece, Color, Square};
449 ///
450 /// let board = Board::default();
451 ///
452 /// let new_board = board.set_piece(Piece::Queen,
453 /// Color::White,
454 /// Square::E4)
455 /// .expect("Valid Position");
456 ///
457 /// assert_eq!(new_board.pieces(Piece::Queen).count(), 3);
458 /// ```
459 #[deprecated(
460 since = "3.1.0",
461 note = "When doing board setup, use the BoardBuilder structure. It ensures you don't end up with an invalid position."
462 )]
463 #[inline]
464 pub fn set_piece(&self, piece: Piece, color: Color, square: Square) -> Option<Board> {
465 let mut result = *self;
466 let square_bb = BitBoard::from_square(square);
467 match self.piece_on(square) {
468 None => result.xor(piece, square_bb, color),
469 Some(x) => {
470 // remove x from the bitboard
471 if self.color_combined(Color::White) & square_bb == square_bb {
472 result.xor(x, square_bb, Color::White);
473 } else {
474 result.xor(x, square_bb, Color::Black);
475 }
476 // add piece to the bitboard
477 result.xor(piece, square_bb, color);
478 }
479 }
480
481 // If setting this piece down leaves my opponent in check, and it's my move, then the
482 // position is not a valid chess board
483 result.side_to_move = !result.side_to_move;
484 result.update_pin_info();
485 if result.checkers != EMPTY {
486 return None;
487 }
488
489 // undo our damage
490 result.side_to_move = !result.side_to_move;
491 result.update_pin_info();
492
493 Some(result)
494 }
495
496 /// For a chess UI: clear a particular square.
497 ///
498 /// ```
499 /// use chess::{Board, Square, Piece};
500 ///
501 /// let board = Board::default();
502 ///
503 /// let new_board = board.clear_square(Square::A1)
504 /// .expect("Valid Position");
505 ///
506 /// assert_eq!(new_board.pieces(Piece::Rook).count(), 3);
507 /// ```
508 #[deprecated(
509 since = "3.1.0",
510 note = "When doing board setup, use the BoardBuilder structure. It ensures you don't end up with an invalid position."
511 )]
512 #[inline]
513 pub fn clear_square(&self, square: Square) -> Option<Board> {
514 let mut result = *self;
515 let square_bb = BitBoard::from_square(square);
516 match self.piece_on(square) {
517 None => {}
518 Some(x) => {
519 // remove x from the bitboard
520 if self.color_combined(Color::White) & square_bb == square_bb {
521 result.xor(x, square_bb, Color::White);
522 } else {
523 result.xor(x, square_bb, Color::Black);
524 }
525 }
526 }
527
528 // If setting this piece down leaves my opponent in check, and it's my move, then the
529 // position is not a valid chess board
530 result.side_to_move = !result.side_to_move;
531 result.update_pin_info();
532 if result.checkers != EMPTY {
533 return None;
534 }
535
536 // undo our damage
537 result.side_to_move = !result.side_to_move;
538 result.update_pin_info();
539
540 Some(result)
541 }
542
543 /// Switch the color of the player without actually making a move. Returns None if the current
544 /// player is in check.
545 ///
546 /// Note that this erases the en-passant information, so applying this function twice does not
547 /// always give the same result back.
548 ///
549 /// ```
550 /// use chess::{Board, Color};
551 ///
552 /// let board = Board::default();
553 ///
554 /// assert_eq!(board.side_to_move(), Color::White);
555 ///
556 /// let new_board = board.null_move().expect("Valid Position");
557 ///
558 /// assert_eq!(new_board.side_to_move(), Color::Black);
559 /// ```
560 #[inline]
561 pub fn null_move(&self) -> Option<Board> {
562 if self.checkers != EMPTY {
563 None
564 } else {
565 let mut result = *self;
566 result.side_to_move = !result.side_to_move;
567 result.remove_ep();
568 result.update_pin_info();
569 Some(result)
570 }
571 }
572
573 /// Does this board "make sense"?
574 /// Do all the pieces make sense, do the bitboards combine correctly, etc?
575 /// This is for sanity checking.
576 ///
577 /// ```
578 /// use chess::{Board, Color, Piece, Square};
579 ///
580 /// let board = Board::default();
581 ///
582 /// assert_eq!(board.is_sane(), true);
583 ///
584 /// // Remove the king
585 /// let bad_board = board.clear_square(Square::E1).expect("Valid Position");
586 /// assert_eq!(bad_board.is_sane(), false);
587 /// ```
588 pub fn is_sane(&self) -> bool {
589 // make sure there is no square with multiple pieces on it
590 for x in ALL_PIECES.iter() {
591 for y in ALL_PIECES.iter() {
592 if *x != *y {
593 if self.pieces(*x) & self.pieces(*y) != EMPTY {
594 return false;
595 }
596 }
597 }
598 }
599
600 // make sure the colors don't overlap, either
601 if self.color_combined(Color::White) & self.color_combined(Color::Black) != EMPTY {
602 return false;
603 }
604
605 // grab all the pieces by OR'ing together each piece() BitBoard
606 let combined = ALL_PIECES
607 .iter()
608 .fold(EMPTY, |cur, next| cur | self.pieces(*next));
609
610 // make sure that's equal to the combined bitboard
611 if combined != *self.combined() {
612 return false;
613 }
614
615 // make sure there is exactly one white king
616 if (self.pieces(Piece::King) & self.color_combined(Color::White)).popcnt() != 1 {
617 return false;
618 }
619
620 // make sure there is exactly one black king
621 if (self.pieces(Piece::King) & self.color_combined(Color::Black)).popcnt() != 1 {
622 return false;
623 }
624
625 // make sure the en_passant square has a pawn on it of the right color
626 match self.en_passant {
627 None => {}
628 Some(x) => {
629 if self.pieces(Piece::Pawn)
630 & self.color_combined(!self.side_to_move)
631 & BitBoard::from_square(x)
632 == EMPTY
633 {
634 return false;
635 }
636 }
637 }
638
639 // make sure my opponent is not currently in check (because that would be illegal)
640 let mut board_copy = *self;
641 board_copy.side_to_move = !board_copy.side_to_move;
642 board_copy.update_pin_info();
643 if board_copy.checkers != EMPTY {
644 return false;
645 }
646
647 // for each color, verify that, if they have castle rights, that they haven't moved their
648 // rooks or king
649 for color in ALL_COLORS.iter() {
650 // get the castle rights
651 let castle_rights = self.castle_rights(*color);
652
653 // the castle rights object will tell us which rooks shouldn't have moved yet.
654 // verify there are rooks on all those squares
655 if castle_rights.unmoved_rooks(*color)
656 & self.pieces(Piece::Rook)
657 & self.color_combined(*color)
658 != castle_rights.unmoved_rooks(*color)
659 {
660 return false;
661 }
662 // if we have castle rights, make sure we have a king on the (E, {1,8}) square,
663 // depending on the color
664 if castle_rights != CastleRights::NoRights {
665 if self.pieces(Piece::King) & self.color_combined(*color)
666 != get_file(File::E) & get_rank(color.to_my_backrank())
667 {
668 return false;
669 }
670 }
671 }
672
673 // we must make sure the kings aren't touching
674 if get_king_moves(self.king_square(Color::White)) & self.pieces(Piece::King) != EMPTY {
675 return false;
676 }
677
678 // it checks out
679 return true;
680 }
681
682 /// Get a hash of the board.
683 #[inline]
684 pub fn get_hash(&self) -> u64 {
685 self.hash
686 ^ if let Some(ep) = self.en_passant {
687 Zobrist::en_passant(ep.get_file(), !self.side_to_move)
688 } else {
689 0
690 }
691 ^ Zobrist::castles(
692 self.castle_rights[self.side_to_move.to_index()],
693 self.side_to_move,
694 )
695 ^ Zobrist::castles(
696 self.castle_rights[(!self.side_to_move).to_index()],
697 !self.side_to_move,
698 )
699 ^ if self.side_to_move == Color::Black {
700 Zobrist::color()
701 } else {
702 0
703 }
704 }
705
706 /// Get a pawn hash of the board (a hash that only changes on color change and pawn moves).
707 ///
708 /// Currently not implemented...
709 #[inline]
710 pub fn get_pawn_hash(&self) -> u64 {
711 0
712 }
713
714 /// What piece is on a particular `Square`? Is there even one?
715 ///
716 /// ```
717 /// use chess::{Board, Piece, Square};
718 ///
719 /// let board = Board::default();
720 ///
721 /// assert_eq!(board.piece_on(Square::A1), Some(Piece::Rook));
722 /// assert_eq!(board.piece_on(Square::D4), None);
723 /// ```
724 #[inline]
725 pub fn piece_on(&self, square: Square) -> Option<Piece> {
726 let opp = BitBoard::from_square(square);
727 if self.combined() & opp == EMPTY {
728 None
729 } else {
730 //naiive algorithm
731 /*
732 for p in ALL_PIECES {
733 if self.pieces(*p) & opp {
734 return p;
735 }
736 } */
737 if (self.pieces(Piece::Pawn) ^ self.pieces(Piece::Knight) ^ self.pieces(Piece::Bishop))
738 & opp
739 != EMPTY
740 {
741 if self.pieces(Piece::Pawn) & opp != EMPTY {
742 Some(Piece::Pawn)
743 } else if self.pieces(Piece::Knight) & opp != EMPTY {
744 Some(Piece::Knight)
745 } else {
746 Some(Piece::Bishop)
747 }
748 } else {
749 if self.pieces(Piece::Rook) & opp != EMPTY {
750 Some(Piece::Rook)
751 } else if self.pieces(Piece::Queen) & opp != EMPTY {
752 Some(Piece::Queen)
753 } else {
754 Some(Piece::King)
755 }
756 }
757 }
758 }
759
760 /// What color piece is on a particular square?
761 #[inline]
762 pub fn color_on(&self, square: Square) -> Option<Color> {
763 if (self.color_combined(Color::White) & BitBoard::from_square(square)) != EMPTY {
764 Some(Color::White)
765 } else if (self.color_combined(Color::Black) & BitBoard::from_square(square)) != EMPTY {
766 Some(Color::Black)
767 } else {
768 None
769 }
770 }
771
772 /// Unset the en_passant square.
773 fn remove_ep(&mut self) {
774 self.en_passant = None;
775 }
776
777 /// Give me the en_passant square, if it exists.
778 ///
779 /// ```
780 /// use chess::{Board, ChessMove, Square};
781 ///
782 /// let move1 = ChessMove::new(Square::D2,
783 /// Square::D4,
784 /// None);
785 ///
786 /// let move2 = ChessMove::new(Square::H7,
787 /// Square::H5,
788 /// None);
789 ///
790 /// let move3 = ChessMove::new(Square::D4,
791 /// Square::D5,
792 /// None);
793 ///
794 /// let move4 = ChessMove::new(Square::E7,
795 /// Square::E5,
796 /// None);
797 ///
798 /// let board = Board::default().make_move_new(move1)
799 /// .make_move_new(move2)
800 /// .make_move_new(move3)
801 /// .make_move_new(move4);
802 ///
803 /// assert_eq!(board.en_passant(), Some(Square::E5));
804 /// ```
805 #[inline]
806 pub fn en_passant(self) -> Option<Square> {
807 self.en_passant
808 }
809
810 /// Set the en_passant square. Note: This must only be called when self.en_passant is already
811 /// None.
812 fn set_ep(&mut self, sq: Square) {
813 // Only set self.en_passant if the pawn can actually be captured next move.
814 if get_adjacent_files(sq.get_file())
815 & get_rank(sq.get_rank())
816 & self.pieces(Piece::Pawn)
817 & self.color_combined(!self.side_to_move)
818 != EMPTY
819 {
820 self.en_passant = Some(sq);
821 }
822 }
823
824 /// Is a particular move legal? This function is very slow, but will work on unsanitized
825 /// input.
826 ///
827 /// ```
828 /// use chess::{Board, ChessMove, Square, MoveGen};
829 ///
830 /// let move1 = ChessMove::new(Square::E2,
831 /// Square::E4,
832 /// None);
833 ///
834 /// let move2 = ChessMove::new(Square::E2,
835 /// Square::E5,
836 /// None);
837 ///
838 /// let board = Board::default();
839 ///
840 /// assert_eq!(board.legal(move1), true);
841 /// assert_eq!(board.legal(move2), false);
842 /// ```
843 #[inline]
844 pub fn legal(&self, m: ChessMove) -> bool {
845 MoveGen::new_legal(&self).find(|x| *x == m).is_some()
846 }
847
848 /// Make a chess move onto a new board.
849 ///
850 /// panic!() if king is captured.
851 ///
852 /// ```
853 /// use chess::{Board, ChessMove, Square, Color};
854 ///
855 /// let m = ChessMove::new(Square::D2,
856 /// Square::D4,
857 /// None);
858 ///
859 /// let board = Board::default();
860 /// assert_eq!(board.make_move_new(m).side_to_move(), Color::Black);
861 /// ```
862 #[inline]
863 pub fn make_move_new(&self, m: ChessMove) -> Board {
864 let mut result = mem::MaybeUninit::<Board>::uninit();
865 unsafe {
866 self.make_move(m, &mut *result.as_mut_ptr());
867 result.assume_init()
868 }
869 }
870
871 /// Make a chess move onto an already allocated `Board`.
872 ///
873 /// panic!() if king is captured.
874 ///
875 /// ```
876 /// use chess::{Board, ChessMove, Square, Color};
877 ///
878 /// let m = ChessMove::new(Square::D2,
879 /// Square::D4,
880 /// None);
881 ///
882 /// let board = Board::default();
883 /// let mut result = Board::default();
884 /// board.make_move(m, &mut result);
885 /// assert_eq!(result.side_to_move(), Color::Black);
886 /// ```
887 #[inline]
888 pub fn make_move(&self, m: ChessMove, result: &mut Board) {
889 *result = *self;
890 result.remove_ep();
891 result.checkers = EMPTY;
892 result.pinned = EMPTY;
893 let source = m.get_source();
894 let dest = m.get_dest();
895
896 let source_bb = BitBoard::from_square(source);
897 let dest_bb = BitBoard::from_square(dest);
898 let move_bb = source_bb ^ dest_bb;
899 let moved = self.piece_on(source).unwrap();
900
901 result.xor(moved, source_bb, self.side_to_move);
902 result.xor(moved, dest_bb, self.side_to_move);
903 if let Some(captured) = self.piece_on(dest) {
904 result.xor(captured, dest_bb, !self.side_to_move);
905 }
906
907 #[allow(deprecated)]
908 result.remove_their_castle_rights(CastleRights::square_to_castle_rights(
909 !self.side_to_move,
910 dest,
911 ));
912
913 #[allow(deprecated)]
914 result.remove_my_castle_rights(CastleRights::square_to_castle_rights(
915 self.side_to_move,
916 source,
917 ));
918
919 let opp_king = result.pieces(Piece::King) & result.color_combined(!result.side_to_move);
920
921 let castles = moved == Piece::King && (move_bb & get_castle_moves()) == move_bb;
922
923 let ksq = opp_king.to_square();
924
925 const CASTLE_ROOK_START: [File; 8] = [
926 File::A,
927 File::A,
928 File::A,
929 File::A,
930 File::H,
931 File::H,
932 File::H,
933 File::H,
934 ];
935 const CASTLE_ROOK_END: [File; 8] = [
936 File::D,
937 File::D,
938 File::D,
939 File::D,
940 File::F,
941 File::F,
942 File::F,
943 File::F,
944 ];
945
946 if moved == Piece::Knight {
947 result.checkers ^= get_knight_moves(ksq) & dest_bb;
948 } else if moved == Piece::Pawn {
949 if let Some(Piece::Knight) = m.get_promotion() {
950 result.xor(Piece::Pawn, dest_bb, self.side_to_move);
951 result.xor(Piece::Knight, dest_bb, self.side_to_move);
952 result.checkers ^= get_knight_moves(ksq) & dest_bb;
953 } else if let Some(promotion) = m.get_promotion() {
954 result.xor(Piece::Pawn, dest_bb, self.side_to_move);
955 result.xor(promotion, dest_bb, self.side_to_move);
956 } else if (source_bb & get_pawn_source_double_moves()) != EMPTY
957 && (dest_bb & get_pawn_dest_double_moves()) != EMPTY
958 {
959 result.set_ep(dest);
960 result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
961 } else if Some(dest.ubackward(self.side_to_move)) == self.en_passant {
962 result.xor(
963 Piece::Pawn,
964 BitBoard::from_square(dest.ubackward(self.side_to_move)),
965 !self.side_to_move,
966 );
967 result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
968 } else {
969 result.checkers ^= get_pawn_attacks(ksq, !result.side_to_move, dest_bb);
970 }
971 } else if castles {
972 let my_backrank = self.side_to_move.to_my_backrank();
973 let index = dest.get_file().to_index();
974 let start = BitBoard::set(my_backrank, unsafe {
975 *CASTLE_ROOK_START.get_unchecked(index)
976 });
977 let end = BitBoard::set(my_backrank, unsafe {
978 *CASTLE_ROOK_END.get_unchecked(index)
979 });
980 result.xor(Piece::Rook, start, self.side_to_move);
981 result.xor(Piece::Rook, end, self.side_to_move);
982 }
983 // now, lets see if we're in check or pinned
984 let attackers = result.color_combined(result.side_to_move)
985 & ((get_bishop_rays(ksq)
986 & (result.pieces(Piece::Bishop) | result.pieces(Piece::Queen)))
987 | (get_rook_rays(ksq)
988 & (result.pieces(Piece::Rook) | result.pieces(Piece::Queen))));
989
990 for sq in attackers {
991 let between = between(sq, ksq) & result.combined();
992 if between == EMPTY {
993 result.checkers ^= BitBoard::from_square(sq);
994 } else if between.popcnt() == 1 {
995 result.pinned ^= between;
996 }
997 }
998
999 result.side_to_move = !result.side_to_move;
1000 }
1001
1002 /// Update the pin information.
1003 fn update_pin_info(&mut self) {
1004 self.pinned = EMPTY;
1005 self.checkers = EMPTY;
1006
1007 let ksq = (self.pieces(Piece::King) & self.color_combined(self.side_to_move)).to_square();
1008
1009 let pinners = self.color_combined(!self.side_to_move)
1010 & ((get_bishop_rays(ksq) & (self.pieces(Piece::Bishop) | self.pieces(Piece::Queen)))
1011 | (get_rook_rays(ksq) & (self.pieces(Piece::Rook) | self.pieces(Piece::Queen))));
1012
1013 for sq in pinners {
1014 let between = between(sq, ksq) & self.combined();
1015 if between == EMPTY {
1016 self.checkers ^= BitBoard::from_square(sq);
1017 } else if between.popcnt() == 1 {
1018 self.pinned ^= between;
1019 }
1020 }
1021
1022 self.checkers ^= get_knight_moves(ksq)
1023 & self.color_combined(!self.side_to_move)
1024 & self.pieces(Piece::Knight);
1025
1026 self.checkers ^= get_pawn_attacks(
1027 ksq,
1028 self.side_to_move,
1029 self.color_combined(!self.side_to_move) & self.pieces(Piece::Pawn),
1030 );
1031 }
1032
1033 /// Give me the `BitBoard` of my pinned pieces.
1034 #[inline]
1035 pub fn pinned(&self) -> &BitBoard {
1036 &self.pinned
1037 }
1038
1039 /// Give me the `Bitboard` of the pieces putting me in check.
1040 #[inline]
1041 pub fn checkers(&self) -> &BitBoard {
1042 &self.checkers
1043 }
1044}
1045
1046impl fmt::Display for Board {
1047 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1048 let fen: BoardBuilder = self.into();
1049 write!(f, "{}", fen)
1050 }
1051}
1052
1053impl TryFrom<&BoardBuilder> for Board {
1054 type Error = Error;
1055
1056 fn try_from(fen: &BoardBuilder) -> Result<Self, Self::Error> {
1057 let mut board = Board::new();
1058
1059 for sq in ALL_SQUARES.iter() {
1060 if let Some((piece, color)) = fen[*sq] {
1061 board.xor(piece, BitBoard::from_square(*sq), color);
1062 }
1063 }
1064
1065 board.side_to_move = fen.get_side_to_move();
1066
1067 if let Some(ep) = fen.get_en_passant() {
1068 board.side_to_move = !board.side_to_move;
1069 board.set_ep(ep);
1070 board.side_to_move = !board.side_to_move;
1071 }
1072
1073 #[allow(deprecated)]
1074 board.add_castle_rights(Color::White, fen.get_castle_rights(Color::White));
1075 #[allow(deprecated)]
1076 board.add_castle_rights(Color::Black, fen.get_castle_rights(Color::Black));
1077
1078 board.update_pin_info();
1079
1080 if board.is_sane() {
1081 Ok(board)
1082 } else {
1083 Err(Error::InvalidBoard)
1084 }
1085 }
1086}
1087
1088impl TryFrom<&mut BoardBuilder> for Board {
1089 type Error = Error;
1090
1091 fn try_from(fen: &mut BoardBuilder) -> Result<Self, Self::Error> {
1092 (&*fen).try_into()
1093 }
1094}
1095
1096impl TryFrom<BoardBuilder> for Board {
1097 type Error = Error;
1098
1099 fn try_from(fen: BoardBuilder) -> Result<Self, Self::Error> {
1100 (&fen).try_into()
1101 }
1102}
1103
1104impl FromStr for Board {
1105 type Err = Error;
1106
1107 fn from_str(value: &str) -> Result<Self, Self::Err> {
1108 Ok(BoardBuilder::from_str(value)?.try_into()?)
1109 }
1110}
1111
1112#[test]
1113fn test_null_move_en_passant() {
1114 let start =
1115 Board::from_str("rnbqkbnr/pppp2pp/8/4pP2/8/8/PPPP1PPP/RNBQKBNR w KQkq e6 0 0").unwrap();
1116 let expected =
1117 Board::from_str("rnbqkbnr/pppp2pp/8/4pP2/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 0").unwrap();
1118 assert_eq!(start.null_move().unwrap(), expected);
1119}