diff --git a/src/repr/bitboard.rs b/src/repr/bitboard.rs index c548e6c..8fed134 100644 --- a/src/repr/bitboard.rs +++ b/src/repr/bitboard.rs @@ -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 diff --git a/src/repr/board.rs b/src/repr/board.rs index cf95dde..8b552b9 100644 --- a/src/repr/board.rs +++ b/src/repr/board.rs @@ -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 diff --git a/src/repr/coords.rs b/src/repr/coords.rs index 86ef6b0..5b06c21 100644 --- a/src/repr/coords.rs +++ b/src/repr/coords.rs @@ -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 for (CoordAxis, CoordAxis) { fn from(val: CoordPair) -> Self { - val.from_index() + val.into_indexes() } }