BitBoard changes

- Clean up operator traits: remove the & versions and make the macro do more with less input
- Implement From<Square> for BitBoards: simplify conversion of Squares to BitBoards
- Take BitBoards by value in several places
- Clean up unit tests
This commit is contained in:
Eryn Wells 2024-01-06 16:11:52 -08:00
parent 2105004dc2
commit ff839db041

View file

@ -42,24 +42,22 @@ impl BitBoard {
moves_getter!(queen_moves);
moves_getter!(king_moves);
pub fn from_square(sq: Square) -> BitBoard {
BitBoard(1 << sq.index())
}
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 has_piece_at(self, sq: Square) -> bool {
!(self & sq.into()).is_empty()
}
pub fn place_piece_at(&mut self, sq: &Square) {
self.0 |= 1 << sq.index()
pub fn place_piece_at(&mut self, sq: Square) {
let sq_bb: BitBoard = sq.into();
*self |= sq_bb
}
fn remove_piece_at(&mut self, sq: &Square) {
self.0 &= !(1 << sq.index())
fn remove_piece_at(&mut self, sq: Square) {
let sq_bb: BitBoard = sq.into();
*self &= !sq_bb
}
}
@ -68,7 +66,13 @@ impl BitBoard {
BitScanner::new(self.0)
.map(|x| u8::try_from(x))
.take_while(|x| x.is_ok())
.map(|x| Square::from_index_unsafe(x.unwrap()))
.map(|x| Square::from_index(x.unwrap() as usize))
}
}
impl From<Square> for BitBoard {
fn from(value: Square) -> Self {
BitBoard(1 << value as u64)
}
}
@ -125,41 +129,35 @@ impl fmt::Debug for BitBoard {
}
macro_rules! infix_op {
($trait_type:ident, $func_name:ident, $left_type:ty, $right_type:ty, $op:tt) => {
($trait_type:ident, $func_name:ident, $left_type:ty, $right_type:ty) => {
impl $trait_type<$right_type> for $left_type {
type Output = BitBoard;
#[inline]
fn $func_name(self, rhs: $right_type) -> Self::Output {
BitBoard(self.0 $op rhs.0)
BitBoard($trait_type::$func_name(self.0, rhs.0))
}
}
};
}
macro_rules! assign_op {
($trait_type:ident, $func_name:ident, $left_type:ty, $op:tt) => {
($trait_type:ident, $func_name:ident, $left_type:ty) => {
impl $trait_type for $left_type {
#[inline]
fn $func_name(&mut self, rhs: Self) {
self.0 $op rhs.0
$trait_type::$func_name(&mut self.0, rhs.0)
}
}
};
}
infix_op!(BitAnd, bitand, BitBoard, BitBoard, &);
infix_op!(BitAnd, bitand, &BitBoard, BitBoard, &);
infix_op!(BitAnd, bitand, BitBoard, &BitBoard, &);
infix_op!(BitAnd, bitand, &BitBoard, &BitBoard, &);
infix_op!(BitAnd, bitand, BitBoard, BitBoard);
assign_op!(BitAndAssign, bitand_assign, BitBoard, &=);
assign_op!(BitOrAssign, bitor_assign, BitBoard, |=);
assign_op!(BitAndAssign, bitand_assign, BitBoard);
assign_op!(BitOrAssign, bitor_assign, BitBoard);
infix_op!(BitOr, bitor, BitBoard, BitBoard, |);
infix_op!(BitOr, bitor, &BitBoard, BitBoard, |);
infix_op!(BitOr, bitor, BitBoard, &BitBoard, |);
infix_op!(BitOr, bitor, &BitBoard, &BitBoard, |);
infix_op!(BitOr, bitor, BitBoard, BitBoard);
impl Not for BitBoard {
type Output = BitBoard;
@ -212,41 +210,44 @@ mod tests {
#[test]
fn has_piece_at() {
let bb = BitBoard(0b1001100);
let c1 = Square::from_algebraic_str("c1").expect("Unable to get square for 'c1'");
assert!(bb.has_piece_at(&c1));
let b1 = Square::from_algebraic_str("b1").expect("Unable to get square for 'b1'");
assert!(!bb.has_piece_at(&b1));
assert!(bb.has_piece_at(Square::C1));
assert!(!bb.has_piece_at(Square::B1));
}
#[test]
fn place_piece_at() {
let sq = Square::from_index(34).expect("Invalid square index");
let sq = Square::E4;
let mut bb = BitBoard(0b1001100);
bb.place_piece_at(&sq);
assert!(bb.has_piece_at(&sq));
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 sq = Square::A3;
let mut bb = BitBoard(0b1001100);
bb.remove_piece_at(&sq);
assert!(!bb.has_piece_at(&sq));
bb.remove_piece_at(sq);
assert!(!bb.has_piece_at(sq));
}
#[test]
fn pieces() {
let bb = BitBoard(0x1001_1010); // e4
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()) {
assert_eq!(a, b);
}
}
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);
#[test]
fn occupancy_spot_check() {
let bb =
BitBoard(0b10000000_00000000_00100000_00000100_00000000_00000000_00010000_00001000);
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()) {
assert_eq!(a, b);
}
}
}