board backing referencing improvements
This commit is contained in:
parent
54f7b8e472
commit
69dc686d0a
@ -5,7 +5,6 @@ use super::{
|
||||
};
|
||||
use static_assertions::const_assert;
|
||||
|
||||
#[cfg(not(feature = "bitvec"))]
|
||||
pub type BitBoardInner = u64;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
@ -42,6 +41,10 @@ impl BitBoard {
|
||||
self.0.count_ones() as usize
|
||||
}
|
||||
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
// Directional shifts with edge masking (prevents wrapping)
|
||||
pub const fn east(&self) -> Self {
|
||||
let mask = !Self::col_mask(Board::BOARD_SIZE - 1).0; // Mask to block column 7 bits
|
||||
|
||||
@ -46,6 +46,40 @@ pub enum Winner {
|
||||
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
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub struct Board {
|
||||
@ -117,7 +151,7 @@ impl Board {
|
||||
pub const BOARD_SIZE: CoordAxis = 8;
|
||||
|
||||
/// 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
|
||||
#[allow(clippy::new_without_default)]
|
||||
@ -164,24 +198,8 @@ impl Board {
|
||||
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 {
|
||||
self.board(color).get(coord)
|
||||
get_board!(self, color).get(coord)
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// only pub for setting up benchmark
|
||||
pub const fn place_unchecked(&mut self, coord: CoordPair, piece: Piece) {
|
||||
self.board_mut(piece).set(coord, true);
|
||||
self.board_mut(piece.flip()).set(coord, false);
|
||||
let is_white = matches!(piece, Piece::White);
|
||||
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) {
|
||||
self.board_mut(Piece::White).set(coord, false);
|
||||
self.board_mut(Piece::Black).set(coord, false);
|
||||
get_board!(self, Piece::White).set(coord, false);
|
||||
get_board!(self, Piece::Black).set(coord, false);
|
||||
}
|
||||
|
||||
/// 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
|
||||
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> {
|
||||
@ -230,7 +249,7 @@ impl Board {
|
||||
}
|
||||
|
||||
self.place_unchecked(coord, piece);
|
||||
if self.propegate_from(coord) == 0 {
|
||||
if !self.propegate_from(coord) {
|
||||
self.delete(coord);
|
||||
Err("move would not propegate")
|
||||
} else {
|
||||
@ -239,31 +258,32 @@ impl Board {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
return 0;
|
||||
return false;
|
||||
};
|
||||
|
||||
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
|
||||
self.apply_flip_mask(starting_color, flip_mask);
|
||||
|
||||
count
|
||||
did_flip
|
||||
}
|
||||
|
||||
fn apply_flip_mask(&mut self, color: Piece, flip_mask: BitBoard) {
|
||||
*self.board_mut(color) |= flip_mask;
|
||||
*self.board_mut(color.flip()) &= !flip_mask;
|
||||
*get_board!(mut self, color) |= flip_mask;
|
||||
*get_board!(mut self, color.flip()) &= !flip_mask;
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let player_board = *self.board(starting_color);
|
||||
let opponent_board = *self.board(starting_color.flip());
|
||||
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);
|
||||
@ -275,14 +295,14 @@ impl Board {
|
||||
// Expand in direction until edge or non-opponent piece
|
||||
loop {
|
||||
current = dir(¤t);
|
||||
if current.count() == 0 || !current.intersects(opponent_board) {
|
||||
if current.is_empty() || !current.intersects(*opponent_board) {
|
||||
break;
|
||||
}
|
||||
temp_flips |= current;
|
||||
}
|
||||
|
||||
// If terminated on a player piece, keep the flips
|
||||
if current.intersects(player_board) {
|
||||
if current.intersects(*player_board) {
|
||||
flip_mask |= temp_flips;
|
||||
}
|
||||
}
|
||||
@ -292,7 +312,7 @@ impl Board {
|
||||
|
||||
/// Count the number of a type of [`Piece`] on the board
|
||||
pub const fn count(&self, piece: Piece) -> usize {
|
||||
self.board(piece).count()
|
||||
get_board!(self, piece).count()
|
||||
}
|
||||
|
||||
/// Get the "net score" of a player
|
||||
|
||||
@ -20,9 +20,13 @@ impl CoordPair {
|
||||
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)
|
||||
}
|
||||
|
||||
pub const fn area(pos: CoordAxis) -> Self {
|
||||
Self(pos.pow(2) as CoordPairInner)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CoordPair {
|
||||
@ -40,7 +44,7 @@ impl std::fmt::Debug for CoordPair {
|
||||
|
||||
impl From<CoordPair> for (CoordAxis, CoordAxis) {
|
||||
fn from(val: CoordPair) -> Self {
|
||||
val.from_index()
|
||||
val.into_indexes()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user