diff --git a/src/repr/bitboard.rs b/src/repr/bitboard.rs index 4728493..799393d 100644 --- a/src/repr/bitboard.rs +++ b/src/repr/bitboard.rs @@ -79,18 +79,6 @@ impl BitBoard { self.south(n).west(n) } - /// All direction methods - pub const DIRECTIONS: [fn(&Self, usize) -> Self; 8] = [ - BitBoard::east, - BitBoard::west, - BitBoard::north, - BitBoard::south, - BitBoard::northeast, - BitBoard::northwest, - BitBoard::southeast, - BitBoard::southwest, - ]; - // Mask for a specific column (e.g., col_mask(7) = 0x8080808080808080) const fn col_mask(col: CoordAxis) -> Self { let mut mask = 0; @@ -110,12 +98,16 @@ impl BitBoard { pub const fn intersects(self, other: Self) -> bool { (self.0 & other.0) > 0 } -} -impl std::ops::Not for BitBoard { - type Output = BitBoard; + pub const fn bitor_assign(&mut self, other: Self) { + self.0 = self.0 | other.0; + } - fn not(self) -> Self::Output { + pub const fn bitand_assign(&mut self, other: Self) { + self.0 = self.0 & other.0; + } + + pub const fn not(self) -> Self { Self(!self.0) } } @@ -136,18 +128,6 @@ impl std::ops::BitOr for BitBoard { } } -impl std::ops::BitAndAssign for BitBoard { - fn bitand_assign(&mut self, rhs: Self) { - *self = *self & rhs; - } -} - -impl std::ops::BitOrAssign for BitBoard { - fn bitor_assign(&mut self, rhs: Self) { - *self = *self | rhs; - } -} - #[cfg(test)] mod test { use super::*; diff --git a/src/repr/board.rs b/src/repr/board.rs index f5762ed..a74e524 100644 --- a/src/repr/board.rs +++ b/src/repr/board.rs @@ -227,22 +227,26 @@ impl Board { /// Return a modified [`Board`] with the piece placed at a position /// Returns None if the move was invalid - pub fn what_if(&self, coord: CoordPair, piece: Piece) -> Result { + pub const fn what_if(&self, coord: CoordPair, piece: Piece) -> Option { // extract check here to avoid copy if self.get(coord).is_some() { - return Err("position is occupied"); + return None; } let mut self_copy = *self; - self_copy.place(coord, piece).map(|_| self_copy) + if let Ok(_) = self_copy.place(coord, piece) { + Some(self_copy) + } else { + None + } } /// Returns a bool which represents whether or not a move would propegate and be valid - pub fn would_prop(&self, coord: CoordPair, piece: Piece) -> bool { + pub const fn would_prop(&self, coord: CoordPair, piece: Piece) -> bool { self.get(coord).is_none() && !self.propegate_from_dry(coord, piece).is_empty() } - pub fn place(&mut self, coord: CoordPair, piece: Piece) -> Result<(), &'static str> { + pub const fn place(&mut self, coord: CoordPair, piece: Piece) -> Result<(), &'static str> { if self.get(coord).is_some() { return Err("position is occupied"); } @@ -257,7 +261,7 @@ impl Board { /// Propegate the board and captures starting from a specific position /// returns true if flips occurred - fn propegate_from(&mut self, coord: CoordPair, starting_color: Piece) -> bool { + const fn propegate_from(&mut self, coord: CoordPair, starting_color: Piece) -> bool { let flip_mask = self.propegate_from_dry(coord, starting_color); let did_flip = !flip_mask.is_empty(); @@ -267,40 +271,52 @@ impl Board { did_flip } - fn apply_flip_mask(&mut self, color: Piece, flip_mask: BitBoard) { + const fn apply_flip_mask(&mut self, color: Piece, flip_mask: BitBoard) { + // did some investigation, seems using xor actually decreases + // performance over the branchfull impl? 3.2-3.5% slower get_board!(mut self, color).bitor_assign(flip_mask); - get_board!(mut self, color.flip()).bitand_assign(!flip_mask); + get_board!(mut self, color.flip()).bitand_assign(flip_mask.not()); } /// Propegate piece captures originating from (i, j) /// DO NOT USE THIS ALONE, this should be called as a part of /// [`Board::place`] or [`Board::place_and_prop_unchecked`] - fn propegate_from_dry(&self, coords: CoordPair, starting_color: Piece) -> BitBoard { + const fn propegate_from_dry(&self, coords: CoordPair, starting_color: Piece) -> BitBoard { let player_board = get_board!(self, starting_color); let opponent_board = get_board!(self, starting_color.flip()); let mut flip_mask = BitBoard::new(); let seed = BitBoard::from_coord(coords); - for dir in BitBoard::DIRECTIONS { - let mut current = seed; - let mut temp_flips = BitBoard::new(); + macro_rules! apply_dir { + ($base:expr, $sum_mask:expr, $dir:expr) => { + let mut current = $base; + let mut temp_flips = BitBoard::new(); - // Expand in direction until edge or non-opponent piece - loop { - current = dir(¤t, 1); - if current.is_empty() || !current.intersects(*opponent_board) { - break; + // Expand in direction until edge or non-opponent piece + loop { + current = $dir(¤t, 1); + if current.is_empty() || !current.intersects(*opponent_board) { + break; + } + temp_flips.bitor_assign(current); } - temp_flips |= current; - } - // If terminated on a player piece, keep the flips - if current.intersects(*player_board) { - flip_mask |= temp_flips; - } + // If terminated on a player piece, keep the flips + if current.intersects(*player_board) { + $sum_mask.bitor_assign(temp_flips); + } + }; } + apply_dir!(seed, flip_mask, BitBoard::east); + apply_dir!(seed, flip_mask, BitBoard::west); + apply_dir!(seed, flip_mask, BitBoard::north); + apply_dir!(seed, flip_mask, BitBoard::south); + apply_dir!(seed, flip_mask, BitBoard::northeast); + apply_dir!(seed, flip_mask, BitBoard::northwest); + apply_dir!(seed, flip_mask, BitBoard::southeast); + apply_dir!(seed, flip_mask, BitBoard::southwest); flip_mask }