hexe/position/
mod.rs

1//! A chess game state position.
2
3use core::board::{MultiBoard, PieceMap};
4use core::misc::Contained;
5use core::mv::{self, MoveVec};
6use prelude::*;
7
8mod state;
9pub use self::state::*;
10
11mod mv_gen;
12pub use self::mv_gen::*;
13
14#[cfg(all(test, nightly))]
15mod benches;
16
17/// A representation of the current game state.
18#[derive(Clone)]
19pub struct Position {
20    /// The current state.
21    state: State,
22
23    /// A piece map board representation for fast lookups.
24    pieces: PieceMap,
25
26    /// Bitboards for each color and piece role.
27    board: MultiBoard,
28
29    /// The color for the player whose turn it is.
30    player: Color,
31}
32
33impl PartialEq for Position {
34    fn eq(&self, other: &Position) -> bool {
35        // Skip checking `board`; it represents the same data as `pieces`.
36        self.pieces == other.pieces &&
37        self.player == other.player &&
38        self.state  == other.state
39    }
40}
41
42impl Eq for Position {}
43
44impl Default for Position {
45    #[inline]
46    fn default() -> Position {
47        const STANDARD: Position = Position {
48            state: State::STANDARD,
49            pieces: PieceMap::STANDARD,
50            board: MultiBoard::STANDARD,
51            player: Color::White,
52        };
53        STANDARD
54    }
55}
56
57impl Position {
58    /// Returns the inner piece map.
59    #[inline]
60    pub fn pieces(&self) -> &PieceMap {
61        &self.pieces
62    }
63
64    /// Returns the inner board.
65    #[inline]
66    pub fn board(&self) -> &MultiBoard {
67        &self.board
68    }
69
70    /// Creates a move generator for this position and `moves`.
71    ///
72    /// # Examples
73    ///
74    /// ```
75    /// use hexe::core::mv::MoveVec;
76    /// use hexe::position::Position;
77    ///
78    /// let mut moves = MoveVec::new();
79    /// let pos = Position::default();
80    ///
81    /// pos.gen(&mut moves).legal();
82    /// ```
83    #[inline]
84    pub fn gen<'a, 'b>(&'a self, moves: &'b mut MoveVec) -> MoveGen<'a, 'b> {
85        MoveGen { pos: self, buf: moves }
86    }
87
88    /// Returns whether the move is legal for this position.
89    pub fn is_legal(&self, mv: Move) -> bool {
90        use self::mv::Matches;
91
92        let src = mv.src();
93        let dst = mv.dst();
94
95        let player  = self.player();
96        let king    = self.king_square(player);
97        let board   = self.board();
98        let checked = board.is_attacked(king, player);
99
100        match mv.matches() {
101            Matches::Castle(mv) => {
102                // Cannot castle out of check
103                if checked {
104                    return false;
105                }
106
107                let right = mv.right();
108                if player != right.color() || !self.rights().contains(right) {
109                    return false;
110                }
111
112                // Cannot castle through or into check
113                for sq in right.path_iter() {
114                    if board.is_attacked(sq, player) {
115                        return false;
116                    }
117                }
118
119                true
120            },
121            _ => unimplemented!(),
122        }
123    }
124
125    /// Returns whether `self` contains the value.
126    #[inline]
127    pub fn contains<'a, T: Contained<&'a Self>>(&'a self, value: T) -> bool {
128        value.contained_in(self)
129    }
130
131    /// Returns the current player's color.
132    #[inline]
133    pub fn player(&self) -> Color {
134        self.player
135    }
136
137    /// Returns the bitboard corresponding to the current player.
138    #[inline]
139    pub fn player_bitboard(&self) -> Bitboard {
140        self.board().bitboard(self.player())
141    }
142
143    /// Returns the opponent player's color.
144    #[inline]
145    pub fn opponent(&self) -> Color {
146        !self.player()
147    }
148
149    /// Returns the bitboard corresponding to the opponent player.
150    #[inline]
151    pub fn opponent_bitboard(&self) -> Bitboard {
152        self.board().bitboard(self.opponent())
153    }
154
155    /// Returns the en passant square.
156    #[inline]
157    pub fn en_passant(&self) -> Option<Square> {
158        self.state.en_passant()
159    }
160
161    /// Returns the castle rights for both players.
162    #[inline]
163    pub fn rights(&self) -> Rights {
164        self.state.rights()
165    }
166
167    /// Returns the square where the color's king lies on.
168    #[inline]
169    pub fn king_square(&self, color: Color) -> Square {
170        let piece = Piece::new(Role::King, color);
171        let board = self.board().bitboard(piece);
172
173        // Both colors should *always* have a king
174        debug_assert!(!board.is_empty(), "{:?} not found", piece);
175
176        unsafe { board.lsb_unchecked() }
177    }
178}
179
180impl<'a> Contained<&'a Position> for Square {
181    #[inline]
182    fn contained_in(self, pos: &Position) -> bool {
183        pos.pieces().contains(self)
184    }
185}
186
187macro_rules! impl_contained {
188    ($($t:ty),+) => {
189        $(impl<'a> Contained<&'a Position> for $t {
190            #[inline]
191            fn contained_in(self, pos: &Position) -> bool {
192                !pos.board().bitboard(self).is_empty()
193            }
194        })+
195    }
196}
197
198impl_contained! { Piece, Role, Color }
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn initial_pieces() {
206        let pos = Position::default();
207        let all = pos.board().all_bits();
208
209        for square in Square::ALL {
210            if let Some(&piece) = pos.pieces().get(square) {
211                assert!(all.contains(square));
212
213                let board = pos.board();
214                assert!(board.contains(square, piece));
215                assert!(board.contains(square, piece.role()));
216                assert!(board.contains(square, piece.color()));
217            } else {
218                let (a, b) = pos.board.split();
219                for &slice in &[&a[..], &b[..]] {
220                    for &bitboard in slice {
221                        assert!(!bitboard.contains(square));
222                    }
223                }
224            }
225        }
226    }
227}