bit_board/
bitboard.rs

1use bitvec::slice::BitSlice;
2
3use crate::DimensionMismatch;
4
5pub trait BitBoard: Sized {
6    /// Returns the number of rows in the board.
7    fn n_rows(&self) -> usize;
8
9    /// Returns the number of columns in the board.
10    fn n_cols(&self) -> usize;
11
12    /// Returns a mutable reference to the underlying bits.
13    fn board_mut(&mut self) -> &mut BitSlice;
14
15    /// Returns an immutable reference to the underlying bits.
16    fn board(&self) -> &BitSlice;
17
18    /// Get the index that we can use to directly access a certain spot on the board
19    fn index_of(&self, row: usize, col: usize) -> usize {
20        assert!(
21            row <= (self.n_rows() - 1),
22            "row cannot be greater than n_rows"
23        );
24        assert!(
25            col <= (self.n_cols() - 1),
26            "col cannot be greater than n_cols"
27        );
28        (row * self.n_cols()) + col
29    }
30
31    /// Get the row and column of the linear index
32    fn row_col_of(&self, index: usize) -> (usize, usize) {
33        let row = index / self.n_cols();
34        let col = index % self.n_cols();
35        (row, col)
36    }
37
38    /// Set all bits to the desired value.
39    fn fill(&mut self, value: bool) {
40        self.board_mut().fill(value);
41    }
42
43    fn or(&self, other: &impl BitBoard) -> Result<Self, DimensionMismatch>;
44    fn and(&self, other: &impl BitBoard) -> Result<Self, DimensionMismatch>;
45
46    /// Set the value at index [row, col] to be the `new_val`.
47    fn set(&mut self, row: usize, col: usize, value: bool) {
48        let new_ind = self.index_of(row, col);
49        self.board_mut().set(new_ind, value);
50    }
51    /// Set an entire column to a certain value
52    fn set_col(&mut self, col: usize, value: bool) {
53        // For each row
54        for r_idx in 0..self.n_rows() {
55            // Calculate the index
56            let idx = (r_idx * self.n_cols()) + col;
57            self.board_mut().set(idx, value);
58        }
59    }
60
61    /// Set an entire row to a certain value
62    fn set_row(&mut self, row: usize, value: bool) {
63        // For each column in the row
64        for cidx in 0..self.n_cols() {
65            // Calculate the index
66            let idx = (row * self.n_cols()) + cidx;
67            self.board_mut().set(idx, value);
68        }
69    }
70
71    /// Will set the neighbors immediately above, below, left, and right to `value`. If
72    /// the neighbor is out of bounds, nothing will happen
73    fn set_cardinal_neighbors(&mut self, row: usize, col: usize, value: bool) {
74        // Above
75        if row > 0 {
76            self.set(row - 1, col, value);
77        }
78
79        // Below
80        if row < self.n_rows() - 1 {
81            self.set(row + 1, col, value);
82        }
83
84        // Left
85        if col > 0 {
86            self.set(row, col - 1, value);
87        }
88
89        // Right
90        if col < self.n_cols() - 1 {
91            self.set(row, col + 1, value);
92        }
93    }
94
95    /// Set just the spots diagonal from the given position to `value`. If
96    /// the neighbor is out of bounds, nothing will happen
97    fn set_diagonals(&mut self, row: usize, col: usize, value: bool) {
98        // Above left
99        if row > 0 && col > 0 {
100            self.set(row - 1, col - 1, value);
101        }
102
103        // Above right
104        if row > 0 && col < self.n_cols() - 1 {
105            self.set(row - 1, col + 1, value);
106        }
107
108        // Below left
109        if row < self.n_rows() - 1 && col > 0 {
110            self.set(row + 1, col - 1, value);
111        }
112
113        // Below right
114        if row < self.n_rows() - 1 && col < self.n_cols() - 1 {
115            self.set(row + 1, col + 1, value);
116        }
117    }
118
119    /// Set the cardinal neighbors and the diagonal neighbors to `value`. If
120    /// the neighbor is out of bounds, nothing will happen
121    fn set_all_neighbors(&mut self, row: usize, col: usize, value: bool) {
122        self.set_cardinal_neighbors(row, col, value);
123        self.set_diagonals(row, col, value);
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use crate::{bitboard::BitBoard, bitboardstatic::BitBoardStatic};
130    use rstest::rstest;
131
132    #[rstest]
133    #[case(0, 0, 0)]
134    #[case(1, 0, 1)]
135    #[case(2, 1, 0)]
136    #[case(3, 1, 1)]
137    fn index_of_and_row_col_of(#[case] index: usize, #[case] row: usize, #[case] col: usize) {
138        let bb = BitBoardStatic::<1>::new(2, 2);
139        assert_eq!(bb.index_of(row, col), index);
140        assert_eq!(bb.row_col_of(index), (row, col));
141    }
142
143    #[rstest]
144    #[case(0, 0, 0)]
145    #[case(1, 0, 1)]
146    #[case(2, 0, 2)]
147    #[case(3, 1, 0)]
148    #[case(4, 1, 1)]
149    #[case(5, 1, 2)]
150    #[case(6, 2, 0)]
151    #[case(7, 2, 1)]
152    #[case(8, 2, 2)]
153    fn index_of_and_row_col_of_3x3(#[case] index: usize, #[case] row: usize, #[case] col: usize) {
154        let bb = BitBoardStatic::<1>::new(3, 3);
155        assert_eq!(bb.index_of(row, col), index);
156        assert_eq!(bb.row_col_of(index), (row, col));
157    }
158
159    #[test]
160    fn index_of_and_row_col_of_2x10() {
161        let bb = BitBoardStatic::<1>::new(2, 10);
162        for index in 0..20 {
163            let (row, col) = bb.row_col_of(index);
164            assert_eq!(bb.index_of(row, col), index);
165        }
166    }
167}