From 5499628be01d8c4efde40b8133fef21279d4165d Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Tue, 18 Feb 2025 21:51:13 -0500 Subject: [PATCH] add dual impl for `bitboard` (bitvec and standard) --- Cargo.toml | 5 +++- src/bitboard.rs | 64 +++++++++++++++++++++++++++++++++++++++++++------ src/board.rs | 1 + 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index da379e9..5a88ae9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,12 @@ debug = true lto = true +[features] +bitvec = [ "dep:bitvec" ] + [dependencies] arrayvec = "0.7" -bitvec = "1" +bitvec = { version = "1", optional = true } either = "1.13" indicatif = "0.17" lazy_static = "1.5" diff --git a/src/bitboard.rs b/src/bitboard.rs index ee79c18..b2b5083 100644 --- a/src/bitboard.rs +++ b/src/bitboard.rs @@ -1,16 +1,27 @@ use crate::board::{BOARD_AREA, BOARD_SIZE}; -use bitvec::prelude::*; 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% 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")] +pub type BitBoardInner = BitArr!(for BOARD_AREA, in u64, 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); -/// Backing Type of BitBoard -type BBBaseType = u64; - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct BitBoard(BitArr!(for BOARD_AREA, in BBBaseType, Lsb0)); - impl Default for BitBoard { fn default() -> Self { Self::new() @@ -18,22 +29,61 @@ impl Default for BitBoard { } 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)) & 1) != 0 + } + + #[cfg(not(feature = "bitvec"))] + pub const fn set(&mut self, row: usize, col: usize, value: bool) { + let index = Self::get_index(row, col); + if value { + self.set_bit(index); // Set the bit at (row, col) to 1 + } else { + self.clear_bit(index); // Clear the bit at (row, col) + } + } + + #[cfg(not(feature = "bitvec"))] + const fn clear_bit(&mut self, index: usize) { + self.0 &= !(1 << index) + } + + #[cfg(not(feature = "bitvec"))] + const fn set_bit(&mut self, index: usize) { + self.0 |= 1 << index + } + + #[cfg(not(feature = "bitvec"))] + pub const fn count(&self) -> usize { + self.0.count_ones() as usize + } + 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); } + #[cfg(feature = "bitvec")] pub fn count(&self) -> usize { self.0.count_ones() } diff --git a/src/board.rs b/src/board.rs index 4721ede..7d4141d 100644 --- a/src/board.rs +++ b/src/board.rs @@ -11,6 +11,7 @@ use std::{cmp::Ordering, fmt}; pub const BOARD_SIZE: usize = 8; /// Area of the board +#[allow(dead_code)] pub const BOARD_AREA: usize = BOARD_SIZE * BOARD_SIZE; /// A chain of positions across the board