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}