board optimizations

This commit is contained in:
Simon Gardling 2025-03-12 10:28:32 -04:00
parent 7863e0324f
commit 16eb6a1259
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
2 changed files with 47 additions and 51 deletions

View File

@ -79,18 +79,6 @@ impl BitBoard {
self.south(n).west(n) 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) // Mask for a specific column (e.g., col_mask(7) = 0x8080808080808080)
const fn col_mask(col: CoordAxis) -> Self { const fn col_mask(col: CoordAxis) -> Self {
let mut mask = 0; let mut mask = 0;
@ -110,12 +98,16 @@ impl BitBoard {
pub const fn intersects(self, other: Self) -> bool { pub const fn intersects(self, other: Self) -> bool {
(self.0 & other.0) > 0 (self.0 & other.0) > 0
} }
pub const fn bitor_assign(&mut self, other: Self) {
self.0 = self.0 | other.0;
} }
impl std::ops::Not for BitBoard { pub const fn bitand_assign(&mut self, other: Self) {
type Output = BitBoard; self.0 = self.0 & other.0;
}
fn not(self) -> Self::Output { pub const fn not(self) -> Self {
Self(!self.0) 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)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -227,22 +227,26 @@ impl Board {
/// Return a modified [`Board`] with the piece placed at a position /// Return a modified [`Board`] with the piece placed at a position
/// Returns None if the move was invalid /// Returns None if the move was invalid
pub fn what_if(&self, coord: CoordPair, piece: Piece) -> Result<Self, &'static str> { pub const fn what_if(&self, coord: CoordPair, piece: Piece) -> Option<Self> {
// extract check here to avoid copy // extract check here to avoid copy
if self.get(coord).is_some() { if self.get(coord).is_some() {
return Err("position is occupied"); return None;
} }
let mut self_copy = *self; 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 /// 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() 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() { if self.get(coord).is_some() {
return Err("position is occupied"); return Err("position is occupied");
} }
@ -257,7 +261,7 @@ impl Board {
/// Propegate the board and captures starting from a specific position /// Propegate the board and captures starting from a specific position
/// returns true if flips occurred /// 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 flip_mask = self.propegate_from_dry(coord, starting_color);
let did_flip = !flip_mask.is_empty(); let did_flip = !flip_mask.is_empty();
@ -267,40 +271,52 @@ impl Board {
did_flip 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).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) /// Propegate piece captures originating from (i, j)
/// DO NOT USE THIS ALONE, this should be called as a part of /// DO NOT USE THIS ALONE, this should be called as a part of
/// [`Board::place`] or [`Board::place_and_prop_unchecked`] /// [`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 player_board = get_board!(self, starting_color);
let opponent_board = get_board!(self, starting_color.flip()); let opponent_board = get_board!(self, starting_color.flip());
let mut flip_mask = BitBoard::new(); let mut flip_mask = BitBoard::new();
let seed = BitBoard::from_coord(coords); let seed = BitBoard::from_coord(coords);
for dir in BitBoard::DIRECTIONS { macro_rules! apply_dir {
let mut current = seed; ($base:expr, $sum_mask:expr, $dir:expr) => {
let mut current = $base;
let mut temp_flips = BitBoard::new(); let mut temp_flips = BitBoard::new();
// Expand in direction until edge or non-opponent piece // Expand in direction until edge or non-opponent piece
loop { loop {
current = dir(&current, 1); current = $dir(&current, 1);
if current.is_empty() || !current.intersects(*opponent_board) { if current.is_empty() || !current.intersects(*opponent_board) {
break; break;
} }
temp_flips |= current; temp_flips.bitor_assign(current);
} }
// If terminated on a player piece, keep the flips // If terminated on a player piece, keep the flips
if current.intersects(*player_board) { if current.intersects(*player_board) {
flip_mask |= temp_flips; $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 flip_mask
} }