board backing referencing improvements

This commit is contained in:
Simon Gardling 2025-03-10 21:48:31 -04:00
parent 54f7b8e472
commit 69dc686d0a
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D
3 changed files with 65 additions and 38 deletions

View File

@ -5,7 +5,6 @@ use super::{
}; };
use static_assertions::const_assert; use static_assertions::const_assert;
#[cfg(not(feature = "bitvec"))]
pub type BitBoardInner = u64; pub type BitBoardInner = u64;
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
@ -42,6 +41,10 @@ impl BitBoard {
self.0.count_ones() as usize self.0.count_ones() as usize
} }
pub const fn is_empty(&self) -> bool {
self.0 == 0
}
// Directional shifts with edge masking (prevents wrapping) // Directional shifts with edge masking (prevents wrapping)
pub const fn east(&self) -> Self { pub const fn east(&self) -> Self {
let mask = !Self::col_mask(Board::BOARD_SIZE - 1).0; // Mask to block column 7 bits let mask = !Self::col_mask(Board::BOARD_SIZE - 1).0; // Mask to block column 7 bits

View File

@ -46,6 +46,40 @@ pub enum Winner {
None, None,
} }
macro_rules! get_board {
// Immutable static access
($self:expr, Piece::White) => {
$self.white_board
};
($self:expr, Piece::Black) => {
$self.black_board
};
// Mutable static access
(mut $self:expr, Piece::White) => {
$self.white_board
};
(mut $self:expr, Piece::Black) => {
$self.black_board
};
// Immutable dynamic access
($self:expr, $piece:expr) => {{
match $piece {
Piece::White => &$self.white_board,
Piece::Black => &$self.black_board,
}
}};
// Mutable dynamic access
(mut $self:expr, $piece:expr) => {{
match $piece {
Piece::White => &mut $self.white_board,
Piece::Black => &mut $self.black_board,
}
}};
}
/// Repersents a Othello game board at a certain space /// Repersents a Othello game board at a certain space
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
pub struct Board { pub struct Board {
@ -117,7 +151,7 @@ impl Board {
pub const BOARD_SIZE: CoordAxis = 8; pub const BOARD_SIZE: CoordAxis = 8;
/// Area of the board /// Area of the board
pub const BOARD_AREA: CoordPair = CoordPair(CoordPair(Self::BOARD_SIZE).0.pow(2)); pub const BOARD_AREA: CoordPair = CoordPair::area(Self::BOARD_SIZE);
/// Create a new empty board /// Create a new empty board
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
@ -164,24 +198,8 @@ impl Board {
Self::all_positions().filter(move |&coord| self.would_prop(coord, color)) Self::all_positions().filter(move |&coord| self.would_prop(coord, color))
} }
/// Get a reference to a backing [`BitBoard`]
const fn board(&self, color: Piece) -> &BitBoard {
match color {
Piece::Black => &self.black_board,
Piece::White => &self.white_board,
}
}
/// Get a mutable reference to a backing [`BitBoard`]
const fn board_mut(&mut self, color: Piece) -> &mut BitBoard {
match color {
Piece::Black => &mut self.black_board,
Piece::White => &mut self.white_board,
}
}
pub const fn get_piece(&self, coord: CoordPair, color: Piece) -> bool { pub const fn get_piece(&self, coord: CoordPair, color: Piece) -> bool {
self.board(color).get(coord) get_board!(self, color).get(coord)
} }
/// Returns the color of a place on the [`Board`] at a position /// Returns the color of a place on the [`Board`] at a position
@ -198,13 +216,14 @@ impl Board {
/// Place a piece without checking for propegation of validity /// Place a piece without checking for propegation of validity
/// only pub for setting up benchmark /// only pub for setting up benchmark
pub const fn place_unchecked(&mut self, coord: CoordPair, piece: Piece) { pub const fn place_unchecked(&mut self, coord: CoordPair, piece: Piece) {
self.board_mut(piece).set(coord, true); let is_white = matches!(piece, Piece::White);
self.board_mut(piece.flip()).set(coord, false); get_board!(self, Piece::White).set(coord, is_white);
get_board!(self, Piece::Black).set(coord, !is_white);
} }
const fn delete(&mut self, coord: CoordPair) { const fn delete(&mut self, coord: CoordPair) {
self.board_mut(Piece::White).set(coord, false); get_board!(self, Piece::White).set(coord, false);
self.board_mut(Piece::Black).set(coord, false); get_board!(self, Piece::Black).set(coord, false);
} }
/// Return a modified [`Board`] with the piece placed at a position /// Return a modified [`Board`] with the piece placed at a position
@ -221,7 +240,7 @@ impl Board {
/// 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 fn would_prop(&self, coord: CoordPair, piece: Piece) -> bool {
self.get(coord).is_none() && self.propegate_from_dry(coord, piece).count() > 0 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 fn place(&mut self, coord: CoordPair, piece: Piece) -> Result<(), &'static str> {
@ -230,7 +249,7 @@ impl Board {
} }
self.place_unchecked(coord, piece); self.place_unchecked(coord, piece);
if self.propegate_from(coord) == 0 { if !self.propegate_from(coord) {
self.delete(coord); self.delete(coord);
Err("move would not propegate") Err("move would not propegate")
} else { } else {
@ -239,31 +258,32 @@ impl Board {
} }
/// Propegate the board and captures starting from a specific position /// Propegate the board and captures starting from a specific position
fn propegate_from(&mut self, coord: CoordPair) -> usize { /// returns true if flips occurred
fn propegate_from(&mut self, coord: CoordPair) -> bool {
let Some(starting_color) = self.get(coord) else { let Some(starting_color) = self.get(coord) else {
return 0; return false;
}; };
let flip_mask = self.propegate_from_dry(coord, starting_color); let flip_mask = self.propegate_from_dry(coord, starting_color);
let count = flip_mask.count(); let did_flip = !flip_mask.is_empty();
// Apply the flips // Apply the flips
self.apply_flip_mask(starting_color, flip_mask); self.apply_flip_mask(starting_color, flip_mask);
count did_flip
} }
fn apply_flip_mask(&mut self, color: Piece, flip_mask: BitBoard) { fn apply_flip_mask(&mut self, color: Piece, flip_mask: BitBoard) {
*self.board_mut(color) |= flip_mask; *get_board!(mut self, color) |= flip_mask;
*self.board_mut(color.flip()) &= !flip_mask; *get_board!(mut self, color.flip()) &= !flip_mask;
} }
/// 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 { fn propegate_from_dry(&self, coords: CoordPair, starting_color: Piece) -> BitBoard {
let player_board = *self.board(starting_color); let player_board = get_board!(self, starting_color);
let opponent_board = *self.board(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);
@ -275,14 +295,14 @@ impl Board {
// Expand in direction until edge or non-opponent piece // Expand in direction until edge or non-opponent piece
loop { loop {
current = dir(&current); current = dir(&current);
if current.count() == 0 || !current.intersects(opponent_board) { if current.is_empty() || !current.intersects(*opponent_board) {
break; break;
} }
temp_flips |= current; temp_flips |= 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; flip_mask |= temp_flips;
} }
} }
@ -292,7 +312,7 @@ impl Board {
/// Count the number of a type of [`Piece`] on the board /// Count the number of a type of [`Piece`] on the board
pub const fn count(&self, piece: Piece) -> usize { pub const fn count(&self, piece: Piece) -> usize {
self.board(piece).count() get_board!(self, piece).count()
} }
/// Get the "net score" of a player /// Get the "net score" of a player

View File

@ -20,9 +20,13 @@ impl CoordPair {
Self(row * Board::BOARD_SIZE + col) Self(row * Board::BOARD_SIZE + col)
} }
fn from_index(&self) -> (CoordAxis, CoordAxis) { fn into_indexes(&self) -> (CoordAxis, CoordAxis) {
self.0.div_mod_floor(&Board::BOARD_SIZE) self.0.div_mod_floor(&Board::BOARD_SIZE)
} }
pub const fn area(pos: CoordAxis) -> Self {
Self(pos.pow(2) as CoordPairInner)
}
} }
impl std::fmt::Display for CoordPair { impl std::fmt::Display for CoordPair {
@ -40,7 +44,7 @@ impl std::fmt::Debug for CoordPair {
impl From<CoordPair> for (CoordAxis, CoordAxis) { impl From<CoordPair> for (CoordAxis, CoordAxis) {
fn from(val: CoordPair) -> Self { fn from(val: CoordPair) -> Self {
val.from_index() val.into_indexes()
} }
} }