// 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, Eq, Hash, PartialEq)] pub(crate) struct BitBoard(u64); impl BitBoard { pub fn empty() -> BitBoard { BitBoard(0) } pub fn from_bit_field(bits: u64) -> BitBoard { BitBoard(bits) } pub fn is_empty(&self) -> bool { self.0 == 0 } pub fn has_piece_at(&self, sq: &Square) -> bool { (self.0 & (1 << sq.index)) > 0 } pub fn place_piece_at(&mut self, sq: &Square) { self.0 |= 1 << sq.index } fn remove_piece_at(&mut self, sq: &Square) { self.0 &= !(1 << sq.index) } } impl BitBoard { const NOT_A_FILE: u64 = 0xfefefefefefefefe; const NOT_H_FILE: u64 = 0x7f7f7f7f7f7f7f7f; #[inline] pub fn shift_north_one(&self) -> BitBoard { BitBoard(self.0 << 8) } #[inline] pub fn shift_north_east_one(&self) -> BitBoard { BitBoard(self.0 << 9 & BitBoard::NOT_A_FILE) } #[inline] pub fn shift_east_one(&self) -> BitBoard { BitBoard(self.0 << 1 & BitBoard::NOT_A_FILE) } #[inline] pub fn shift_south_east_one(&self) -> BitBoard { BitBoard(self.0 >> 7 & BitBoard::NOT_A_FILE) } #[inline] pub fn shift_south_one(&self) -> BitBoard { BitBoard(self.0 >> 8) } #[inline] pub fn shift_south_west_one(&self) -> BitBoard { BitBoard(self.0 >> 9 & BitBoard::NOT_H_FILE) } #[inline] pub fn shift_west_one(&self) -> BitBoard { BitBoard(self.0 >> 1 & BitBoard::NOT_H_FILE) } #[inline] pub fn shift_north_west_one(&self) -> BitBoard { BitBoard(self.0 << 7 & BitBoard::NOT_H_FILE) } } impl BitBoard { 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_unsafe(x.unwrap())) } } impl fmt::Debug for BitBoard { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { let bits = self.0; write!(f, "BitBoard({bits:064b})") } } impl BitAnd for BitBoard { type Output = BitBoard; #[inline] fn bitand(self, rhs: Self) -> Self { BitBoard(self.0 & rhs.0) } } impl BitAnd<&BitBoard> for BitBoard { type Output = BitBoard; #[inline] fn bitand(self, rhs: &Self) -> Self { BitBoard(self.0 & rhs.0) } } impl Not for BitBoard { type Output = BitBoard; #[inline] fn not(self) -> Self::Output { BitBoard(!self.0) } } impl Not for &BitBoard { type Output = BitBoard; #[inline] fn not(self) -> Self::Output { BitBoard(!self.0) } } impl BitOr for BitBoard { type Output = BitBoard; #[inline] fn bitor(self, rhs: Self) -> Self { BitBoard(self.0 | rhs.0) } } impl BitOr<&BitBoard> for BitBoard { type Output = BitBoard; #[inline] fn bitor(self, rhs: &Self) -> Self { BitBoard(self.0 | rhs.0) } } #[cfg(test)] mod tests { use super::*; use crate::square::Square; #[test] fn is_empty() { assert!(BitBoard(0).is_empty()); } #[test] fn has_piece_at() { let bb = BitBoard(0b1001100); let c1 = Square::from_algebraic_string("c1").expect("Unable to get square for 'c1'"); assert!(bb.has_piece_at(&c1)); let b1 = Square::from_algebraic_string("b1").expect("Unable to get square for 'b1'"); assert!(!bb.has_piece_at(&b1)); } #[test] fn place_piece_at() { let sq = Square::from_index(34).expect("Invalid square index"); let mut bb = BitBoard(0b1001100); bb.place_piece_at(&sq); assert!(bb.has_piece_at(&sq)); } #[test] fn remove_piece_at() { let sq = Square::from_index(2).expect("Invalid square index"); let mut bb = BitBoard(0b1001100); bb.remove_piece_at(&sq); assert!(!bb.has_piece_at(&sq)); } #[test] fn cardinal_direction_shifts() { let bb = BitBoard(0x1000_0000); // e4 assert_eq!(bb.shift_north_one(), BitBoard(0x0010_0000_0000), "North"); assert_eq!(bb.shift_east_one(), BitBoard(0x2000_0000), "East"); assert_eq!(bb.shift_south_one(), BitBoard(0x0010_0000), "South"); assert_eq!(bb.shift_west_one(), BitBoard(0x0800_0000), "West"); } #[test] fn intercardinal_direction_shifts() { let bb = BitBoard(0x1000_0000); // e4 println!(" bb: {:?}", bb); assert_eq!( bb.shift_north_east_one(), BitBoard(0x0020_0000_0000), "North East" ); assert_eq!( bb.shift_south_east_one(), BitBoard(0x0020_0000), "South East" ); assert_eq!( bb.shift_south_west_one(), BitBoard(0x0008_0000), "South West" ); assert_eq!( bb.shift_north_west_one(), BitBoard(0x0008_0000_0000), "North West" ); } #[test] fn pieces() { let bb = BitBoard(0x1001_1010); // e4 let mut occupied_squares = bb.occupied_squares(); assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(28))); assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(16))); assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(12))); assert_eq!(occupied_squares.next(), Some(Square::from_index_unsafe(4))); assert_eq!(occupied_squares.next(), None); } }