From 2394afc2104a6359b010dd9ba848e7246814c1ab Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sat, 6 Jan 2024 19:41:26 -0800 Subject: [PATCH] [board] Implement a TrailingBitScanner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This bit scanner iterates populated bits in a BitBoard from the trailing (least-significant) end. Rename BitScanner → LeadingBitScanner. Factor implementation of these two into a macro. Clean up iterator returned by BitBoard::occupied_squares Implement BitBoard::occupied_squares_trailing --- board/src/bitboard/bit_scanner.rs | 86 +++++++++++++++++++++++++------ board/src/bitboard/bitboard.rs | 17 +++--- board/src/bitboard/mod.rs | 2 +- 3 files changed, 83 insertions(+), 22 deletions(-) diff --git a/board/src/bitboard/bit_scanner.rs b/board/src/bitboard/bit_scanner.rs index 257a13a..bf90394 100644 --- a/board/src/bitboard/bit_scanner.rs +++ b/board/src/bitboard/bit_scanner.rs @@ -1,17 +1,24 @@ // Eryn Wells -pub(crate) struct BitScanner { - bits: u64, - shift: usize, +macro_rules! bit_scanner { + ($name:ident) => { + pub(crate) struct $name { + bits: u64, + shift: usize, + } + + impl $name { + pub(crate) fn new(bits: u64) -> Self { + Self { bits, shift: 0 } + } + } + }; } -impl BitScanner { - pub(crate) fn new(bits: u64) -> BitScanner { - BitScanner { bits, shift: 0 } - } -} +bit_scanner!(LeadingBitScanner); +bit_scanner!(TrailingBitScanner); -impl Iterator for BitScanner { +impl Iterator for LeadingBitScanner { type Item = usize; fn next(&mut self) -> Option { @@ -37,30 +44,79 @@ impl Iterator for BitScanner { } } +impl Iterator for TrailingBitScanner { + type Item = usize; + + fn next(&mut self) -> Option { + let u64bits = u64::BITS as usize; + + if self.shift == u64bits { + return None; + } + + let shifted_bits = self.bits >> self.shift; + let trailing_zeros = shifted_bits.trailing_zeros() as usize; + + if trailing_zeros == u64bits { + self.shift = trailing_zeros; + return None; + } + + let position = self.shift + trailing_zeros; + // Shift 1 additional place to account for the 1 that `leading_zeros` found. + self.shift += trailing_zeros + 1; + + Some(position) + } +} + #[cfg(test)] mod tests { use super::*; #[test] - fn zero() { - let mut scanner = BitScanner::new(0); + fn leading_zero() { + let mut scanner = LeadingBitScanner::new(0); assert_eq!(scanner.next(), None); } #[test] - fn one() { - let mut scanner = BitScanner::new(1); + fn leading_one() { + let mut scanner = LeadingBitScanner::new(1); assert_eq!(scanner.next(), Some(0)); assert_eq!(scanner.next(), None); } #[test] - fn complex() { - let mut scanner = BitScanner::new(0b11000101); + fn leading_complex() { + let mut scanner = LeadingBitScanner::new(0b11000101); 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(), None); } + + #[test] + fn trailing_zero() { + let mut scanner = TrailingBitScanner::new(0); + assert_eq!(scanner.next(), None); + } + + #[test] + fn trailing_one() { + let mut scanner = TrailingBitScanner::new(1); + assert_eq!(scanner.next(), Some(0)); + assert_eq!(scanner.next(), None); + } + + #[test] + fn trailing_complex() { + let mut scanner = TrailingBitScanner::new(0b11000101); + 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(), None); + } } diff --git a/board/src/bitboard/bitboard.rs b/board/src/bitboard/bitboard.rs index c268ff7..946b4e0 100644 --- a/board/src/bitboard/bitboard.rs +++ b/board/src/bitboard/bitboard.rs @@ -1,8 +1,8 @@ // Eryn Wells use super::library::{library, FILES, RANKS}; -use super::BitScanner; -use crate::Square; +use super::{LeadingBitScanner, TrailingBitScanner}; +use crate::{square::Direction, Square}; use std::fmt; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}; @@ -62,11 +62,16 @@ impl BitBoard { } impl BitBoard { + /// Return an Iterator over the occupied squares, starting from the leading + /// (most-significant bit) end of the field. pub(crate) fn occupied_squares(&self) -> impl Iterator { - BitScanner::new(self.0) - .map(|x| u8::try_from(x)) - .take_while(|x| x.is_ok()) - .map(|x| Square::from_index(x.unwrap() as usize)) + LeadingBitScanner::new(self.0).map(Square::from_index) + } + + /// Return an Iterator over the occupied squares, starting from the trailing + /// (least-significant bit) end of the field. + pub(crate) fn occupied_squares_trailing(&self) -> impl Iterator { + LeadingBitScanner::new(self.0).map(Square::from_index) } } diff --git a/board/src/bitboard/mod.rs b/board/src/bitboard/mod.rs index 02fd8bd..f462e3b 100644 --- a/board/src/bitboard/mod.rs +++ b/board/src/bitboard/mod.rs @@ -3,5 +3,5 @@ mod bitboard; mod library; mod shifts; -pub(crate) use self::bit_scanner::BitScanner; +pub(crate) use self::bit_scanner::{LeadingBitScanner, TrailingBitScanner}; pub(crate) use self::bitboard::BitBoard;