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)
}
/// 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::*;

View File

@ -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<Self, &'static str> {
pub const fn what_if(&self, coord: CoordPair, piece: Piece) -> Option<Self> {
// 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(&current, 1);
if current.is_empty() || !current.intersects(*opponent_board) {
break;
// Expand in direction until edge or non-opponent piece
loop {
current = $dir(&current, 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
}