use std::sync::atomic::{AtomicBool, Ordering};
pub type Pos = (i16, i16);
pub type Row = u64;
pub const GRID: i16 = Row::BITS as i16;
const N: usize = GRID as usize;
pub(crate) const OFFSET: i16 = GRID / 2 - 5;
const MARGIN: i16 = 4;
pub static GRID_OVERFLOW: AtomicBool = AtomicBool::new(false);
#[derive(Debug, Clone)]
pub struct Board {
pub cells: Vec<Pos>,
grid: Box<[Row; N]>,
}
impl Default for Board {
fn default() -> Self {
Self {
cells: Vec::new(),
grid: Box::new([0 as Row; N]),
}
}
}
impl Board {
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn contains(&self, pos: Pos) -> bool {
let gx = (pos.0 + OFFSET) as u32;
let gy = (pos.1 + OFFSET) as usize;
(self.grid[gy] >> gx) & 1 != 0
}
#[inline]
pub(crate) fn row(&self, gy: usize) -> Row {
self.grid[gy]
}
pub fn insert(&mut self, pos: Pos) -> bool {
let (gx, gy) = (pos.0 + OFFSET, pos.1 + OFFSET);
if !(MARGIN..GRID - MARGIN).contains(&gx) || !(MARGIN..GRID - MARGIN).contains(&gy) {
GRID_OVERFLOW.store(true, Ordering::Relaxed);
return false;
}
self.grid[gy as usize] |= (1 as Row) << (gx as u32);
self.cells.push(pos);
true
}
pub fn remove(&mut self, pos: Pos) {
let gx = (pos.0 + OFFSET) as u32;
let gy = (pos.1 + OFFSET) as usize;
self.grid[gy] &= !((1 as Row) << gx);
let popped = self.cells.pop();
debug_assert_eq!(popped, Some(pos), "board remove out of order");
}
pub fn len(&self) -> usize {
self.cells.len()
}
pub fn is_empty(&self) -> bool {
self.cells.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn insert_contains_remove_with_negative_coords() {
let mut b = Board::new();
let lo = MARGIN - OFFSET; let hi = GRID - MARGIN - 1 - OFFSET; let pts = [
(0i16, 0i16),
(lo, hi),
(hi, lo),
(lo, lo),
(hi, hi),
(-3, 7),
];
for &p in &pts {
assert!(!b.contains(p));
assert!(b.insert(p));
assert!(b.contains(p));
}
assert_eq!(b.len(), pts.len());
for &p in pts.iter().rev() {
assert!(b.contains(p));
b.remove(p);
assert!(!b.contains(p));
}
assert!(b.is_empty());
}
#[test]
fn insert_past_margin_signals_overflow_without_panicking() {
GRID_OVERFLOW.store(false, Ordering::Relaxed);
let mut b = Board::new();
assert!(!b.insert((10_000, 0)));
assert!(GRID_OVERFLOW.load(Ordering::Relaxed));
assert!(b.is_empty());
GRID_OVERFLOW.store(false, Ordering::Relaxed);
}
}