hexe_core/board/multi_board/
mod.rs

1//! A bitboard-segmented chess board representation.
2
3use core::{hash, ops, mem};
4
5#[cfg(feature = "simd")]
6use core::simd::u8x64;
7
8use board::{Bitboard, PieceMap};
9use castle::Right;
10use color::Color;
11use piece::{Piece, Role};
12use square::Square;
13use uncon::*;
14
15#[cfg(all(test, nightly))]
16mod benches;
17
18#[cfg(test)]
19mod tests;
20
21mod values {
22    use super::*;
23
24    const PAWN:   u64 = 0x00FF00000000FF00;
25    const KNIGHT: u64 = squares!(B1, B8, G1, G8);
26    const BISHOP: u64 = squares!(C1, C8, F1, F8);
27    const ROOK:   u64 = squares!(A1, A8, H1, H8);
28    const QUEEN:  u64 = squares!(D1, D8);
29    const KING:   u64 = squares!(E1, E8);
30    const WHITE:  u64 = 0x000000000000FFFF;
31    const BLACK:  u64 = 0xFFFF000000000000;
32
33    pub const STANDARD: MultiBoard = MultiBoard {
34        pieces: [PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING],
35        colors: [WHITE, BLACK],
36    };
37}
38
39const NUM_PIECES: usize = 6;
40const NUM_COLORS: usize = 2;
41const NUM_BOARDS: usize = NUM_PIECES + NUM_COLORS;
42const NUM_BYTES:  usize = NUM_BOARDS * 8;
43
44/// A full chess board, represented as multiple bitboard segments.
45#[repr(C)]
46#[derive(Clone, Eq)]
47pub struct MultiBoard {
48    pieces: [u64; NUM_PIECES],
49    colors: [u64; NUM_COLORS],
50}
51
52impl PartialEq for MultiBoard {
53    #[inline]
54    fn eq(&self, other: &Self) -> bool {
55        #[cfg(feature = "simd")]
56        {
57            self as *const _ == other as *const _ || self.simd() == other.simd()
58        }
59        #[cfg(not(feature = "simd"))]
60        {
61            self.bytes()[..] == other.bytes()[..]
62        }
63    }
64}
65
66impl Default for MultiBoard {
67    #[inline]
68    fn default() -> MultiBoard {
69        unsafe { mem::zeroed() }
70    }
71}
72
73impl AsRef<[u64]> for MultiBoard {
74    #[inline]
75    fn as_ref(&self) -> &[u64] {
76        let array = self as *const _ as *const [_; NUM_BOARDS];
77        unsafe { &*array }
78    }
79}
80
81impl AsMut<[u64]> for MultiBoard {
82    #[inline]
83    fn as_mut(&mut self) -> &mut [u64] {
84        let array = self as *mut _ as *mut [_; NUM_BOARDS];
85        unsafe { &mut *array }
86    }
87}
88
89impl AsRef<[Bitboard]> for MultiBoard {
90    #[inline]
91    fn as_ref(&self) -> &[Bitboard] {
92        let array = self as *const _ as *const [_; NUM_BOARDS];
93        unsafe { &*array }
94    }
95}
96
97impl AsMut<[Bitboard]> for MultiBoard {
98    #[inline]
99    fn as_mut(&mut self) -> &mut [Bitboard] {
100        let array = self as *mut _ as *mut [_; NUM_BOARDS];
101        unsafe { &mut *array }
102    }
103}
104
105impl<'a> From<&'a PieceMap> for MultiBoard {
106    fn from(map: &PieceMap) -> MultiBoard {
107        let mut board = MultiBoard::default();
108        for (square, &piece) in map {
109            board.insert_unchecked(square, piece);
110        }
111        board
112    }
113}
114
115impl hash::Hash for MultiBoard {
116    #[inline]
117    fn hash<H: hash::Hasher>(&self, state: &mut H) {
118        state.write(self.bytes());
119    }
120}
121
122impl ops::Index<Role> for MultiBoard {
123    type Output = Bitboard;
124
125    #[inline]
126    fn index(&self, role: Role) -> &Bitboard {
127        Bitboard::convert_ref(&self.pieces[role as usize])
128    }
129}
130
131impl ops::IndexMut<Role> for MultiBoard {
132    #[inline]
133    fn index_mut(&mut self, role: Role) -> &mut Bitboard {
134        Bitboard::convert_mut(&mut self.pieces[role as usize])
135    }
136}
137
138impl ops::Index<Color> for MultiBoard {
139    type Output = Bitboard;
140
141    #[inline]
142    fn index(&self, color: Color) -> &Bitboard {
143        Bitboard::convert_ref(&self.colors[color as usize])
144    }
145}
146
147impl ops::IndexMut<Color> for MultiBoard {
148    #[inline]
149    fn index_mut(&mut self, color: Color) -> &mut Bitboard {
150        Bitboard::convert_mut(&mut self.colors[color as usize])
151    }
152}
153
154impl MultiBoard {
155    /// The board for standard chess.
156    pub const STANDARD: MultiBoard = values::STANDARD;
157
158    #[cfg(feature = "simd")]
159    #[inline]
160    fn simd(&self) -> u8x64 {
161        u8x64::load_unaligned(self.bytes())
162    }
163
164    #[inline]
165    fn bytes(&self) -> &[u8; NUM_BYTES] {
166        unsafe { self.into_unchecked() }
167    }
168
169    /// Clears the board of all pieces.
170    #[inline]
171    pub fn clear(&mut self) {
172        unsafe { ::util::zero(self) }
173    }
174
175    /// Returns whether `self` is empty.
176    ///
177    /// For much better performance and readability, is recommended to use this
178    /// method over checking whether `board.len() == 0`.
179    ///
180    /// # Examples
181    ///
182    /// Basic usage:
183    ///
184    /// ```
185    /// use hexe_core::board::MultiBoard;
186    ///
187    /// assert!(!MultiBoard::STANDARD.is_empty());
188    /// assert!(MultiBoard::default().is_empty());
189    /// ```
190    #[inline]
191    pub fn is_empty(&self) -> bool {
192        self.all_bits().is_empty()
193    }
194
195    /// Returns the total number of pieces in `self`.
196    ///
197    /// # Examples
198    ///
199    /// Basic usage:
200    ///
201    /// ```
202    /// use hexe_core::board::MultiBoard;
203    ///
204    /// let board = MultiBoard::STANDARD;
205    /// assert_eq!(board.len(), 32);
206    /// ```
207    #[inline]
208    pub fn len(&self) -> usize {
209        self.all_bits().len()
210    }
211
212    /// Returns all bits of the pieces contained in `self`.
213    ///
214    /// # Examples
215    ///
216    /// Basic usage:
217    ///
218    /// ```
219    /// use hexe_core::board::MultiBoard;
220    ///
221    /// let board = MultiBoard::STANDARD;
222    /// let value = 0xFFFF00000000FFFFu64;
223    ///
224    /// assert_eq!(board.all_bits(), value.into());
225    /// ```
226    #[inline]
227    pub fn all_bits(&self) -> Bitboard {
228        Bitboard(self.colors[0] | self.colors[1])
229    }
230
231    /// Returns the `Bitboard` for `value` in `self`.
232    ///
233    /// # Examples
234    ///
235    /// Basic usage:
236    ///
237    /// ```
238    /// use hexe_core::board::MultiBoard;
239    /// use hexe_core::prelude::*;
240    ///
241    /// let board   = MultiBoard::STANDARD;
242    /// let king_sq = board.bitboard(Piece::WhiteKing).lsb();
243    ///
244    /// assert_eq!(king_sq, Some(Square::E1));
245    /// ```
246    #[inline]
247    pub fn bitboard<T: Index>(&self, value: T) -> Bitboard {
248        value.bitboard(self)
249    }
250
251    /// Returns the bits of the royal pieces, King and Queen.
252    #[inline]
253    pub fn royals(&self) -> Bitboard {
254        self.bitboard(Role::Queen) | self.bitboard(Role::King)
255    }
256
257    /// Returns the first square that `value` appears at, if any.
258    #[inline]
259    pub fn first<T: Index>(&self, value: T) -> Option<Square> {
260        self.bitboard(value).lsb()
261    }
262
263    /// Returns the first square that `value` may appear at, without checking
264    /// whether it exists in `self`.
265    #[inline]
266    pub unsafe fn first_unchecked<T: Index>(&self, value: T) -> Square {
267        self.bitboard(value).lsb_unchecked()
268    }
269
270    /// Returns the last square that `value` appears at, if any.
271    #[inline]
272    pub fn last<T: Index>(&self, value: T) -> Option<Square> {
273        self.bitboard(value).msb()
274    }
275
276    /// Returns the last square that `value` may appear at, without checking
277    /// whether it exists in `self`.
278    #[inline]
279    pub unsafe fn last_unchecked<T: Index>(&self, value: T) -> Square {
280        self.bitboard(value).msb_unchecked()
281    }
282
283    /// Returns the total number of `value` in `self`.
284    ///
285    /// # Examples
286    ///
287    /// Basic usage:
288    ///
289    /// ```
290    /// use hexe_core::board::MultiBoard;
291    /// use hexe_core::prelude::*;
292    ///
293    /// let board = MultiBoard::STANDARD;
294    ///
295    /// assert_eq!(board.count(Color::Black), 16);
296    /// assert_eq!(board.count(Piece::WhiteRook), 2);
297    /// assert_eq!(board.count(Role::Queen), 2);
298    /// ```
299    #[inline]
300    pub fn count<T: Index>(&self, value: T) -> usize {
301        self.bitboard(value).len()
302    }
303
304    /// Returns whether `value` is contained at all squares in `bits`.
305    ///
306    /// # Examples
307    ///
308    /// Basic usage:
309    ///
310    /// ```
311    /// use hexe_core::board::MultiBoard;
312    /// use hexe_core::prelude::*;
313    ///
314    /// let board = MultiBoard::STANDARD;
315    ///
316    /// assert!(board.contains(Square::A2, Color::White));
317    /// assert!(board.contains(Square::C8, Role::Bishop));
318    /// assert!(board.contains(Rank::Seven, Piece::BlackPawn));
319    /// ```
320    #[inline]
321    pub fn contains<T, U>(&self, bits: T, value: U) -> bool
322        where T: Into<Bitboard>, U: Index
323    {
324        self.bitboard(value).contains(bits)
325    }
326
327    /// Returns whether `value` is contained at any square in `bits`.
328    ///
329    /// # Examples
330    ///
331    /// Basic usage:
332    ///
333    /// ```
334    /// use hexe_core::board::MultiBoard;
335    /// use hexe_core::prelude::*;
336    ///
337    /// let board = MultiBoard::STANDARD;
338    ///
339    /// assert!(board.contains_any(File::B, Role::Knight));
340    /// assert!(board.contains_any(Rank::One, Role::King));
341    /// ```
342    #[inline]
343    pub fn contains_any<T, U>(&self, bits: T, value: U) -> bool
344        where T: Into<Bitboard>, U: Index
345    {
346        !(self.bitboard(value) & bits).is_empty()
347    }
348
349    /// Inserts `piece` at each square in `bits`, removing any other pieces
350    /// that may be at `bits`.
351    #[inline]
352    pub fn insert<T: Into<Bitboard>>(&mut self, bits: T, piece: Piece) {
353        let value = bits.into();
354        self.remove_all(value);
355        self.insert_unchecked(value, piece);
356    }
357
358    /// Performs a **blind** insertion of `piece` at a each square in `bits`.
359    ///
360    /// It _does not_ check whether other pieces are located at `bits`. If the
361    /// board may contain pieces at `bits`, then [`insert`](#method.insert)
362    /// should be called instead.
363    #[inline]
364    pub fn insert_unchecked<T: Into<Bitboard>>(&mut self, bits: T, piece: Piece) {
365        let value = bits.into();
366        self[piece.color()] |= value;
367        self[piece.role() ] |= value;
368    }
369
370    /// Removes each piece at `bits` for `value`.
371    #[inline]
372    pub fn remove<T, U>(&mut self, bits: T, value: U)
373        where T: Into<Bitboard>, U: Index
374    {
375        value.remove(bits, self);
376    }
377
378    /// Performs a **blind** removal of `value` at `bits`.
379    ///
380    /// It _does not_ check whether other pieces that `value` does not represent
381    /// are located at `bits`.
382    #[inline]
383    pub fn remove_unchecked<T, U>(&mut self, bits: T, value: U)
384        where T: Into<Bitboard>, U: Index
385    {
386        value.remove_unchecked(bits, self);
387    }
388
389    /// Removes all pieces at `bits`.
390    ///
391    /// # Examples
392    ///
393    /// Basic usage:
394    ///
395    /// ```
396    /// use hexe_core::board::MultiBoard;
397    /// use hexe_core::prelude::*;
398    ///
399    /// let mut board = MultiBoard::STANDARD;
400    /// let squares = [
401    ///     Square::A1,
402    ///     Square::C1,
403    ///     Square::F2,
404    /// ];
405    ///
406    /// for &square in squares.iter() {
407    ///     assert!(board[Color::White].contains(square));
408    ///     board.remove_all(square);
409    ///     assert!(!board[Color::White].contains(square));
410    /// }
411    /// ```
412    #[inline]
413    pub fn remove_all<T: Into<Bitboard>>(&mut self, bits: T) {
414        let value = !bits.into().0;
415        for board in AsMut::<[u64]>::as_mut(self) {
416            *board &= value;
417        }
418    }
419
420    /// Returns references to the underlying bitboards for `Color` and
421    /// `Role`, respectively.
422    #[inline]
423    pub fn split(&self) -> (&[Bitboard; NUM_COLORS], &[Bitboard; NUM_PIECES]) {
424        let colors = &self.colors as *const _ as *const _;
425        let pieces = &self.pieces as *const _ as *const _;
426        unsafe { (&*colors, &*pieces) }
427    }
428
429    /// Returns mutable references to the underlying bitboards for `Color` and
430    /// `Role`, respectively.
431    #[inline]
432    pub fn split_mut(&mut self) -> (&mut [Bitboard; NUM_COLORS], &mut [Bitboard; NUM_PIECES]) {
433        let colors = &mut self.colors as *mut _ as *mut _;
434        let pieces = &mut self.pieces as *mut _ as *mut _;
435        unsafe { (&mut *colors, &mut *pieces) }
436    }
437
438    /// Returns whether the square for `player` is being attacked.
439    ///
440    /// This method _does not_ check whether a piece for `player` actually
441    /// exists at `sq`. To check for that, call `board.contains(sq, player)`.
442    pub fn is_attacked(&self, sq: Square, player: Color) -> bool {
443        macro_rules! check {
444            ($e:expr) => { if $e { return true } };
445        }
446
447        let opp = self.bitboard(!player);
448        let all = opp | self.bitboard(player);
449
450        let pawns = opp & self.bitboard(Role::Pawn);
451        check!(pawns.intersects(sq.pawn_attacks(player)));
452
453        let knights = opp & self.bitboard(Role::Knight);
454        check!(knights.intersects(sq.knight_attacks()));
455
456        let kings = opp & (self.bitboard(Role::King));
457        check!(kings.intersects(sq.king_attacks()));
458
459        let queens = self.bitboard(Role::Queen);
460
461        let bishops = opp & (self.bitboard(Role::Bishop) | queens);
462        check!(bishops.intersects(sq.bishop_attacks(all)));
463
464        let rooks = opp & (self.bitboard(Role::Rook) | queens);
465        rooks.intersects(sq.rook_attacks(all))
466    }
467
468    /// Performs a **blind** castle of the pieces for the castling right.
469    ///
470    /// # Invariants
471    ///
472    /// Under legal castling circumstances, this method makes it so that squares
473    /// involved with castling using `right` are in a correct state post-castle.
474    ///
475    /// There are some cases where the board state may be invalidated if the
476    /// above invariant isn't correctly met:
477    ///
478    /// - If the king is not in its initial position, then a king will spawn
479    ///   both where it was expected to be, as well as where it would move to.
480    ///   The same will happen when the rook is not at its corner of the board.
481    ///
482    /// - If another rook is located where the castling rook is being moved to
483    ///   then both rooks will be removed.
484    ///
485    /// - If any other pieces are located at the involved squares, then other
486    ///   strange things will happen.
487    ///
488    /// The above are all the result of properly defined behavior. They are just
489    /// side effects of how the board is represented and this use of [XOR].
490    ///
491    ///
492    /// # Examples
493    ///
494    /// Basic usage:
495    ///
496    /// ```
497    /// use hexe_core::board::MultiBoard;
498    /// use hexe_core::prelude::*;
499    ///
500    /// let mut board: MultiBoard = {
501    ///     /* create board */
502    ///     # let mut board = MultiBoard::STANDARD;
503    ///     # board.remove_all(Square::B1 | Square::C1 | Square::D1);
504    ///     # board
505    /// };
506    ///
507    /// board.castle(Right::WhiteQueen);
508    /// assert!(board.contains(Square::C1, Piece::WhiteKing));
509    /// assert!(board.contains(Square::D1, Piece::WhiteRook));
510    /// ```
511    ///
512    /// ## Undo-Redo
513    ///
514    /// Because this method internally uses [XOR], it is its own inverse. If the
515    /// involved king and rook sit at their destination squares, they will be
516    /// moved back to their initial squares.
517    ///
518    /// ```
519    /// use hexe_core::board::MultiBoard;
520    /// use hexe_core::castle::Right;
521    ///
522    /// let mut board: MultiBoard = {
523    ///     /* create board */
524    ///     # MultiBoard::STANDARD
525    /// };
526    ///
527    /// let right = Right::WhiteQueen;
528    /// let clone = board.clone();
529    ///
530    /// board.castle(right);
531    /// board.castle(right);
532    ///
533    /// assert!(board == clone);
534    /// ```
535    ///
536    /// [XOR]: https://en.wikipedia.org/wiki/Exclusive_or
537    #[inline]
538    pub fn castle(&mut self, right: Right) {
539        // (King, Rook)
540        static MASKS: [(u64, u64); 4] = [
541            (squares!(E1, G1), squares!(H1, F1)),
542            (squares!(E1, C1), squares!(A1, D1)),
543            (squares!(E8, G8), squares!(H8, F8)),
544            (squares!(E8, C8), squares!(A8, D8)),
545        ];
546
547        let (king, rook) = MASKS[right as usize];
548        self[right.color()]   ^= king | rook;
549        self[Role::King] ^= king;
550        self[Role::Rook] ^= rook;
551    }
552}
553
554/// A type that can be used for [`MultiBoard`](struct.MultiBoard.html) indexing
555/// operations.
556pub trait Index {
557    /// Returns the bitboard for `self` in `board`.
558    fn bitboard(self, board: &MultiBoard) -> Bitboard;
559
560    /// Removes the `bits` of `self` from `board`.
561    fn remove<T: Into<Bitboard>>(self, bits: T, board: &mut MultiBoard);
562
563    /// Performs a **blind** removal of `self` at `bits` in `board`.
564    fn remove_unchecked<T: Into<Bitboard>>(self, bits: T, board: &mut MultiBoard);
565}
566
567impl Index for Color {
568    #[inline]
569    fn bitboard(self, board: &MultiBoard) -> Bitboard {
570        board[self]
571    }
572
573    #[inline]
574    fn remove<T: Into<Bitboard>>(self, bits: T, board: &mut MultiBoard) {
575        self.remove_unchecked(board[self] & bits.into(), board);
576    }
577
578    #[inline]
579    fn remove_unchecked<T: Into<Bitboard>>(self, bits: T, board: &mut MultiBoard) {
580        let value = !bits.into().0;
581        board[self] &= value;
582        for piece in &mut board.pieces {
583            *piece &= value;
584        }
585    }
586}
587
588impl Index for Piece {
589    #[inline]
590    fn bitboard(self, board: &MultiBoard) -> Bitboard {
591        self.color().bitboard(board) & self.role().bitboard(board)
592    }
593
594    #[inline]
595    fn remove<T: Into<Bitboard>>(self, bits: T, board: &mut MultiBoard) {
596        let value = board[self.color()] | board[self.role()];
597        self.remove_unchecked(value & bits.into(), board);
598    }
599
600    #[inline]
601    fn remove_unchecked<T: Into<Bitboard>>(self, bits: T, board: &mut MultiBoard) {
602        let value = !bits.into().0;
603        board[self.color()] &= value;
604        board[self.role() ] &= value;
605    }
606}
607
608impl Index for Role {
609    #[inline]
610    fn bitboard(self, board: &MultiBoard) -> Bitboard {
611        board[self]
612    }
613
614    #[inline]
615    fn remove<T: Into<Bitboard>>(self, bits: T, board: &mut MultiBoard) {
616        self.remove_unchecked(board[self] & bits.into(), board);
617    }
618
619    #[inline]
620    fn remove_unchecked<T: Into<Bitboard>>(self, bits: T, board: &mut MultiBoard) {
621        let value = !bits.into().0;
622        board[self] &= value;
623        for color in &mut board.colors {
624            *color &= value;
625        }
626    }
627}