chess_move_gen/position/
mod.rs

1pub mod fen;
2pub mod make;
3
4use self::fen::*;
5use super::util::grid_to_string_with_props;
6use crate::bb::*;
7use crate::castling_rights::*;
8use crate::hash::{DEFAULT_ZOBRISH_HASH, Zobrist};
9use crate::mv_list::PieceSquareTable;
10use crate::piece::*;
11use crate::side::Side;
12use crate::side::*;
13use crate::square::{Square, SquareInternal};
14use std::fmt;
15
16use std;
17
18pub const STARTING_POSITION_FEN: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QqKk - 0 1";
19
20/// State encodes all game state except position
21#[derive(Debug, Clone, Copy)]
22pub struct State {
23    pub castling_rights: CastlingRights,
24    pub ep_square: Option<Square>,
25    pub stm: Side,
26    pub full_move_number: u16,
27    pub half_move_clock: u8,
28}
29
30impl Default for State {
31    fn default() -> Self {
32        State {
33            castling_rights: NO_RIGHTS,
34            ep_square: None,
35            stm: WHITE,
36            full_move_number: 1,
37            half_move_clock: 0,
38        }
39    }
40}
41
42/// Position encodes all positional information and non-positional game state
43pub struct Position {
44    // grid is an array representation of position
45    grid: [Piece; 64],
46    // bb_sides represents a bitboard for each side
47    bb_sides: [BB; 2],
48    // bb_pieces represents a bitboard for each piece
49    bb_pieces: [BB; 12],
50    // state represents non-positional game state (eg side to move)
51    state: State,
52
53    key: u64,
54
55    hash: &'static Zobrist,
56}
57
58impl std::clone::Clone for Position {
59    fn clone(&self) -> Self {
60        Position {
61            grid: self.grid,
62            bb_sides: self.bb_sides,
63            bb_pieces: self.bb_pieces,
64            state: self.state.clone(),
65            key: self.key,
66            hash: self.hash,
67        }
68    }
69}
70
71impl fmt::Debug for Position {
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73        write!(f, "{}", self.to_string())
74    }
75}
76
77impl fmt::Display for Position {
78    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79        let props = vec![
80            ("    side to move", self.state.stm.to_str().to_string()),
81            (" castling rights", self.state.castling_rights.to_string()),
82            (
83                "      en-passant",
84                self.state
85                    .ep_square
86                    .map_or("-".to_string(), |s| s.to_string()),
87            ),
88            (" half-move clock", self.state.half_move_clock.to_string()),
89            ("full-move number", self.state.full_move_number.to_string()),
90            ("             FEN", self.to_fen()),
91            ("             KEY", format!("{:016X}", self.key)),
92        ];
93        let s = grid_to_string_with_props(
94            |sq: Square| -> char {
95                let pc = self.at(sq);
96                if pc.is_none() { '.' } else { pc.to_char() }
97            },
98            props.as_slice(),
99        );
100
101        write!(f, "{}", &s)
102    }
103}
104
105impl Position {
106    pub fn new(grid: [Piece; 64], state: State) -> Position {
107        let mut bb_pieces = [EMPTY; 12];
108        let mut bb_sides = [EMPTY; 2];
109
110        for (idx, pc) in grid.iter().enumerate().filter(|&(_, &pc)| pc.is_some()) {
111            let bb_mask = BB::new(Square::new(idx as SquareInternal));
112            bb_sides[pc.side().raw()] |= bb_mask;
113            bb_pieces[pc.to_usize()] |= bb_mask;
114        }
115
116        let hash = &DEFAULT_ZOBRISH_HASH;
117        let key = hash.position(&grid, &state);
118
119        Position {
120            grid,
121            bb_pieces,
122            bb_sides,
123            state,
124            hash: &DEFAULT_ZOBRISH_HASH,
125            key,
126        }
127    }
128
129    /// Construct a new position from a FEN string
130    pub fn from_fen(fen: &str) -> Result<Position, String> {
131        from_fen(fen).map(|(grid, state)| Position::new(grid, state))
132    }
133
134    // Convert position to FEN representation
135    pub fn to_fen(&self) -> String {
136        to_fen(&self.grid, &self.state)
137    }
138
139    pub fn hash_key(&self) -> u64 {
140        self.key
141    }
142
143    /// Get position non-positional state
144    pub fn state(&self) -> &State {
145        &self.state
146    }
147
148    /// Get position position
149    pub fn grid(&self) -> &[Piece; 64] {
150        &self.grid
151    }
152
153    /// Get piece at square
154    pub fn at(&self, sq: Square) -> Piece {
155        unsafe { return *self.grid.get_unchecked(sq.to_usize()) }
156    }
157
158    pub fn king_square(&self, side: Side) -> Square {
159        let pc = KING.pc(side);
160        self.bb_pc(pc).bitscan()
161    }
162
163    fn put_piece(&mut self, pc: Piece, sq: Square) {
164        debug_assert!(self.at(sq).is_none());
165
166        let bb_mask = BB::new(sq);
167
168        self.update_grid(sq, pc);
169
170        debug_assert_eq!(self.bb_pc(pc) & bb_mask, EMPTY);
171        debug_assert_eq!(self.bb_side(pc.side()) & bb_mask, EMPTY);
172
173        unsafe {
174            *self.bb_pieces.get_unchecked_mut(pc.to_usize()) ^= bb_mask;
175            *self.bb_sides.get_unchecked_mut(pc.side().to_usize()) ^= bb_mask;
176        }
177    }
178
179    fn remove_piece(&mut self, sq: Square) {
180        debug_assert!(self.at(sq).is_some());
181
182        let pc = self.at(sq);
183        let bb_mask = BB::new(sq);
184
185        self.update_grid(sq, NULL_PIECE);
186
187        debug_assert_eq!(self.bb_pc(pc) & bb_mask, bb_mask);
188        debug_assert_eq!(self.bb_side(pc.side()) & bb_mask, bb_mask);
189
190        unsafe {
191            *self.bb_pieces.get_unchecked_mut(pc.to_usize()) ^= bb_mask;
192            *self.bb_sides.get_unchecked_mut(pc.side().to_usize()) ^= bb_mask;
193        }
194    }
195
196    fn move_piece(&mut self, from: Square, to: Square) -> BB {
197        debug_assert!(self.at(from).is_some());
198        debug_assert!(self.at(to).is_none());
199
200        let pc = self.at(from);
201        let bb_mask = BB::new(from) | BB::new(to);
202
203        debug_assert_eq!(self.bb_pc(pc) & BB::new(from), BB::new(from));
204        debug_assert_eq!(self.bb_side(pc.side()) & BB::new(from), BB::new(from));
205        debug_assert_eq!(self.bb_pc(pc) & BB::new(to), EMPTY);
206        debug_assert_eq!(self.bb_side(pc.side()) & BB::new(to), EMPTY);
207
208        self.update_grid(from, NULL_PIECE);
209        self.update_grid(to, pc);
210
211        unsafe {
212            *self.bb_pieces.get_unchecked_mut(pc.to_usize()) ^= bb_mask;
213            *self.bb_sides.get_unchecked_mut(pc.side().to_usize()) ^= bb_mask;
214        }
215
216        bb_mask
217    }
218
219    fn promote_piece(&mut self, sq: Square, new_pc: Piece) {
220        let old_pc = self.at(sq);
221        let bb_mask = BB::new(sq);
222
223        debug_assert!(old_pc.is_some());
224        debug_assert_eq!(old_pc.side(), new_pc.side());
225
226        self.update_grid(sq, new_pc);
227
228        debug_assert_eq!(self.bb_pc(old_pc) & bb_mask, bb_mask);
229        debug_assert_eq!(self.bb_pc(new_pc) & bb_mask, EMPTY);
230        debug_assert_eq!(self.bb_side(old_pc.side()) & bb_mask, bb_mask);
231
232        unsafe {
233            *(self.bb_pieces.get_unchecked_mut(old_pc.to_usize())) ^= bb_mask;
234            *(self.bb_pieces.get_unchecked_mut(new_pc.to_usize())) |= bb_mask;
235        }
236    }
237
238    /// Get bitboard of pieces for a particular side
239    pub fn bb_side(&self, side: Side) -> BB {
240        unsafe { return *self.bb_sides.get_unchecked(side.to_usize() & 1) }
241    }
242
243    /// Get bitboard of pieces for a particular piece
244    pub fn bb_pc(&self, pc: Piece) -> BB {
245        unsafe { return *self.bb_pieces.get_unchecked(pc.to_usize()) }
246    }
247
248    pub fn piece_iter(&self, pc: Piece) -> BBIterator {
249        self.bb_pieces[pc.to_usize()].iter()
250    }
251
252    /// Get bitboard of sliding pieces for a particular side
253    pub fn bb_sliders(&self, side: Side) -> (BB, BB) {
254        let queens = self.bb_pc(QUEEN.pc(side));
255        let rooks = self.bb_pc(ROOK.pc(side));
256        let bishops = self.bb_pc(BISHOP.pc(side));
257        (queens | bishops, queens | rooks)
258    }
259
260    /// Get bitboard of all occupied squares
261    pub fn bb_occupied(&self) -> BB {
262        self.bb_side(WHITE) | self.bb_side(BLACK)
263    }
264
265    /// Get bitboard of all empty squares
266    pub fn bb_empty(&self) -> BB {
267        !self.bb_occupied()
268    }
269
270    fn update_grid(&mut self, sq: Square, pc: Piece) {
271        unsafe {
272            *(self.grid.get_unchecked_mut(sq.to_usize())) = pc;
273        }
274    }
275
276    pub fn piece_square_score(&self, piece_square_table: &PieceSquareTable) -> i16 {
277        let mut score = 0i16;
278
279        for (idx, &pc) in self.grid().iter().enumerate() {
280            if pc.is_some() {
281                let piece_square_value =
282                    piece_square_table.score(pc.kind(), Square(idx).from_side(pc.side()));
283
284                score += if self.state().stm == pc.side() {
285                    piece_square_value
286                } else {
287                    -piece_square_value
288                }
289            }
290        }
291
292        score
293    }
294}
295
296#[cfg(test)]
297mod test {
298    use super::*;
299    use unindent;
300
301    #[test]
302    fn test_is_not_too_big() {
303        assert_eq!(std::mem::size_of::<Position>(), 224);
304    }
305
306    #[test]
307    fn test_to_string() {
308        let position =
309            &Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w").unwrap();
310
311        let expected = unindent::unindent(
312            "
313          ABCDEFGH
314        8|rnbqkbnr|8     side to move: white
315        7|pppppppp|7  castling rights: QqKk
316        6|........|6       en-passant: -
317        5|........|5  half-move clock: 0
318        4|........|4 full-move number: 1
319        3|........|3              FEN: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QqKk - 0 1
320        2|PPPPPPPP|2              KEY: 0674AFC18BB45C18
321        1|RNBQKBNR|1
322          ABCDEFGH
323        ",
324        );
325        assert_eq!(position.to_string(), expected);
326    }
327}