alcibiades/
board.rs

1//! Defines how the chess board is represented in memory.
2
3use std::fmt;
4use utils::parse_fen;
5
6
7/// `WHITE` or `BLACK`.
8pub type Color = usize;
9
10pub const WHITE: Color = 0;
11pub const BLACK: Color = 1;
12
13
14/// `KING`, `QUEEN`, `ROOK`, `BISHOP`, `KINGHT`, `PAWN` or `PIECE_NONE`.
15pub type PieceType = usize;
16
17pub const KING: PieceType = 0;
18pub const QUEEN: PieceType = 1;
19pub const ROOK: PieceType = 2;
20pub const BISHOP: PieceType = 3;
21pub const KNIGHT: PieceType = 4;
22pub const PAWN: PieceType = 5;
23pub const PIECE_NONE: PieceType = 6;
24
25
26/// From 0 to 63 (0 is A1, 1 is B1, .. , 62 is G8, 63 is H8).
27pub type Square = usize;
28
29
30/// A set of squares on the chessboard.
31///
32/// `u64` bit-sets called *bitboards* can be used to represent a set
33/// of squares on the chessboard. For example, the set of squares that
34/// are occupied by white rooks in the beginning of the game is: `1 <<
35/// A1 | 1 << H1`. `0` represents the empty set, `0xffffffffffffffff`
36/// represents the set of all 64 squares on the board.
37pub type Bitboard = u64;
38
39
40/// Describes how the pieces are placed on the board.
41#[derive(Clone, Debug)]
42pub struct PiecesPlacement {
43    /// An array of occupation bitboards indexed by piece type.  For
44    /// example, `pieces_placement.piece_type[PAWN]` gives the set of
45    /// all pawns on the board (white and black).
46    pub piece_type: [Bitboard; 6],
47
48    /// An array of occupation bitboards indexed by color.  For
49    /// example, `pieces_placement.color[WHITE]` gives the set of all
50    /// white pieces and pawns on the board.
51    pub color: [Bitboard; 2],
52}
53
54impl fmt::Display for PiecesPlacement {
55    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56        let mut s = String::new();
57        for rank in (0..8).rev() {
58            s.push('\n');
59            for file in 0..8 {
60                let square = Board::square(file, rank);
61                let bb = 1 << square;
62                let piece = match bb {
63                    x if x & self.piece_type[KING] != 0 => 'k',
64                    x if x & self.piece_type[QUEEN] != 0 => 'q',
65                    x if x & self.piece_type[ROOK] != 0 => 'r',
66                    x if x & self.piece_type[BISHOP] != 0 => 'b',
67                    x if x & self.piece_type[KNIGHT] != 0 => 'n',
68                    x if x & self.piece_type[PAWN] != 0 => 'p',
69                    _ => '.',
70                };
71                if bb & self.color[WHITE] != 0 {
72                    s.push(piece.to_uppercase().next().unwrap());
73                } else {
74                    s.push(piece);
75                }
76            }
77        }
78        writeln!(f, "{}", s)
79    }
80}
81
82
83/// `QUEENSIDE` or `KINGSIDE`.
84pub type CastlingSide = usize;
85
86pub const QUEENSIDE: CastlingSide = 0;
87pub const KINGSIDE: CastlingSide = 1;
88
89
90/// Holds information about which player can castle on which side.
91///
92/// The castling rights are held in a `usize` value. The lowest 4 bits
93/// of the value contain the whole needed information. It is laid out
94/// in the following way:
95///
96/// ```text
97///  usize                    3   2   1   0
98///  +----------------------+---+---+---+---+
99///  |                      |   |   |   |   |
100///  |    Unused (zeros)    |Castling flags |
101///  |                      |   |   |   |   |
102///  +----------------------+---+---+---+---+
103///
104///  bit 0 -- if set, white can castle on queen-side;
105///  bit 1 -- if set, white can castle on king-side;
106///  bit 2 -- if set, black can castle on queen-side;
107///  bit 3 -- if set, black can castle on king-side.
108/// ```
109#[derive(Clone, Copy, Debug)]
110pub struct CastlingRights(usize);
111
112impl CastlingRights {
113    /// Creates a new instance.
114    ///
115    /// The least significant 4 bits of `value` are used as a raw
116    /// value for the new instance.
117    #[inline]
118    pub fn new(value: usize) -> CastlingRights {
119        CastlingRights(value & 0b1111)
120    }
121
122    /// Returns the contained raw value (between 0 and 15).
123    #[inline]
124    pub fn value(&self) -> usize {
125        self.0
126    }
127
128    /// Grants a given player the right to castle on a given side.
129    ///
130    /// This method returns `true` if the player did not have the
131    /// right to castle on the given side before this method was
132    /// called, and `false` otherwise.
133    pub fn grant(&mut self, player: Color, side: CastlingSide) -> bool {
134        assert!(player <= 1);
135        assert!(side <= 1);
136        let rights_before = self.0;
137        let granted = 1 << (player << 1) << side;
138        self.0 |= granted;
139
140        granted & !rights_before != 0
141    }
142
143    /// Updates the castling rights after played move.
144    ///
145    /// `orig_square` and `dest_square` describe the played move.
146    #[inline]
147    pub fn update(&mut self, orig_square: Square, dest_square: Square) {
148        debug_assert!(orig_square <= 63);
149        debug_assert!(dest_square <= 63);
150        const WQ: usize = (1 << (WHITE << 1) << QUEENSIDE);
151        const WK: usize = (1 << (WHITE << 1) << KINGSIDE);
152        const W: usize = WQ | WK;
153        const BQ: usize = (1 << (BLACK << 1) << QUEENSIDE);
154        const BK: usize = (1 << (BLACK << 1) << KINGSIDE);
155        const B: usize = BQ | BK;
156
157        // On each move, the value of `CASTLING_RELATION` for the
158        // origin and destination squares should be AND-ed with the
159        // castling rights value, to derive the updated castling
160        // rights.
161        const CASTLING_RELATION: [usize; 64] = [
162            !WQ, !0, !0, !0, !W, !0, !0, !WK,
163            !0,  !0, !0, !0, !0, !0, !0, !0,
164            !0,  !0, !0, !0, !0, !0, !0, !0,
165            !0,  !0, !0, !0, !0, !0, !0, !0,
166            !0,  !0, !0, !0, !0, !0, !0, !0,
167            !0,  !0, !0, !0, !0, !0, !0, !0,
168            !0,  !0, !0, !0, !0, !0, !0, !0,
169            !BQ, !0, !0, !0, !B, !0, !0, !BK
170        ];
171        self.0 &= CASTLING_RELATION[orig_square] & CASTLING_RELATION[dest_square];
172    }
173
174    /// Returns if a given player has the rights to castle on a given
175    /// side.
176    #[inline]
177    pub fn can_castle(&self, player: Color, side: CastlingSide) -> bool {
178        debug_assert!(player <= 1);
179        debug_assert!(side <= 1);
180        (1 << (player << 1) << side) & self.0 != 0
181    }
182}
183
184impl fmt::Display for CastlingRights {
185    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186        let mut value = self.value();
187        for s in ["Q", "K", "q", "k"].iter() {
188            if value & 1 == 1 {
189                try!(f.write_str(s));
190            }
191            value >>= 1;
192        }
193        Ok(())
194    }
195}
196
197
198/// Represents an illegal position error.
199pub struct IllegalBoard;
200
201
202/// Holds a chess position.
203#[derive(Clone, Debug)]
204pub struct Board {
205    /// The placement of the pieces on the board.
206    pub pieces: PiecesPlacement,
207
208    /// The side to move.
209    pub to_move: Color,
210
211    /// The castling rights for both players.
212    pub castling_rights: CastlingRights,
213
214    /// If the previous move was a double pawn push, contains pushed
215    /// pawn's file (a value between 0 and 7). Otherwise contains `8`.
216    pub enpassant_file: usize,
217
218    /// The set of all occupied squares on the board.
219    ///
220    /// Always equals `self.pieces.color[WHITE] |
221    /// self.pieces.color[BLACK]`. Deserves a field on its own because
222    /// it is very frequently needed.
223    pub occupied: Bitboard,
224}
225
226impl Board {
227    /// Creates a new instance from Forsyth–Edwards Notation (FEN).
228    pub fn from_fen(fen: &str) -> Result<Board, IllegalBoard> {
229        parse_fen(fen).map(|x| x.0)
230    }
231
232    /// Returns the square on given file and rank.
233    ///
234    /// * `file` should be a number between 0 and 7 (0 is file A, 7 is file H).
235    /// * `rank` should be a number between 0 and 7 (0 is rank 1, 7 is rank 8).
236    #[inline]
237    pub fn square(file: usize, rank: usize) -> Square {
238        debug_assert!(file < 8);
239        debug_assert!(rank < 8);
240        (rank << 3) + file
241    }
242
243    /// Returns the file of a given square.
244    ///
245    /// The returned number will be between 0 and 7 (0 is file A, 7 is file H).
246    #[inline]
247    pub fn file(square: Square) -> usize {
248        debug_assert!(square <= 63);
249        square % 8
250    }
251
252    /// Returns the rank of a given square.
253    ///
254    /// The returned number will be between 0 and 7 (0 is rank 1, 7 is rank 8).
255    #[inline]
256    pub fn rank(square: Square) -> usize {
257        debug_assert!(square <= 63);
258        square >> 3
259    }
260}
261
262
263#[cfg(test)]
264mod tests {
265    use super::*;
266    use squares::*;
267
268    #[test]
269    fn castling_rights() {
270        let mut c = CastlingRights::new(0b1110);
271        assert_eq!(c.can_castle(WHITE, QUEENSIDE), false);
272        assert_eq!(c.can_castle(WHITE, KINGSIDE), true);
273        assert_eq!(c.can_castle(BLACK, QUEENSIDE), true);
274        assert_eq!(c.can_castle(BLACK, KINGSIDE), true);
275        c.update(H8, H7);
276        assert_eq!(c.can_castle(WHITE, QUEENSIDE), false);
277        assert_eq!(c.can_castle(WHITE, KINGSIDE), true);
278        assert_eq!(c.can_castle(BLACK, QUEENSIDE), true);
279        assert_eq!(c.can_castle(BLACK, KINGSIDE), false);
280        assert_eq!(c.value(), 0b0110);
281        assert_eq!(c.grant(BLACK, KINGSIDE), true);
282        assert_eq!(c.grant(BLACK, KINGSIDE), false);
283        assert_eq!(c.value(), 0b1110);
284    }
285}