[board] Implement a BitScanner on u64
This struct implements an Iterator that returns the positions of the 1 bits in the u64.
BitBoard takes this iterator and maps it into Squares in the pieces() method.
This is laying the groundwork for an iterator over the pieces in a Position. 👀
			
			
This commit is contained in:
		
							parent
							
								
									a2f88f0fde
								
							
						
					
					
						commit
						1da827a3bb
					
				
					 3 changed files with 91 additions and 23 deletions
				
			
		| 
						 | 
				
			
			@ -1,11 +1,14 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
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<Item = Square> {
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										67
									
								
								board/src/bitboard/bit_scanner.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								board/src/bitboard/bit_scanner.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
// Eryn Wells <eryn@erynwells.me>
 | 
			
		||||
 | 
			
		||||
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<Self::Item> {
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue