137 lines
3.4 KiB
Rust
137 lines
3.4 KiB
Rust
use super::{
|
|
board::Board,
|
|
coords::{CoordPair, CoordPairInner},
|
|
CoordAxis,
|
|
};
|
|
use allocative::Allocative;
|
|
use static_assertions::const_assert;
|
|
|
|
pub type BitBoardInner = u64;
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq, Allocative)]
|
|
pub struct BitBoard(BitBoardInner);
|
|
|
|
// BitBoard should be big enough to fit all points on the board
|
|
const_assert!(std::mem::size_of::<BitBoard>() * 8 >= Board::AREA.0 as usize);
|
|
|
|
impl BitBoard {
|
|
#[allow(clippy::new_without_default)]
|
|
pub const fn new() -> Self {
|
|
Self(0)
|
|
}
|
|
|
|
pub const fn get(&self, coord: CoordPair) -> bool {
|
|
self.get_by_index(coord.0)
|
|
}
|
|
|
|
pub const fn set(&mut self, coord: CoordPair, value: bool) {
|
|
self.set_by_index(coord.0, value);
|
|
}
|
|
|
|
const fn get_by_index(&self, index: CoordPairInner) -> bool {
|
|
((self.0 >> index) & 0b1) != 0b0
|
|
}
|
|
|
|
const fn set_by_index(&mut self, index: CoordPairInner, value: bool) {
|
|
// PERF! branchless setting of bit (~+3% perf bump)
|
|
self.0 &= !(0b1 << index); // clear bit
|
|
self.0 |= (value as BitBoardInner) << index; // set bit (if needed)
|
|
}
|
|
|
|
pub const fn count(&self) -> usize {
|
|
self.0.count_ones() as usize
|
|
}
|
|
|
|
pub const fn is_empty(&self) -> bool {
|
|
self.0 == 0b0
|
|
}
|
|
|
|
pub const fn east(&self, n: usize) -> Self {
|
|
let mask = !Self::col_mask(Board::SIZE - 1).0; // Mask to block column BOARD_SIZE-1 bits
|
|
Self((self.0 & mask) << n)
|
|
}
|
|
|
|
pub const fn west(&self, n: usize) -> Self {
|
|
let mask = !Self::col_mask(0).0;
|
|
Self((self.0 & mask) >> n)
|
|
}
|
|
|
|
pub const fn north(&self, n: usize) -> Self {
|
|
Self(self.0 >> (Board::SIZE as usize * n))
|
|
}
|
|
|
|
pub const fn south(&self, n: usize) -> Self {
|
|
Self(self.0 << (Board::SIZE as usize * n))
|
|
}
|
|
|
|
pub const fn northeast(&self, n: usize) -> Self {
|
|
self.north(n).east(n)
|
|
}
|
|
|
|
pub const fn northwest(&self, n: usize) -> Self {
|
|
self.north(n).west(n)
|
|
}
|
|
|
|
pub const fn southeast(&self, n: usize) -> Self {
|
|
self.south(n).east(n)
|
|
}
|
|
|
|
pub const fn southwest(&self, n: usize) -> Self {
|
|
self.south(n).west(n)
|
|
}
|
|
|
|
// Mask for a specific column (e.g., col_mask(7) = 0x8080808080808080)
|
|
const fn col_mask(col: CoordAxis) -> Self {
|
|
let mut mask = 0b0;
|
|
let mut i = 0b0;
|
|
while i < Board::AREA.0 {
|
|
mask |= 0b1 << (i + col);
|
|
i += Board::SIZE;
|
|
}
|
|
Self(mask)
|
|
}
|
|
|
|
// Create a BitBoard from a single coordinate
|
|
pub const fn from_coord(coord: CoordPair) -> Self {
|
|
Self(1 << coord.0)
|
|
}
|
|
|
|
pub const fn intersects(self, other: Self) -> bool {
|
|
(self.0 & other.0) > 0b0
|
|
}
|
|
|
|
pub const fn bitor_assign(&mut self, other: Self) {
|
|
self.0 |= other.0;
|
|
}
|
|
|
|
pub const fn bitand_assign(&mut self, other: Self) {
|
|
self.0 &= other.0;
|
|
}
|
|
|
|
pub const fn not(self) -> Self {
|
|
Self(!self.0)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn set_and_get() {
|
|
let mut b = BitBoard::new();
|
|
for c in 0..Board::AREA.0 {
|
|
assert!(
|
|
!b.get(CoordPair(c)),
|
|
"A just-initalized BitBoard should be completely empty"
|
|
)
|
|
}
|
|
|
|
assert!(!b.get((2, 4).into()));
|
|
b.set((2, 4).into(), true);
|
|
assert!(b.get((2, 4).into()));
|
|
b.set((2, 4).into(), false);
|
|
assert!(!b.get((2, 4).into()));
|
|
}
|
|
}
|