use crate::board::{BOARD_AREA, BOARD_SIZE}; use const_fn::const_fn; use static_assertions::const_assert; // quick explanation for the dual-nature of [`BitBoard`] // There's both a `bitvec` impl (which is variable length) // and a `native` impl which uses a u64 as the backing type // the `native` impl is ~15-25% faster (in non-BitBoard specific benchmarks) // `bitvec` is only really useful if you're using esoteric board sizes #[cfg(feature = "bitvec")] use bitvec::prelude::*; #[cfg(feature = "bitvec")] type BBBaseType = u64; #[cfg(feature = "bitvec")] pub type BitBoardInner = BitArr!(for BOARD_AREA, in BBBaseType, Lsb0); #[cfg(not(feature = "bitvec"))] pub type BitBoardInner = u64; #[derive(Copy, Clone, PartialEq, Eq)] pub struct BitBoard(BitBoardInner); // BitBoard should be big enough to fit all points on the board const_assert!(std::mem::size_of::() * 8 >= BOARD_AREA); impl Default for BitBoard { fn default() -> Self { Self::new() } } impl BitBoard { #[cfg(feature = "bitvec")] pub const fn new() -> Self { Self(bitarr!(BBBaseType, Lsb0; 0; BOARD_AREA)) } #[cfg(not(feature = "bitvec"))] pub const fn new() -> Self { Self(0) } #[cfg(not(feature = "bitvec"))] pub const fn get(&self, row: usize, col: usize) -> bool { ((self.0 >> Self::get_index(row, col)) & 0b1) != 0b0 } #[cfg(not(feature = "bitvec"))] pub const fn set(&mut self, row: usize, col: usize, value: bool) { let index = Self::get_index(row, col); // PERF! branchless setting of bit (~+3% perf bump) self.0 &= !(0b1 << index); // clear bit self.0 |= (value as BitBoardInner) << index; // set bit (if needed) } const fn get_index(row: usize, col: usize) -> usize { row * BOARD_SIZE + col } #[cfg(feature = "bitvec")] pub fn get(&self, row: usize, col: usize) -> bool { self.0[Self::get_index(row, col)] } #[cfg(feature = "bitvec")] pub fn set(&mut self, row: usize, col: usize, value: bool) { self.0.set(Self::get_index(row, col), value); } // works on both `bitvec` and native (const on native) #[const_fn(cfg(not(feature = "bitvec")))] pub const fn count(&self) -> usize { self.0.count_ones() as usize } } #[cfg(test)] mod test { use super::*; #[test] fn set_and_get() { let mut b = BitBoard::new(); for i in 0..BOARD_SIZE { for j in 0..BOARD_SIZE { assert!( !b.get(i, j), "A just-initalized BitBoard should be completely empty" ) } } assert!(!b.get(2, 4)); b.set(2, 4, true); assert!(b.get(2, 4)); b.set(2, 4, false); assert!(!b.get(2, 4)); } }