PosMap cleanup

This commit is contained in:
Simon Gardling 2025-02-19 20:59:47 -05:00
parent 8c5fea8359
commit c2120dfcc4
Signed by: titaniumtown
GPG Key ID: 9AB28AC10ECE533D

View File

@ -15,8 +15,10 @@ pub const BOARD_SIZE: usize = 8;
#[allow(dead_code)] #[allow(dead_code)]
pub const BOARD_AREA: usize = BOARD_SIZE * BOARD_SIZE; pub const BOARD_AREA: usize = BOARD_SIZE * BOARD_SIZE;
const BOARD_SIZE_N1: usize = BOARD_SIZE - 1;
/// A chain of positions across the board /// A chain of positions across the board
type Chain = ArrayVec<(usize, usize), BOARD_SIZE>; type Chain = ArrayVec<(usize, usize), BOARD_SIZE_N1>;
/// A collection of chains (up vert, down vert, left horiz, right horiz, diagonals....) /// A collection of chains (up vert, down vert, left horiz, right horiz, diagonals....)
type ChainCollection = ArrayVec<Chain, 8>; type ChainCollection = ArrayVec<Chain, 8>;
@ -24,58 +26,64 @@ type ChainCollection = ArrayVec<Chain, 8>;
/// Map of all points on the board against some type T /// Map of all points on the board against some type T
/// Used to index like so: example[i][j] /// Used to index like so: example[i][j]
/// with each coordinate /// with each coordinate
type PosMap<T> = ArrayVec<ArrayVec<T, BOARD_SIZE>, BOARD_SIZE>; struct PosMap<T>(ArrayVec<T, BOARD_AREA>);
impl<T> PosMap<T> {
pub fn get(&self, row: usize, col: usize) -> &T {
&self.0[row * BOARD_SIZE + col]
}
}
/// Creates a lookup map for adjacencies and chains from each position on the board /// Creates a lookup map for adjacencies and chains from each position on the board
fn gen_adj_lookup() -> PosMap<ChainCollection> { fn gen_adj_lookup() -> PosMap<ChainCollection> {
(0..BOARD_SIZE) PosMap(
.map(|i| { Board::all_positions()
(0..BOARD_SIZE) .map(|(i, j)| {
.map(|j| { let (i_chain, j_chain) = (
let (i_chain, j_chain) = ( split_from(0..=BOARD_SIZE - 1, i),
split_from(0..=BOARD_SIZE - 1, i), split_from(0..=BOARD_SIZE - 1, j),
split_from(0..=BOARD_SIZE - 1, j), );
);
let mut chains: ChainCollection = ArrayVec::new_const(); let mut chains: ChainCollection = ArrayVec::new_const();
chains.extend( chains.extend(
i_chain i_chain
.clone() .clone()
.map(|range| range.map(move |i| (i, j))) .map(|range| range.map(move |i| (i, j)))
.map(Iterator::collect), .map(Iterator::collect),
); );
chains.extend(
j_chain
.clone()
.map(|range| range.map(move |j| (i, j)))
.map(Iterator::collect),
);
// handle diagonals chains.extend(
chains.extend(diag_raw(i_chain, j_chain).map(Iterator::collect)); j_chain
.clone()
.map(|range| range.map(move |j| (i, j)))
.map(Iterator::collect),
);
// make sure all chains are in the proper range so we can ignore bounds checking later // handle diagonals
assert!( chains.extend(diag_raw(i_chain, j_chain).map(Iterator::collect));
chains.iter().all(|x| x.iter().all(
|(i, j)| (0..BOARD_SIZE).contains(i) && (0..BOARD_SIZE).contains(j)
)),
"chains go out-of-bounds"
);
// ensure all nodes in all chains are unique across chains, ensures beavior in
// [`Board::propegate_from`]
let mut uniq = HashSet::new();
assert!(
chains.iter().flatten().all(move |x| uniq.insert(x)),
"there are duplicate nodes in chain"
);
// make sure all chains are in the proper range so we can ignore bounds checking later
assert!(
chains chains
}) .iter()
.collect() .flatten()
}) .all(|(i, j)| (0..BOARD_SIZE).contains(i) && (0..BOARD_SIZE).contains(j)),
.collect() "chains go out-of-bounds"
);
// ensure all nodes in all chains are unique across chains, ensures beavior in
// [`Board::propegate_from`]
let mut uniq = HashSet::new();
assert!(
chains.iter().flatten().all(move |x| uniq.insert(x)),
"there are duplicate nodes in chain"
);
chains
})
.collect(),
)
} }
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
@ -87,7 +95,7 @@ pub enum Winner {
lazy_static! { lazy_static! {
/// Precompute all possible chains for each position on the board /// Precompute all possible chains for each position on the board
pub static ref ADJ_LOOKUP: PosMap<ChainCollection> = gen_adj_lookup(); static ref ADJ_LOOKUP: PosMap<ChainCollection> = gen_adj_lookup();
} }
/// Repersents a Othello game board at a certain space /// Repersents a Othello game board at a certain space
@ -254,8 +262,7 @@ impl Board {
return Err("position is occupied"); return Err("position is occupied");
} }
self.place_unchecked(i, j, piece); self.place_unchecked(i, j, piece);
let captured = self.propegate_from(i, j); if self.propegate_from(i, j) > 0 {
if captured > 0 {
Ok(()) Ok(())
} else { } else {
self.delete(i, j); self.delete(i, j);
@ -273,6 +280,7 @@ impl Board {
let iterator = unsafe { let iterator = unsafe {
// SAFETY! `propegate_from_dry` should not have overlapping chains // SAFETY! `propegate_from_dry` should not have overlapping chains
// if overlapping chains were to exist, `self.place_unchecked` could collide with `self.get` // if overlapping chains were to exist, `self.place_unchecked` could collide with `self.get`
// I now have a check in `ADJ_LOOKUP` on creation
(*(self as *const Self)).propegate_from_dry(i, j, starting_color) (*(self as *const Self)).propegate_from_dry(i, j, starting_color)
}; };
@ -294,7 +302,8 @@ impl Board {
j: usize, j: usize,
starting_color: Piece, starting_color: Piece,
) -> impl Iterator<Item = &(usize, usize)> + use<'_> { ) -> impl Iterator<Item = &(usize, usize)> + use<'_> {
ADJ_LOOKUP[i][j] ADJ_LOOKUP
.get(i, j)
.iter() .iter()
.filter_map(move |chain| { .filter_map(move |chain| {
let mut end_idx = None; let mut end_idx = None;