diff --git a/board/src/bitboard.rs b/board/src/bitboard.rs index 83f1154..02f66ba 100644 --- a/board/src/bitboard.rs +++ b/board/src/bitboard.rs @@ -1,11 +1,14 @@ // Eryn Wells +mod bit_scanner; + +use self::bit_scanner::BitScanner; use crate::square::Square; use std::fmt; use std::ops::{BitAnd, BitOr, Not}; #[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct BitBoard(u64); +pub(crate) struct BitBoard(u64); impl BitBoard { pub fn empty() -> BitBoard { @@ -16,7 +19,7 @@ impl BitBoard { BitBoard(bits) } - fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { self.0 == 0 } @@ -31,20 +34,6 @@ impl BitBoard { fn remove_piece_at(&mut self, sq: &Square) { self.0 &= !(1 << sq.index) } - - #[cfg(target_arch = "arm")] - fn _arm_count_leading_zeros(&self) -> u8 { - let mut number_of_leading_zeros: u8 = 0; - unsafe { - asm!( - "clz {count}, {bitfield}", - count = out(reg) number_of_leading_zeros, - bitfield = in(reg) self.0, - ); - } - - number_of_leading_zeros - } } impl BitBoard { @@ -92,6 +81,15 @@ impl BitBoard { } } +impl BitBoard { + fn pieces(&self) -> impl Iterator { + BitScanner::new(self.0) + .map(|x| u8::try_from(x)) + .take_while(|x| x.is_ok()) + .map(|x| Square::from_index_unsafe(x.unwrap())) + } +} + impl fmt::Debug for BitBoard { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { let bits = self.0; @@ -229,12 +227,15 @@ mod tests { ); } - #[cfg(target_arch = "arm")] #[test] - fn arm_count_leading_zeros() { - assert_eq!(BitBoard(0)._arm_count_leading_zeros(), 0); - assert_eq!(BitBoard(0b10)._arm_count_leading_zeros(), 62); - assert_eq!(BitBoard(0b1010)._arm_count_leading_zeros(), 60); - assert_eq!(BitBoard(0x80000000)._arm_count_leading_zeros(), 0); + fn pieces() { + let bb = BitBoard(0x1001_1010); // e4 + + let mut pieces = bb.pieces(); + assert_eq!(pieces.next(), Some(Square::from_index_unsafe(28))); + assert_eq!(pieces.next(), Some(Square::from_index_unsafe(16))); + assert_eq!(pieces.next(), Some(Square::from_index_unsafe(12))); + assert_eq!(pieces.next(), Some(Square::from_index_unsafe(4))); + assert_eq!(pieces.next(), None); } } diff --git a/board/src/bitboard/bit_scanner.rs b/board/src/bitboard/bit_scanner.rs new file mode 100644 index 0000000..3d45b2c --- /dev/null +++ b/board/src/bitboard/bit_scanner.rs @@ -0,0 +1,67 @@ +// Eryn Wells + +use super::BitBoard; +use crate::square::Square; + +pub(crate) struct BitScanner { + bits: u64, + shift: u32, +} + +impl BitScanner { + pub(crate) fn new(bits: u64) -> BitScanner { + BitScanner { bits, shift: 0 } + } +} + +impl Iterator for BitScanner { + type Item = u32; + + fn next(&mut self) -> Option { + if self.shift == u64::BITS { + return None; + } + + let shifted_bits = self.bits << self.shift; + let leading_zeros = shifted_bits.leading_zeros(); + + if leading_zeros == u64::BITS { + self.shift = leading_zeros; + return None; + } + + let position = u64::BITS - (self.shift + leading_zeros + 1); + // Shift 1 additional place to account for the 1 that `leading_zeros` found. + self.shift += leading_zeros + 1; + + Some(position) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn zero() { + let mut scanner = BitScanner::new(0); + assert_eq!(scanner.next(), None); + } + + #[test] + fn one() { + let mut scanner = BitScanner::new(1); + assert_eq!(scanner.next(), Some(0)); + assert_eq!(scanner.next(), None); + } + + #[test] + fn complex() { + let mut scanner = BitScanner::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); + } +} diff --git a/board/src/square.rs b/board/src/square.rs index 9f3e581..b96d4a6 100644 --- a/board/src/square.rs +++ b/board/src/square.rs @@ -25,7 +25,7 @@ impl Square { Ok(Square::from_index_unsafe(index)) } - fn from_index_unsafe(index: u8) -> Square { + pub(crate) fn from_index_unsafe(index: u8) -> Square { Square { rank: index / 8, file: index % 8,