checke_rs/
bitboard.rs

1use std::ops::{BitAnd, BitOr};
2
3use thiserror::Error;
4
5/// Represents all black squares on a checkers board.
6pub const BLACK_SQUARES: BitBoard = BitBoard(0x5555555555555555);
7
8/// Represents all white squares on a checkers board.
9pub const WHITE_SQUARES: BitBoard = BitBoard(0xAAAAAAAAAAAAAAAA);
10
11/// Represents all corner squares on a checkers board.
12pub const CORNER_SQUARES: BitBoard = BitBoard(0x8100000000000081);
13
14/// Represents all left squares on a checkers board.
15pub const LEFT_SQUARES: BitBoard = BitBoard(0x8080808080808080);
16
17/// Represents all right squares on a checkers board.
18pub const RIGHT_SQUARES: BitBoard = BitBoard(0x101010101010101);
19
20/// Represents all left and right squares on a checkers board.
21pub const LEFT_AND_RIGHT_SQUARES: BitBoard = BitBoard(0x8181818181818181);
22
23/// Represents all top squares on a checkers board.
24pub const TOP_SQUARES: BitBoard = BitBoard(0xFF00000000000000);
25
26/// Represents all bottom squares on a checkers board.
27pub const BOTTOM_SQUARES: BitBoard = BitBoard(0x00000000000000FF);
28
29/// Represents all top and bottom squares on a checkers board.
30pub const TOP_AND_BOTTOM_SQUARES: BitBoard = BitBoard(0xFF000000000000FF);
31
32/// Bit representation of a checkers board. Backed by 64 bits and exposes various bit operations
33/// that make board calculations easy and fast.
34#[derive(Copy, Clone, Debug)]
35pub struct BitBoard(u64);
36
37impl BitBoard {
38    /// Constructs a new [BitBoard] instance with the given value.
39    pub const fn new(value: u64) -> Self {
40        Self(value)
41    }
42
43    /// Calculates whether this bitboard is empty. A bitboard is considered empty if no
44    /// bits have the value of 1.
45    ///
46    /// ```rust
47    /// use checke_rs::bitboard::BitBoard;
48    ///
49    /// let bb = BitBoard::new(0b00000);
50    ///
51    /// assert!(bb.empty())
52    /// ```
53    pub fn empty(&self) -> bool {
54        self.0 == 0
55    }
56
57    /// Returns a list of isolated bitboards representing each piece contained on this
58    /// bitboard instance.
59    ///
60    /// ```rust
61    /// use checke_rs::bitboard::BitBoard;
62    ///
63    /// let bb = BitBoard::new(0b001010);
64    /// let pieces = bb.pieces();
65    ///
66    /// assert_eq!(pieces.len(), 2);
67    /// ```
68    pub fn pieces(&self) -> Vec<MonoBitBoard> {
69        let mut result = Vec::new();
70        for index in 0..64 {
71            if (self.0 & (1 << index)) != 0 {
72                result.push(MonoBitBoard(1 << index))
73            }
74        }
75
76        result
77    }
78
79    /// Calculates whether the given [MonoBitBoard] overlaps with this bitboard instance.
80    /// A bitboard overlaps with another when they have at least one bit in common.
81    ///
82    /// ```
83    /// use checke_rs::bitboard::{BitBoard, MonoBitBoard};
84    ///
85    /// let position = MonoBitBoard::new(0b010).unwrap();
86    /// let bitboard = BitBoard::new(0b101010);
87    ///
88    /// assert!(bitboard.contains(position));
89    /// ```
90    pub fn contains(&self, bitboard: MonoBitBoard) -> bool {
91        !(*self & bitboard).empty()
92    }
93}
94
95impl BitAnd for BitBoard {
96    type Output = BitBoard;
97
98    fn bitand(self, rhs: Self) -> Self::Output {
99        BitBoard(self.0 & rhs.0)
100    }
101}
102
103impl BitOr for BitBoard {
104    type Output = BitBoard;
105
106    fn bitor(self, rhs: Self) -> Self::Output {
107        BitBoard(self.0 | rhs.0)
108    }
109}
110
111impl BitAnd<MonoBitBoard> for BitBoard {
112    type Output = BitBoard;
113
114    fn bitand(self, rhs: MonoBitBoard) -> Self::Output {
115        BitBoard(self.0 & rhs.0)
116    }
117}
118
119impl BitOr<MonoBitBoard> for BitBoard {
120    type Output = BitBoard;
121
122    fn bitor(self, rhs: MonoBitBoard) -> Self::Output {
123        BitBoard(self.0 | rhs.0)
124    }
125}
126
127#[derive(Debug, Error)]
128#[error("MonoBitBoard can only be constructed with a value that contains a single bit of 1.")]
129pub struct MonoBitBoardError;
130
131/// Special kind of bitboard that enforces that only a single bit has the value of 1.
132/// This can be useful when representing a piece or single cell using bitboard and type safety.
133#[derive(Copy, Clone, Debug)]
134pub struct MonoBitBoard(u64);
135
136impl MonoBitBoard {
137    /// Attempt to create a [MonoBitBoard] with the given value.
138    pub fn new(value: u64) -> Result<Self, MonoBitBoardError> {
139        let is_single_piece = value != 0 && (value & (value - 1)) == 0;
140        match is_single_piece {
141            true => {
142                let result = MonoBitBoard(value);
143                Ok(result)
144            }
145            false => Err(MonoBitBoardError)
146        }
147    }
148}
149
150impl TryFrom<BitBoard> for MonoBitBoard {
151    type Error = MonoBitBoardError;
152
153    /// Attempts to convert a [BitBoard] into a [MonoBitBoard]
154    fn try_from(value: BitBoard) -> Result<Self, Self::Error> {
155        MonoBitBoard::new(value.0)
156    }
157}
158
159macro_rules! impl_common {
160    ($x:ident, $y:ident) => {
161        impl PartialEq for $x {
162            fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
163        }
164
165        impl PartialEq<u64> for $x {
166            fn eq(&self, other: &u64) -> bool { self.0 == *other }
167        }
168
169        impl PartialEq<$y> for $x {
170            fn eq(&self, other: &$y) -> bool {
171                self.0 == other.0
172            }
173        }
174    }
175}
176
177impl_common!(MonoBitBoard, BitBoard);
178impl_common!(BitBoard, MonoBitBoard);