use super::board::{Row, GRID, OFFSET};
use super::line::{Dir, Line};
const G: usize = GRID as usize;
const DIAG: usize = 2 * G;
#[derive(Clone)]
pub struct LineIndex {
h: Box<[Row; G]>,
v: Box<[Row; G]>,
dp: Box<[Row; DIAG]>,
dn: Box<[Row; DIAG]>,
}
impl LineIndex {
pub fn new() -> Self {
Self {
h: Box::new([0 as Row; G]),
v: Box::new([0 as Row; G]),
dp: Box::new([0 as Row; DIAG]),
dn: Box::new([0 as Row; DIAG]),
}
}
#[inline]
fn grid(line: &Line) -> (usize, usize) {
(
(line.origin.0 + OFFSET) as usize,
(line.origin.1 + OFFSET) as usize,
)
}
#[inline]
fn slot_mut(&mut self, line: &Line) -> (&mut Row, u8) {
let (gx, gy) = Self::grid(line);
match line.dir {
Dir::H => (&mut self.h[gy], gx as u8),
Dir::V => (&mut self.v[gx], gy as u8),
Dir::DP => (&mut self.dp[gx + gy], gx as u8),
Dir::DN => (&mut self.dn[gx + G - gy], gx as u8),
}
}
#[inline]
fn slot(&self, line: &Line) -> (Row, u8) {
let (gx, gy) = Self::grid(line);
match line.dir {
Dir::H => (self.h[gy], gx as u8),
Dir::V => (self.v[gx], gy as u8),
Dir::DP => (self.dp[gx + gy], gx as u8),
Dir::DN => (self.dn[gx + G - gy], gx as u8),
}
}
pub fn insert(&mut self, line: &Line) {
let (word, pos) = self.slot_mut(line);
*word |= (1 as Row) << pos;
}
pub fn remove(&mut self, line: &Line) {
let (word, pos) = self.slot_mut(line);
*word &= !((1 as Row) << pos);
}
pub fn contains(&self, line: &Line) -> bool {
let (bits, pos) = self.slot(line);
bits & ((1 as Row) << pos) != 0
}
#[inline]
pub fn conflicts(&self, line: &Line, forbid: u8) -> bool {
let (bits, pos) = self.slot(line);
bits & window_mask(pos, forbid) != 0
}
}
impl Default for LineIndex {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for LineIndex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("LineIndex { .. }")
}
}
#[inline]
fn track_pos(line: &Line) -> (i16, i16) {
let (x, y) = line.origin;
match line.dir {
Dir::H => (y, x),
Dir::V => (x, y),
Dir::DP => (x + y, x),
Dir::DN => (x - y, x),
}
}
#[inline]
pub fn lines_conflict(a: &Line, b: &Line, forbid: u8) -> bool {
if a.dir != b.dir {
return false;
}
let (ta, pa) = track_pos(a);
let (tb, pb) = track_pos(b);
ta == tb && (pa - pb).unsigned_abs() <= forbid as u16
}
#[inline]
fn window_mask(pos: u8, forbid: u8) -> Row {
const MAXPOS: u8 = (GRID - 1) as u8;
let lo = pos.saturating_sub(forbid);
let hi = (pos as u16 + forbid as u16).min(MAXPOS as u16) as u8;
let hi_mask = if hi >= MAXPOS {
Row::MAX
} else {
((1 as Row) << (hi + 1)) - 1
};
let lo_mask = if lo == 0 { 0 } else { ((1 as Row) << lo) - 1 };
hi_mask & !lo_mask
}