timecat/utils/
bitboard.rs

1use super::*;
2
3#[repr(transparent)]
4#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
6pub struct BitBoard(u64);
7
8macro_rules! generate_shift_functions {
9    ($func: ident, $operator: tt, $empty_side: ident) => {
10        #[inline]
11        pub fn $func(self, n: u8) -> Self {
12            if n > 7 {
13                BitBoard::EMPTY
14            } else {
15                (self $operator n)
16                    & get_item_unchecked!(
17                        const {
18                            let mut array = [BitBoard::ALL; 8];
19                            let mut i = 1;
20                            while i < 8 {
21                                array[i].0 = array[i - 1].0 & !($empty_side.0 $operator (i - 1));
22                                i += 1;
23                            }
24                            array
25                        },
26                        n as usize,
27                    )
28            }
29        }
30    };
31}
32
33impl BitBoard {
34    pub const EMPTY: Self = Self(0);
35    pub const ALL: Self = Self::new(0xFFFFFFFFFFFFFFFF);
36
37    #[inline]
38    pub const fn new(bb: u64) -> Self {
39        unsafe { std::mem::transmute(bb) }
40    }
41
42    #[inline]
43    pub const fn set_mask(&mut self, mask: u64) {
44        self.0 = mask;
45    }
46
47    /// Generates the bitboard of the square at the intersection of the given rank and file.
48    #[inline]
49    pub const fn from_rank_and_file(rank: Rank, file: File) -> Self {
50        Self::new(1 << ((rank.to_int() << 3) ^ file.to_int()))
51    }
52
53    #[inline]
54    pub const fn popcnt(self) -> u32 {
55        self.0.count_ones()
56    }
57
58    #[inline]
59    pub const fn reverse_colors(self) -> Self {
60        Self::new(self.0.swap_bytes())
61    }
62
63    #[inline]
64    pub const unsafe fn to_square_index_unchecked(self) -> usize {
65        self.0.trailing_zeros() as usize
66    }
67
68    #[inline]
69    pub const unsafe fn to_square_unchecked(self) -> Square {
70        Square::from_index(self.to_square_index_unchecked())
71    }
72
73    #[inline]
74    pub const fn to_square_index(self) -> Option<usize> {
75        if self.is_empty() {
76            None
77        } else {
78            Some(unsafe { self.to_square_index_unchecked() })
79        }
80    }
81
82    #[inline]
83    pub const fn to_square(self) -> Option<Square> {
84        if self.is_empty() {
85            None
86        } else {
87            Some(unsafe { self.to_square_unchecked() })
88        }
89    }
90
91    #[inline]
92    pub const fn xor_square(&mut self, square: Square) {
93        self.0 ^= square.to_bitboard().0;
94    }
95
96    #[inline]
97    pub const fn remove_square(&mut self, square: Square) {
98        self.0 &= !square.to_bitboard().0;
99    }
100
101    pub const unsafe fn pop_square_unchecked(&mut self) -> Square {
102        let square = self.to_square_unchecked();
103        self.xor_square(square);
104        square
105    }
106
107    #[inline]
108    pub const fn pop_square(&mut self) -> Option<Square> {
109        if self.is_empty() {
110            None
111        } else {
112            Some(unsafe { self.pop_square_unchecked() })
113        }
114    }
115
116    #[inline]
117    pub const fn wrapping_mul(self, rhs: Self) -> Self {
118        Self::new(self.0.wrapping_mul(rhs.0))
119    }
120
121    #[inline]
122    pub const fn is_empty(self) -> bool {
123        self.0 == const { Self::EMPTY.0 }
124    }
125
126    /// <https://www.chessprogramming.org/Flipping_Mirroring_and_Rotating#FlipVertically>
127    pub const fn flip_vertical(self) -> Self {
128        let mut bb = self.0;
129        bb = ((bb >> 8) & 0x00FF_00FF_00FF_00FF) | ((bb & 0x00FF_00FF_00FF_00FF) << 8);
130        bb = ((bb >> 16) & 0x0000_FFFF_0000_FFFF) | ((bb & 0x0000_FFFF_0000_FFFF) << 16);
131        bb = (bb >> 32) | ((bb & 0x0000_0000_FFFF_FFFF) << 32);
132        Self::new(bb)
133    }
134
135    /// <https://www.chessprogramming.org/Flipping_Mirroring_and_Rotating#MirrorHorizontally>
136    pub const fn flip_horizontal(self) -> Self {
137        let mut bb = self.0;
138        bb = ((bb >> 1) & 0x5555_5555_5555_5555) | ((bb & 0x5555_5555_5555_5555) << 1);
139        bb = ((bb >> 2) & 0x3333_3333_3333_3333) | ((bb & 0x3333_3333_3333_3333) << 2);
140        bb = ((bb >> 4) & 0x0F0F_0F0F_0F0F_0F0F) | ((bb & 0x0F0F_0F0F_0F0F_0F0F) << 4);
141        Self::new(bb)
142    }
143
144    /// <https://www.chessprogramming.org/Flipping_Mirroring_and_Rotating#FlipabouttheDiagonal>
145    pub const fn flip_diagonal(self) -> Self {
146        let mut bb = self.0;
147        let mut t = (bb ^ (bb << 28)) & 0x0F0F_0F0F_0000_0000;
148        bb = bb ^ t ^ (t >> 28);
149        t = (bb ^ (bb << 14)) & 0x3333_0000_3333_0000;
150        bb = bb ^ t ^ (t >> 14);
151        t = (bb ^ (bb << 7)) & 0x5500_5500_5500_5500;
152        bb = bb ^ t ^ (t >> 7);
153        Self::new(bb)
154    }
155
156    /// <https://www.chessprogramming.org/Flipping_Mirroring_and_Rotating#FlipabouttheAntidiagonal>
157    pub const fn flip_anti_diagonal(self) -> Self {
158        let mut bb = self.0;
159        let mut t = bb ^ (bb << 36);
160        bb = bb ^ ((t ^ (bb >> 36)) & 0xF0F0_F0F0_0F0F_0F0F);
161        t = (bb ^ (bb << 18)) & 0xCCCC_0000_CCCC_0000;
162        bb = bb ^ t ^ (t >> 18);
163        t = (bb ^ (bb << 9)) & 0xAA00_AA00_AA00_AA00;
164        bb = bb ^ t ^ (t >> 9);
165        Self::new(bb)
166    }
167
168    #[inline]
169    pub const fn shift_up_n_times(self, n: u8) -> Self {
170        if n > 7 {
171            Self::EMPTY
172        } else {
173            Self::new(self.0 << (n << 3))
174        }
175    }
176
177    #[inline]
178    pub const fn shift_down_n_times(self, n: u8) -> Self {
179        if n > 7 {
180            Self::EMPTY
181        } else {
182            Self::new(self.0 >> (n << 3))
183        }
184    }
185
186    generate_shift_functions!(shift_left_n_times, >>, BB_FILE_H);
187    generate_shift_functions!(shift_right_n_times, <<, BB_FILE_A);
188
189    #[inline]
190    pub const fn shift_up(self) -> Self {
191        self.shift_up_n_times(1)
192    }
193
194    #[inline]
195    pub const fn shift_down(self) -> Self {
196        self.shift_down_n_times(1)
197    }
198
199    #[inline]
200    pub const fn shift_left(self) -> Self {
201        Self::new((self.0 & const { !BB_FILE_A.0 }) >> 1)
202    }
203
204    #[inline]
205    pub const fn shift_right(self) -> Self {
206        Self::new((self.0 & const { !BB_FILE_H.0 }) << 1)
207    }
208
209    #[inline]
210    pub const fn shift_forward_n_times(self, color: Color, n: u8) -> Self {
211        match color {
212            White => self.shift_up_n_times(n),
213            Black => self.shift_down_n_times(n),
214        }
215    }
216
217    #[inline]
218    pub const fn shift_backward_n_times(self, color: Color, n: u8) -> Self {
219        match color {
220            White => self.shift_down_n_times(n),
221            Black => self.shift_up_n_times(n),
222        }
223    }
224
225    #[inline]
226    pub const fn shift_forward(self, color: Color) -> Self {
227        self.shift_forward_n_times(color, 1)
228    }
229
230    #[inline]
231    pub const fn shift_backward(self, color: Color) -> Self {
232        self.shift_backward_n_times(color, 1)
233    }
234
235    #[inline]
236    pub const fn contains(self, square: Square) -> bool {
237        !Self::new(self.0 & square.to_bitboard().0).is_empty()
238    }
239
240    #[inline]
241    pub const fn into_inner(self) -> u64 {
242        self.0
243    }
244
245    #[inline]
246    pub const fn to_usize(self) -> usize {
247        self.0 as usize
248    }
249}
250
251impl From<&BitBoard> for u64 {
252    #[inline]
253    fn from(value: &BitBoard) -> Self {
254        value.0
255    }
256}
257
258impl From<BitBoard> for u64 {
259    #[inline]
260    fn from(value: BitBoard) -> Self {
261        (&value).into()
262    }
263}
264
265impl From<u64> for BitBoard {
266    #[inline]
267    fn from(value: u64) -> Self {
268        Self::new(value)
269    }
270}
271
272impl From<&u64> for BitBoard {
273    #[inline]
274    fn from(value: &u64) -> Self {
275        (*value).into()
276    }
277}
278
279macro_rules! implement_u64_methods {
280    ($($visibility:vis const fn $function:ident(self $(, $argument:ident: $argument_type:ty)* $(,)?) -> $return_type:ty),* $(,)?) => {
281        impl BitBoard {
282            $(
283                #[inline]
284                $visibility const fn $function(&self, $($argument: $argument_type),*) -> $return_type {
285                    Self::new(self.0.$function($($argument),*))
286                }
287            )*
288        }
289    };
290}
291
292implement_u64_methods!(
293    pub const fn wrapping_shl(self, rhs: u32) -> Self,
294    pub const fn wrapping_shr(self, rhs: u32) -> Self,
295);
296
297macro_rules! implement_bitwise_operations {
298    (@bit_shifting $direct_trait: ident, $assign_trait: ident, $direct_func: ident, $assign_func: ident) => {
299        impl<T> $assign_trait<T> for BitBoard where u64: $assign_trait<T> {
300
301            #[inline]
302            fn $assign_func(&mut self, rhs: T) {
303                self.0.$assign_func(rhs)
304            }
305        }
306
307        impl<T> $direct_trait<T> for BitBoard where Self: $assign_trait<T> {
308            type Output = Self;
309
310            #[inline]
311            fn $direct_func(mut self, rhs: T) -> Self::Output {
312                self.$assign_func(rhs);
313                self
314            }
315        }
316
317        impl<T> $direct_trait<T> for &BitBoard where BitBoard: $direct_trait<T> {
318            type Output = <BitBoard as $direct_trait<T>>::Output;
319
320            #[inline]
321            fn $direct_func(self, rhs: T) -> Self::Output {
322                (*self).$direct_func(rhs)
323            }
324        }
325    };
326
327    ($direct_trait: ident, $assign_trait: ident, $direct_func: ident, $assign_func: ident) => {
328        implement_bitwise_operations!(@bigger_integer_implementation $direct_trait, $assign_trait, $direct_func, $assign_func, u128);
329        implement_bitwise_operations!(@bigger_integer_implementation $direct_trait, $assign_trait, $direct_func, $assign_func, u64);
330        implement_bitwise_operations!(@bigger_integer_implementation $direct_trait, $assign_trait, $direct_func, $assign_func, i128);
331
332        impl<T> $assign_trait<T> for BitBoard where u64: From<T> {
333
334            #[inline]
335            fn $assign_func(&mut self, rhs: T) {
336                self.0.$assign_func(u64::from(rhs))
337            }
338        }
339
340        impl<T> $direct_trait<T> for BitBoard where u64: From<T> {
341            type Output = Self;
342
343            #[inline]
344            fn $direct_func(mut self, rhs: T) -> Self::Output {
345                self.$assign_func(rhs);
346                self
347            }
348        }
349
350        impl<T> $direct_trait<T> for &BitBoard where u64: From<T> {
351            type Output = BitBoard;
352
353            #[inline]
354            fn $direct_func(self, rhs: T) -> Self::Output {
355                (*self).$direct_func(rhs)
356            }
357        }
358    };
359
360    (@bigger_integer_implementation $direct_trait: ident, $assign_trait: ident, $direct_func: ident, $assign_func: ident, $int_type: ident) => {
361        impl $assign_trait<&BitBoard> for $int_type {
362            #[inline]
363            fn $assign_func(&mut self, rhs: &BitBoard) {
364                self.$assign_func(rhs.0 as $int_type)
365            }
366        }
367
368        impl $assign_trait<BitBoard> for $int_type {
369            #[inline]
370            fn $assign_func(&mut self, rhs: BitBoard) {
371                self.$assign_func(&rhs)
372            }
373        }
374
375        impl $direct_trait<&BitBoard> for $int_type {
376            type Output = $int_type;
377
378            #[inline]
379            fn $direct_func(mut self, rhs: &BitBoard) -> Self::Output {
380                self.$assign_func(rhs);
381                self
382            }
383        }
384
385        impl $direct_trait<BitBoard> for $int_type {
386            type Output = $int_type;
387
388            #[inline]
389            fn $direct_func(self, rhs: BitBoard) -> Self::Output {
390                self.$direct_func(&rhs)
391            }
392        }
393
394        impl $direct_trait<&BitBoard> for &$int_type {
395            type Output = $int_type;
396
397            #[inline]
398            fn $direct_func(self, rhs: &BitBoard) -> Self::Output {
399                (*self).$direct_func(rhs)
400            }
401        }
402
403        impl $direct_trait<BitBoard> for &$int_type {
404            type Output = $int_type;
405
406            #[inline]
407            fn $direct_func(self, rhs: BitBoard) -> Self::Output {
408                self.$direct_func(&rhs)
409            }
410        }
411    };
412}
413
414implement_bitwise_operations!(BitAnd, BitAndAssign, bitand, bitand_assign);
415implement_bitwise_operations!(BitOr, BitOrAssign, bitor, bitor_assign);
416implement_bitwise_operations!(BitXor, BitXorAssign, bitxor, bitxor_assign);
417implement_bitwise_operations!(Mul, MulAssign, mul, mul_assign);
418implement_bitwise_operations!(@bit_shifting Shl, ShlAssign, shl, shl_assign);
419implement_bitwise_operations!(@bit_shifting Shr, ShrAssign, shr, shr_assign);
420
421impl Not for &BitBoard {
422    type Output = BitBoard;
423
424    #[inline]
425    fn not(self) -> BitBoard {
426        BitBoard::new(!self.0)
427    }
428}
429
430impl Not for BitBoard {
431    type Output = Self;
432
433    #[inline]
434    fn not(self) -> Self {
435        !&self
436    }
437}
438
439impl Iterator for BitBoard {
440    type Item = Square;
441
442    #[inline]
443    fn next(&mut self) -> Option<Square> {
444        self.pop_square()
445    }
446}
447
448impl fmt::Display for BitBoard {
449    #[inline]
450    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
451        let occupied_symbol = "X".colorize(BITBOARD_OCCUPIED_SQUARE_STYLE);
452        write!(
453            f,
454            "{}",
455            get_board_string(true, |square| if self.contains(square) {
456                occupied_symbol.as_str().into()
457            } else {
458                Cow::Borrowed(" ")
459            })
460        )
461    }
462}
463
464#[cfg(feature = "pyo3")]
465impl<'source> FromPyObject<'source> for BitBoard {
466    fn extract_bound(ob: &Bound<'source, PyAny>) -> PyResult<Self> {
467        if let Ok(int) = ob.extract::<u64>() {
468            return Ok(Self::new(int));
469        }
470        Err(Pyo3Error::Pyo3TypeConversionError {
471            from: ob.to_string().into(),
472            to: std::any::type_name::<Self>().into(),
473        }
474        .into())
475    }
476}