This commit is contained in:
Eryn Wells 2025-05-08 17:37:51 -07:00
parent d5cdf273c8
commit 091cc99cb3
42 changed files with 805 additions and 1662 deletions

View file

@ -1,8 +1,10 @@
// Eryn Wells <eryn@erynwells.me>
use chessfriend_core::Square;
macro_rules! bit_scanner {
($name:ident) => {
pub(crate) struct $name {
pub struct $name {
bits: u64,
shift: usize,
}
@ -18,8 +20,15 @@ macro_rules! bit_scanner {
bit_scanner!(LeadingBitScanner);
bit_scanner!(TrailingBitScanner);
fn _index_to_square(index: usize) -> Square {
unsafe {
#[allow(clippy::cast_possible_truncation)]
Square::from_index_unchecked(index as u8)
}
}
impl Iterator for LeadingBitScanner {
type Item = usize;
type Item = Square;
fn next(&mut self) -> Option<Self::Item> {
let u64bits = u64::BITS as usize;
@ -40,12 +49,12 @@ impl Iterator for LeadingBitScanner {
// Shift 1 additional place to account for the 1 that `leading_zeros` found.
self.shift += leading_zeros + 1;
Some(position)
Some(_index_to_square(position))
}
}
impl Iterator for TrailingBitScanner {
type Item = usize;
type Item = Square;
fn next(&mut self) -> Option<Self::Item> {
let u64bits = u64::BITS as usize;
@ -66,7 +75,7 @@ impl Iterator for TrailingBitScanner {
// Shift 1 additional place to account for the 1 that `leading_zeros` found.
self.shift += trailing_zeros + 1;
Some(position)
Some(_index_to_square(position))
}
}
@ -83,17 +92,17 @@ mod tests {
#[test]
fn leading_one() {
let mut scanner = LeadingBitScanner::new(1);
assert_eq!(scanner.next(), Some(0));
assert_eq!(scanner.next(), Some(Square::A1));
assert_eq!(scanner.next(), None);
}
#[test]
fn leading_complex() {
let mut scanner = LeadingBitScanner::new(0b_1100_0101);
assert_eq!(scanner.next(), Some(7));
assert_eq!(scanner.next(), Some(6));
assert_eq!(scanner.next(), Some(2));
assert_eq!(scanner.next(), Some(0));
assert_eq!(scanner.next(), Some(Square::H1));
assert_eq!(scanner.next(), Some(Square::G1));
assert_eq!(scanner.next(), Some(Square::C1));
assert_eq!(scanner.next(), Some(Square::A1));
assert_eq!(scanner.next(), None);
}
@ -106,17 +115,17 @@ mod tests {
#[test]
fn trailing_one() {
let mut scanner = TrailingBitScanner::new(1);
assert_eq!(scanner.next(), Some(0));
assert_eq!(scanner.next(), Some(Square::A1));
assert_eq!(scanner.next(), None);
}
#[test]
fn trailing_complex() {
let mut scanner = TrailingBitScanner::new(0b_1100_0101);
assert_eq!(scanner.next(), Some(0));
assert_eq!(scanner.next(), Some(2));
assert_eq!(scanner.next(), Some(6));
assert_eq!(scanner.next(), Some(7));
assert_eq!(scanner.next(), Some(Square::A1));
assert_eq!(scanner.next(), Some(Square::C1));
assert_eq!(scanner.next(), Some(Square::G1));
assert_eq!(scanner.next(), Some(Square::H1));
assert_eq!(scanner.next(), None);
}
}

View file

@ -144,7 +144,7 @@ impl BitBoard {
self.0 != 0
}
/// Returns `true` if this [BitBoard] has the bit corresponding to `square` set.
/// Returns `true` if this [`BitBoard`] has the bit corresponding to `square` set.
///
/// ## Examples
///
@ -179,7 +179,7 @@ impl BitBoard {
self.0.count_ones()
}
/// Set a square in this [BitBoard] by toggling the corresponding bit to 1.
/// Set a square in this [`BitBoard`] by toggling the corresponding bit to 1.
/// This always succeeds, even if the bit was already set.
///
/// ## Examples
@ -237,21 +237,20 @@ impl BitBoard {
&self,
direction: &IterationDirection,
) -> Box<dyn Iterator<Item = Square>> {
#[allow(clippy::cast_possible_truncation)]
fn index_to_square(index: usize) -> Square {
unsafe { Square::from_index_unchecked(index as u8) }
}
match direction {
IterationDirection::Leading => {
Box::new(LeadingBitScanner::new(self.0).map(index_to_square))
}
IterationDirection::Trailing => {
Box::new(TrailingBitScanner::new(self.0).map(index_to_square))
}
IterationDirection::Leading => Box::new(self.occupied_squares_leading()),
IterationDirection::Trailing => Box::new(self.occupied_squares_trailing()),
}
}
pub fn occupied_squares_leading(&self) -> LeadingBitScanner {
LeadingBitScanner::new(self.0)
}
pub fn occupied_squares_trailing(&self) -> TrailingBitScanner {
TrailingBitScanner::new(self.0)
}
#[must_use]
pub fn first_occupied_square(&self, direction: &IterationDirection) -> Option<Square> {
match direction {
@ -264,7 +263,7 @@ impl BitBoard {
/// board, starting at the leading (most-significant) end of the board. If
/// the board is empty, returns `None`.
#[must_use]
fn first_occupied_square_leading(self) -> Option<Square> {
pub fn first_occupied_square_leading(self) -> Option<Square> {
let leading_zeros = self._leading_zeros();
if leading_zeros < SQUARES_NUM {
unsafe {
@ -281,7 +280,7 @@ impl BitBoard {
/// board, starting at the trailing (least-significant) end of the board.
/// If the board is empty, returns `None`.
#[must_use]
fn first_occupied_square_trailing(self) -> Option<Square> {
pub fn first_occupied_square_trailing(self) -> Option<Square> {
let trailing_zeros = self._trailing_zeros();
if trailing_zeros < SQUARES_NUM {
@ -496,12 +495,9 @@ mod tests {
let bb = BitBoard(0b01010100);
let expected_squares = [Square::G1, Square::E1, Square::C1];
for (a, b) in bb
.occupied_squares(&IterationDirection::Leading)
.zip(expected_squares.iter().copied())
{
assert_eq!(a, b);
}
bb.occupied_squares(&IterationDirection::Leading)
.zip(expected_squares)
.for_each(|(a, b)| assert_eq!(a, b));
}
#[test]
@ -512,12 +508,9 @@ mod tests {
let expected_squares = [Square::H8, Square::F6, Square::C5, Square::E2, Square::D1];
for (a, b) in bb
.occupied_squares(&IterationDirection::Leading)
.zip(expected_squares.iter().cloned())
{
assert_eq!(a, b);
}
bb.occupied_squares(&IterationDirection::Leading)
.zip(expected_squares)
.for_each(|(a, b)| assert_eq!(a, b));
}
#[test]