use std::collections::HashMap;
use crate::board_layout::BoardLayout;
pub(super) const HEX_NEIGHBORS: [(i16, i16); 6] =
[(1, 0), (-1, 0), (0, 1), (0, -1), (1, -1), (-1, 1)];
pub(super) struct BoardIndex {
pub(super) id_to_xy: HashMap<usize, [f32; 2]>,
pub(super) board_neighbors: HashMap<usize, Vec<usize>>,
pub(super) pitch_mm: f64,
}
impl BoardIndex {
pub(super) fn build(board: &BoardLayout) -> Self {
let mut qr_to_id: HashMap<(i16, i16), usize> = HashMap::new();
let mut id_to_qr: HashMap<usize, (i16, i16)> = HashMap::new();
let mut id_to_xy: HashMap<usize, [f32; 2]> = HashMap::new();
for m in board.markers() {
if let (Some(q), Some(r)) = (m.q, m.r) {
qr_to_id.insert((q, r), m.id);
id_to_qr.insert(m.id, (q, r));
}
id_to_xy.insert(m.id, m.xy_mm);
}
let mut board_neighbors: HashMap<usize, Vec<usize>> = HashMap::new();
for (&id, &(q, r)) in &id_to_qr {
let neighbors = HEX_NEIGHBORS
.iter()
.filter_map(|&(dq, dr)| qr_to_id.get(&(q + dq, r + dr)).copied())
.collect();
board_neighbors.insert(id, neighbors);
}
Self {
id_to_xy,
board_neighbors,
pitch_mm: board.pitch_mm as f64,
}
}
pub(super) fn nearest_within(&self, xy_mm: [f64; 2], tolerance_mm: f64) -> Option<usize> {
let tol2 = tolerance_mm * tolerance_mm;
let mut best_id = None;
let mut best_d2 = tol2;
for (&id, &bxy) in &self.id_to_xy {
let dx = xy_mm[0] - bxy[0] as f64;
let dy = xy_mm[1] - bxy[1] as f64;
let d2 = dx * dx + dy * dy;
if d2 < best_d2 {
best_d2 = d2;
best_id = Some(id);
}
}
best_id
}
pub(super) fn are_neighbors(&self, id_a: usize, id_b: usize) -> bool {
self.board_neighbors
.get(&id_a)
.is_some_and(|nbrs| nbrs.contains(&id_b))
}
pub(super) fn nearest_k_ids(&self, xy_mm: [f64; 2], k: usize) -> Vec<(usize, f64)> {
if k == 0 {
return Vec::new();
}
let mut ranked: Vec<(usize, f64)> = self
.id_to_xy
.iter()
.map(|(&id, &bxy)| {
let dx = xy_mm[0] - f64::from(bxy[0]);
let dy = xy_mm[1] - f64::from(bxy[1]);
(id, dx * dx + dy * dy)
})
.collect();
ranked.sort_by(|(ida, d2a), (idb, d2b)| d2a.total_cmp(d2b).then_with(|| ida.cmp(idb)));
ranked.truncate(k);
ranked
}
}
#[inline]
pub(super) fn dist2(a: [f64; 2], b: [f64; 2]) -> f64 {
let dx = a[0] - b[0];
let dy = a[1] - b[1];
dx * dx + dy * dy
}