[bitboard] Replace separate methods for leading and trailing iteration

Add chessfriend_bitboard::IterationDirection
Make BitBoard::occupied_squares() take an IterationDirection and return an iterator
corresponding to the direction.
Do the same for ::first_occupied_square().
This commit is contained in:
Eryn Wells 2025-05-02 14:26:09 -07:00
parent 9f62996175
commit 7b0469d689
3 changed files with 44 additions and 19 deletions

View file

@ -1,7 +1,8 @@
// Eryn Wells <eryn@erynwells.me>
use crate::bit_scanner::{LeadingBitScanner, TrailingBitScanner};
use crate::direction::IterationDirection;
use crate::library;
use crate::{LeadingBitScanner, TrailingBitScanner};
use chessfriend_core::{Color, Direction, File, Rank, Square};
use forward_ref::{forward_ref_binop, forward_ref_op_assign, forward_ref_unop};
use std::fmt;
@ -223,27 +224,39 @@ impl BitBoard {
self.0.is_power_of_two()
}
/// Return an Iterator over the occupied squares. The Iterator yields
/// squares starting from the leading (most-significant bit) end of the
/// board.
/// Return an Iterator over the occupied squares.
#[must_use]
pub fn occupied_squares(&self) -> impl Iterator<Item = Square> {
LeadingBitScanner::new(self.0).map(|idx| unsafe { Square::from_index_unchecked(idx as u8) })
pub fn occupied_squares(
&self,
direction: IterationDirection,
) -> Box<dyn Iterator<Item = Square>> {
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))
}
}
}
/// Return an Iterator over the occupied squares. The Iterator yields
/// squares starting from the trailing (least-significant bit) end of the
/// board.
pub fn occupied_squares_trailing(&self) -> impl Iterator<Item = Square> {
TrailingBitScanner::new(self.0)
.map(|idx| unsafe { Square::from_index_unchecked(idx as u8) })
#[must_use]
pub fn first_occupied_square(&self, direction: IterationDirection) -> Option<Square> {
match direction {
IterationDirection::Leading => self.first_occupied_square_leading(),
IterationDirection::Trailing => self.first_occupied_square_trailing(),
}
}
/// If the board is not empty, returns the first occupied square on the
/// board, starting at the leading (most-significant) end of the board. If
/// the board is empty, returns `None`.
#[must_use]
pub fn first_occupied_square(&self) -> Option<Square> {
fn first_occupied_square_leading(&self) -> Option<Square> {
let leading_zeros = self.0.leading_zeros() as u8;
if leading_zeros < Square::NUM as u8 {
unsafe {
@ -260,7 +273,7 @@ impl BitBoard {
/// board, starting at the trailing (least-significant) end of the board.
/// If the board is empty, returns `None`.
#[must_use]
pub fn first_occupied_square_trailing(&self) -> Option<Square> {
fn first_occupied_square_trailing(&self) -> Option<Square> {
let trailing_zeros = self.0.trailing_zeros() as u8;
if trailing_zeros < Square::NUM as u8 {
unsafe { Some(Square::from_index_unchecked(trailing_zeros)) }
@ -458,7 +471,10 @@ mod tests {
fn single_rank_occupancy() {
let bb = BitBoard(0b01010100);
let expected_squares = [Square::G1, Square::E1, Square::C1];
for (a, b) in bb.occupied_squares().zip(expected_squares.iter().cloned()) {
for (a, b) in bb
.occupied_squares(IterationDirection::Leading)
.zip(expected_squares.iter().cloned())
{
assert_eq!(a, b);
}
}
@ -470,7 +486,10 @@ mod tests {
let expected_squares = [Square::H8, Square::F6, Square::C5, Square::E2, Square::D1];
for (a, b) in bb.occupied_squares().zip(expected_squares.iter().cloned()) {
for (a, b) in bb
.occupied_squares(IterationDirection::Leading)
.zip(expected_squares.iter().cloned())
{
assert_eq!(a, b);
}
}
@ -514,7 +533,7 @@ mod tests {
#[test]
fn first_occupied_squares() {
let bb = bitboard![A8 E1];
assert_eq!(bb.first_occupied_square(), Some(Square::A8));
assert_eq!(bb.first_occupied_square_leading(), Some(Square::A8));
assert_eq!(bb.first_occupied_square_trailing(), Some(Square::E1));
let bb = bitboard![D6 E7 F8];

View file

@ -0,0 +1,6 @@
#[derive(Default)]
pub enum IterationDirection {
#[default]
Leading,
Trailing,
}

View file

@ -2,12 +2,12 @@
mod bit_scanner;
mod bitboard;
mod direction;
mod library;
mod shifts;
pub use bitboard::BitBoard;
pub(crate) use bit_scanner::{LeadingBitScanner, TrailingBitScanner};
pub use direction::IterationDirection;
#[macro_export]
macro_rules! bitboard {