monster_chess/bitboard/
bitscan.rs

1use super::BitBoard;
2
3/// A direction of either `LEFT` or `RIGHT`, created primarily for bitscans of an arbitrary direction.
4#[derive(Debug, Copy, Clone, PartialEq, Eq)]
5pub enum Direction {
6    LEFT,
7    RIGHT,
8}
9
10impl Direction {
11    pub fn opposite(&self) -> Direction {
12        match self {
13            Direction::RIGHT => Direction::LEFT,
14            Direction::LEFT => Direction::LEFT
15        }
16    }
17}
18
19impl<const T: usize> BitBoard<T> {
20    /// A forward bitscan, which finds the least significant 1-bit.
21    pub fn bitscan_forward(&self) -> u16 {
22        assert!(
23            self.is_set(),
24            "Bitscan Forward only works for non-empty BitBoards."
25        );
26
27        if T == 1 {
28            self.bits[0].trailing_zeros() as u16
29        } else {
30            let mut zeros: u16 = 0;
31            for i in (0..T).rev() {
32                let data = self.bits[i];
33                if data != 0 {
34                    zeros += data.trailing_zeros() as u16;
35                    break;
36                }
37
38                zeros += 128;
39            }
40            zeros
41        }
42    }
43
44    /// A reverse bitscan, which finds the most significant 1-bit.
45    pub fn bitscan_reverse(&self) -> u16 {
46        assert!(
47            self.is_set(),
48            "Bitscan Reverse only works for non-empty BitBoards."
49        );
50
51        if T == 1 {
52            127 - self.bits[0].leading_zeros() as u16
53        } else {
54            let mut zeros: u16 = 0;
55            for i in 0..T {
56                let data = self.bits[i];
57                if data != 0 {
58                    zeros += data.leading_zeros() as u16;
59                    break;
60                }
61
62                zeros += 128;
63            }
64            (((T as u16) * 128) - 1) - zeros
65        }
66    }
67
68    /// A bitscan from either given direction, left or right.
69    pub fn bitscan(&self, direction: Direction) -> u16 {
70        match direction {
71            Direction::LEFT => self.bitscan_forward(),
72            Direction::RIGHT => self.bitscan_reverse(),
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::BitBoard;
80
81    #[test]
82    fn bitscan_forward() {
83        assert_eq!(BitBoard::from_data([0, 1]).bitscan_forward(), 0);
84        assert_eq!(BitBoard::from_data([1, 1]).bitscan_forward(), 0);
85        assert_eq!(BitBoard::from_data([3, 3]).bitscan_forward(), 0);
86        assert_eq!(BitBoard::from_data([1, 0]).bitscan_forward(), 128);
87    }
88
89    #[test]
90    fn bitscan_reverse() {
91        assert_eq!(BitBoard::from_data([0, 1]).bitscan_reverse(), 0);
92        assert_eq!(BitBoard::from_data([1, 1]).bitscan_reverse(), 128);
93        assert_eq!(BitBoard::from_data([3, 3]).bitscan_reverse(), 129);
94        assert_eq!(BitBoard::from_data([1, 0]).bitscan_reverse(), 128);
95    }
96}