1use std::fmt;
2use std::ops::{BitAndAssign, BitOrAssign};
3
4use bitvec::prelude::*;
5
6use crate::{DimensionMismatch, bitboard::BitBoard};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub struct BitBoardStatic<const W: usize> {
10 board: BitArray<[usize; W]>,
12
13 pub n_rows: usize,
15
16 pub n_cols: usize,
18}
19
20impl<const W: usize> fmt::Display for BitBoardStatic<W> {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 write!(f, " ")?; for col in 0..self.n_cols {
25 write!(f, "{}", col % 10)?; }
27 writeln!(f)?;
28
29 for row in 0..self.n_rows {
30 write!(f, "{:>2} ", row)?;
32 for col in 0..self.n_cols {
33 let idx = row * self.n_cols + col;
34 let bit = self.board[idx];
35 let c = if bit { 'X' } else { '.' };
36 write!(f, "{}", c)?;
37 }
38 writeln!(f)?;
39 }
40 Ok(())
41 }
42}
43
44impl<const W: usize> BitBoardStatic<W> {
46 pub fn new(n_rows: usize, n_cols: usize) -> Self {
47 let total_bits = n_rows * n_cols;
49 let available_bits = W * (usize::BITS as usize);
50 assert!(
51 total_bits <= available_bits,
52 "The number of bits required by the board ({} * {}) exceeds the allocated storage ({} bits).",
53 n_rows,
54 n_cols,
55 available_bits
56 );
57
58 Self {
59 board: BitArray::default(),
60 n_rows,
61 n_cols,
62 }
63 }
64}
65
66impl<const W: usize> BitBoard for BitBoardStatic<W> {
67 fn n_rows(&self) -> usize {
68 self.n_rows
69 }
70
71 fn n_cols(&self) -> usize {
72 self.n_cols
73 }
74
75 fn board_mut(&mut self) -> &mut BitSlice {
76 &mut self.board
77 }
78
79 fn board(&self) -> &BitSlice {
80 &self.board
81 }
82
83 fn or(&self, other: &impl BitBoard) -> Result<Self, DimensionMismatch> {
85 if (self.n_rows() != other.n_rows()) || (self.n_cols() != other.n_cols()) {
86 return Err(DimensionMismatch);
87 }
88
89 let mut result = *self;
90 result.board_mut().bitor_assign(other.board());
91 Ok(result)
92 }
93
94 fn and(&self, other: &impl BitBoard) -> Result<Self, DimensionMismatch> {
96 if (self.n_rows() != other.n_rows()) || (self.n_cols() != other.n_cols()) {
97 return Err(DimensionMismatch);
98 }
99
100 let mut result = *self;
101 result.board_mut().bitand_assign(other.board());
102 Ok(result)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108
109 use super::*;
110
111 use rstest::rstest;
112
113 #[test]
114 fn can_construct() {
115 let bb = BitBoardStatic::<1>::new(2, 2);
116 println!("{:?}", bb);
117 }
118
119 #[test]
120 #[should_panic(expected = "row cannot be greater than n_rows")]
121 fn row_too_big() {
122 let bb = BitBoardStatic::<1>::new(2, 2);
123 bb.index_of(10, 0);
124 }
125
126 #[test]
127 #[should_panic(expected = "col cannot be greater than n_col")]
128 fn col_too_big() {
129 let bb = BitBoardStatic::<1>::new(2, 2);
130 bb.index_of(0, 10);
131 }
132
133 #[rstest]
134 #[case(0, 0, 0)]
135 #[case(0, 1, 1)]
136 #[case(1, 0, 2)]
137 #[case(1, 1, 3)]
138 fn index_of(#[case] row: usize, #[case] col: usize, #[case] expected: usize) {
139 let bb = BitBoardStatic::<1>::new(2, 2);
140
141 assert_eq!(expected, bb.index_of(row, col))
142 }
143
144 #[rstest]
145 #[case(0, 0, 0)]
146 #[case(1, 0, 1)]
147 #[case(2, 0, 2)]
148 #[case(3, 0, 3)]
149 #[case(4, 0, 4)]
150 fn col_vec_index_of(#[case] row: usize, #[case] col: usize, #[case] expected: usize) {
151 let bb = BitBoardStatic::<1>::new(5, 1);
152
153 assert_eq!(expected, bb.index_of(row, col))
154 }
155
156 #[rstest]
157 #[case(0, 0, 0)]
158 #[case(0, 1, 1)]
159 #[case(0, 2, 2)]
160 #[case(0, 3, 3)]
161 #[case(0, 4, 4)]
162 fn row_vec_index_of(#[case] row: usize, #[case] col: usize, #[case] expected: usize) {
163 let bb = BitBoardStatic::<1>::new(1, 5);
164
165 assert_eq!(expected, bb.index_of(row, col))
166 }
167
168 #[rstest]
169 #[case(0)]
170 #[case(1)]
171 #[case(2)]
172 #[case(3)]
173 #[case(4)]
174 fn set_col(#[case] col: usize) {
175 let mut bb = BitBoardStatic::<1>::new(5, 5);
176 bb.set_col(col, true);
177
178 for ridx in 0..bb.n_rows {
179 for cidx in 0..bb.n_cols {
180 if cidx == col {
181 assert!(bb.board[bb.index_of(ridx, cidx)])
182 } else {
183 assert!(!bb.board[bb.index_of(ridx, cidx)])
184 }
185 }
186 }
187 }
188
189 #[rstest]
190 #[case(0)]
191 #[case(1)]
192 #[case(2)]
193 #[case(3)]
194 #[case(4)]
195 fn set_row(#[case] row: usize) {
196 let mut bb = BitBoardStatic::<1>::new(5, 5);
197 bb.set_row(row, true);
198
199 for ridx in 0..bb.n_rows {
200 for cidx in 0..bb.n_cols {
201 if ridx == row {
202 assert!(bb.board[bb.index_of(ridx, cidx)])
203 } else {
204 assert!(!bb.board[bb.index_of(ridx, cidx)])
205 }
206 }
207 }
208 }
209
210 #[test]
211 fn can_set_all_bits() {
212 let nr = 3;
214 let nc = 3;
215 let mut bb = BitBoardStatic::<1>::new(nr, nc);
216 bb.set(0, 0, true);
217
218 for ridx in 0..nr {
220 for cidx in 0..nc {
221 bb.set(ridx, cidx, true);
222 }
223 }
224
225 assert!(bb.board[..nr * nc].all());
226
227 for ridx in 0..nr {
229 for cidx in 0..nc {
230 bb.set(ridx, cidx, false);
231 }
232 }
233
234 assert!(bb.board[..nr * nc].not_any());
235 }
236
237 #[rstest]
238 #[case(0, 0, bitvec![0, 1, 1, 0])]
239 #[case(0, 1, bitvec![1, 0, 0, 1])]
240 #[case(1, 0, bitvec![1, 0, 0, 1])]
241 #[case(1, 1, bitvec![0, 1, 1, 0])]
242 fn set_caridnal_neighbors_2x2(
243 #[case] row: usize,
244
245 #[case] col: usize,
246
247 #[case] expect_bv: BitVec,
248 ) {
249 let mut bb = BitBoardStatic::<1>::new(2, 2);
250 bb.set_cardinal_neighbors(row, col, true);
251
252 let mut expect_board = BitArray::<[usize; 1]>::default();
253 expect_board[..expect_bv.len()].copy_from_bitslice(&expect_bv);
254
255 let expect = BitBoardStatic::<1> {
256 board: expect_board,
257 n_rows: 2,
258 n_cols: 2,
259 };
260
261 assert_eq!(expect, bb);
262 }
263
264 #[rstest]
265 #[case(0, 0, bitvec![0, 1, 0, 1, 0, 0, 0, 0, 0])]
266 #[case(0, 1, bitvec![1, 0, 1, 0, 1, 0, 0, 0, 0])]
267 #[case(0, 2, bitvec![0, 1, 0, 0, 0, 1, 0, 0, 0])]
268 #[case(1, 0, bitvec![1, 0, 0, 0, 1, 0, 1, 0, 0])]
269 #[case(1, 1, bitvec![0, 1, 0, 1, 0, 1, 0, 1, 0])]
270 #[case(1, 2, bitvec![0, 0, 1, 0, 1, 0, 0, 0, 1])]
271 #[case(2, 0, bitvec![0, 0, 0, 1, 0, 0, 0, 1, 0])]
272 #[case(2, 1, bitvec![0, 0, 0, 0, 1, 0, 1, 0, 1])]
273 #[case(2, 2, bitvec![0, 0, 0, 0, 0, 1, 0, 1, 0])]
274 fn set_caridnal_neighbors_3x3(
275 #[case] row: usize,
276 #[case] col: usize,
277 #[case] expect_bv: BitVec,
278 ) {
279 let mut bb = BitBoardStatic::<1>::new(3, 3);
280 bb.set_cardinal_neighbors(row, col, true);
281
282 let mut expect_board = BitArray::<[usize; 1]>::default();
283 expect_board[..expect_bv.len()].copy_from_bitslice(&expect_bv);
284
285 let expect = BitBoardStatic::<1> {
286 board: expect_board,
287 n_rows: 3,
288 n_cols: 3,
289 };
290
291 assert_eq!(expect, bb);
292 }
293
294 #[rstest]
295 #[case(0, 0, bitvec![0, 1, 1, 1])]
296 #[case(0, 1, bitvec![1, 0, 1, 1])]
297 #[case(1, 0, bitvec![1, 1, 0, 1])]
298 #[case(1, 1, bitvec![1, 1, 1, 0])]
299 fn set_all_neighbors_2x2(#[case] row: usize, #[case] col: usize, #[case] expect_bv: BitVec) {
300 let mut bb = BitBoardStatic::<1>::new(2, 2);
301 bb.set_all_neighbors(row, col, true);
302
303 let mut expect_board = BitArray::<[usize; 1]>::default();
304 expect_board[..expect_bv.len()].copy_from_bitslice(&expect_bv);
305
306 let expect = BitBoardStatic::<1> {
307 board: expect_board,
308 n_rows: 2,
309 n_cols: 2,
310 };
311
312 assert_eq!(expect, bb);
313 }
314
315 #[rstest]
316 #[case(0, 0, bitvec![0, 1, 0, 1, 1, 0, 0, 0, 0])]
317 #[case(0, 1, bitvec![1, 0, 1, 1, 1, 1, 0, 0, 0])]
318 #[case(0, 2, bitvec![0, 1, 0, 0, 1, 1, 0, 0, 0])]
319 #[case(1, 0, bitvec![1, 1, 0, 0, 1, 0, 1, 1, 0])]
320 #[case(1, 1, bitvec![1, 1, 1, 1, 0, 1, 1, 1, 1])]
321 #[case(1, 2, bitvec![0, 1, 1, 0, 1, 0, 0, 1, 1])]
322 #[case(2, 0, bitvec![0, 0, 0, 1, 1, 0, 0, 1, 0])]
323 #[case(2, 1, bitvec![0, 0, 0, 1, 1, 1, 1, 0, 1])]
324 #[case(2, 2, bitvec![0, 0, 0, 0, 1, 1, 0, 1, 0])]
325 fn set_all_neighbors_3x3(#[case] row: usize, #[case] col: usize, #[case] expect_bv: BitVec) {
326 let mut bb = BitBoardStatic::<1>::new(3, 3);
327 bb.set_all_neighbors(row, col, true);
328
329 let mut expect_board = BitArray::<[usize; 1]>::default();
330 expect_board[..expect_bv.len()].copy_from_bitslice(&expect_bv);
331
332 let expect = BitBoardStatic::<1> {
333 board: expect_board,
334 n_rows: 3,
335 n_cols: 3,
336 };
337
338 assert_eq!(expect, bb);
339 }
340
341 #[rstest]
342 #[case(1, 1, 1, 2)]
343 #[case(2, 1, 1, 2)]
344 #[case(2, 1, 2, 7)]
345 fn and_dimension_mismatch(
346 #[case] b1r: usize,
347 #[case] b1c: usize,
348 #[case] b2r: usize,
349 #[case] b2c: usize,
350 ) {
351 let bb1 = BitBoardStatic::<1>::new(b1r, b1c);
352 let bb8 = BitBoardStatic::<1>::new(b2r, b2c);
353 assert!(bb1.and(&bb8).is_err());
354 }
355
356 #[rstest]
357 #[case(1, 1, 1, 2)]
358 #[case(2, 1, 1, 2)]
359 #[case(2, 1, 2, 7)]
360 fn or_dimension_mismatch(
361 #[case] b1r: usize,
362 #[case] b1c: usize,
363 #[case] b2r: usize,
364 #[case] b2c: usize,
365 ) {
366 let bb1 = BitBoardStatic::<1>::new(b1r, b1c);
367 let bb8 = BitBoardStatic::<1>::new(b2r, b2c);
368 assert!(bb1.or(&bb8).is_err());
369 }
370
371 #[rstest]
372 #[case(bitvec![0, 0, 0, 0], bitvec![0, 0, 0, 0], bitvec![0, 0, 0, 0])] #[case(bitvec![1, 1, 1, 1], bitvec![1, 1, 1, 1], bitvec![1, 1, 1, 1])] #[case(bitvec![0, 0, 0, 0], bitvec![1, 1, 1, 1], bitvec![0, 0, 0, 0])] #[case(bitvec![1, 1, 1, 1], bitvec![1, 0, 0, 1], bitvec![1, 0, 0, 1])] #[case(bitvec![1, 0, 1, 0], bitvec![0, 1, 0, 1], bitvec![0, 0, 0, 0])] #[case(bitvec![1, 1, 0, 0], bitvec![1, 0, 1, 0], bitvec![1, 0, 0, 0])] fn and_operations(
379 #[case] board1_bv: BitVec,
380 #[case] board2_bv: BitVec,
381 #[case] expected: BitVec,
382 ) {
383 let mut board1_arr = BitArray::<[usize; 1]>::default();
384 board1_arr[..board1_bv.len()].copy_from_bitslice(&board1_bv);
385 let bb1 = BitBoardStatic::<1> {
386 board: board1_arr,
387 n_rows: 2,
388 n_cols: 2,
389 };
390
391 let mut board2_arr = BitArray::<[usize; 1]>::default();
392 board2_arr[..board2_bv.len()].copy_from_bitslice(&board2_bv);
393
394 let bb2 = BitBoardStatic::<1> {
395 board: board2_arr,
396 n_rows: 2,
397 n_cols: 2,
398 };
399
400 let result = bb1.and(&bb2).unwrap();
401 assert_eq!(result.board()[..expected.len()].to_bitvec(), expected);
402 assert_eq!(result.n_rows(), 2);
403 assert_eq!(result.n_cols(), 2);
404 }
405
406 #[rstest]
407 #[case(bitvec![0, 0, 0, 0], bitvec![0, 0, 0, 0], bitvec![0, 0, 0, 0])] #[case(bitvec![1, 1, 1, 1], bitvec![1, 1, 1, 1], bitvec![1, 1, 1, 1])] #[case(bitvec![0, 0, 0, 0], bitvec![1, 1, 1, 1], bitvec![1, 1, 1, 1])] #[case(bitvec![0, 0, 0, 0], bitvec![1, 0, 0, 1], bitvec![1, 0, 0, 1])] #[case(bitvec![1, 0, 1, 0], bitvec![0, 1, 0, 1], bitvec![1, 1, 1, 1])] #[case(bitvec![1, 1, 0, 0], bitvec![0, 0, 1, 1], bitvec![1, 1, 1, 1])] #[case(bitvec![1, 0, 0, 1], bitvec![0, 1, 1, 0], bitvec![1, 1, 1, 1])] fn or_operations(
415 #[case] board1_bv: BitVec,
416
417 #[case] board2_bv: BitVec,
418
419 #[case] expected: BitVec,
420 ) {
421 let mut board1_arr = BitArray::<[usize; 1]>::default();
422 board1_arr[..board1_bv.len()].copy_from_bitslice(&board1_bv);
423
424 let bb1 = BitBoardStatic::<1> {
425 board: board1_arr,
426 n_rows: 2,
427 n_cols: 2,
428 };
429
430 let mut board2_arr = BitArray::<[usize; 1]>::default();
431 board2_arr[..board2_bv.len()].copy_from_bitslice(&board2_bv);
432 let bb2 = BitBoardStatic::<1> {
433 board: board2_arr,
434 n_rows: 2,
435 n_cols: 2,
436 };
437
438 let result = bb1.or(&bb2).unwrap();
439 assert_eq!(result.board()[..expected.len()].to_bitvec(), expected);
440 assert_eq!(result.n_rows(), 2);
441 assert_eq!(result.n_cols(), 2);
442 }
443
444 #[test]
445 fn and_or_larger_boards() {
446 let mut bb1 = BitBoardStatic::<1>::new(3, 3);
447 bb1.set_row(0, true); bb1.set(2, 2, true); let mut bb2 = BitBoardStatic::<1>::new(3, 3);
451 bb2.set_col(0, true); bb2.set(1, 1, true); let and_result = bb1.and(&bb2).unwrap();
457 assert!(and_result.board()[0]); assert!(!and_result.board()[1]); assert!(!and_result.board()[2]); assert!(!and_result.board()[3]); assert!(!and_result.board()[4]); assert!(!and_result.board()[6]); let or_result = bb1.or(&bb2).unwrap();
467 assert!(or_result.board()[0]); assert!(or_result.board()[1]); assert!(or_result.board()[2]); assert!(or_result.board()[3]); assert!(or_result.board()[4]); assert!(!or_result.board()[5]); assert!(or_result.board()[6]); assert!(!or_result.board()[7]); assert!(or_result.board()[8]); }
477
478 #[test]
479 fn and_or_preserve_original_boards() {
480 let mut bb1 = BitBoardStatic::<1>::new(2, 2);
481 bb1.set(0, 0, true);
482 let bb1_original = bb1;
483
484 let mut bb2 = BitBoardStatic::<1>::new(2, 2);
485 bb2.set(1, 1, true);
486 let bb2_original = bb2;
487
488 let _and_result = bb1.and(&bb2).unwrap();
490 let _or_result = bb1.or(&bb2).unwrap();
491
492 assert_eq!(bb1, bb1_original);
494 assert_eq!(bb2, bb2_original);
495 }
496}