use crate::coord::{Coord, Idx, Metric};
use crate::full::FullGrid;
use crate::grid::sealed::Sealed;
use crate::grid::{Grid, same_grid, slot};
use crate::tag::Tag;
use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub struct SubGrid<'a, C: Coord> {
root: &'a FullGrid<C>,
cells: Vec<u32>,
tag: Tag,
}
impl<'a, C: Coord> SubGrid<'a, C> {
pub(crate) fn of(root: &'a FullGrid<C>, cells: impl IntoIterator<Item = Idx>) -> Self {
let root_tag = root.tag();
let mut cells: Vec<u32> = cells
.into_iter()
.map(|i| {
same_grid(root_tag, i);
i.raw()
})
.collect();
cells.sort_unstable();
cells.dedup();
let tag = Tag::of(
core::iter::once((root_tag, u64::from(u32::MAX) + 1))
.chain(cells.iter().map(|&i| (root_tag, u64::from(i)))),
);
Self { root, cells, tag }
}
pub fn root_indices(&self) -> impl Iterator<Item = Idx> + '_ {
let tag = self.root.tag();
self.cells.iter().map(move |&i| Idx::new(tag, i))
}
fn of_cells(&self, at: usize) -> Idx {
Idx::new(self.root.tag(), self.cells[at])
}
}
impl<C: Coord> Sealed for SubGrid<'_, C> {
fn tag(&self) -> Tag {
self.tag
}
}
impl<C: Coord> Grid for SubGrid<'_, C> {
type Cell = C;
fn len(&self) -> usize {
self.cells.len()
}
fn coord(&self, i: Idx) -> C {
let at = slot(self.len(), self.tag, i);
self.root.coord(self.of_cells(at))
}
fn index_of(&self, c: C) -> Option<Idx> {
self.of_root(self.root.index_of(c)?)
}
fn dirs(&self) -> &[C::Dir] {
self.root.dirs()
}
fn step(&self, i: Idx, d: C::Dir) -> Option<Idx> {
let at = slot(self.len(), self.tag, i);
self.of_root(self.root.step(self.of_cells(at), d)?)
}
fn neighbors(&self, i: Idx) -> impl Iterator<Item = (C::Dir, Idx)> {
let at = slot(self.len(), self.tag, i);
self.root
.neighbors(self.of_cells(at))
.filter_map(move |(d, j)| Some((d, self.of_root(j)?)))
}
fn in_neighbors(&self, j: Idx) -> impl Iterator<Item = (C::Dir, Idx)> {
let at = slot(self.len(), self.tag, j);
self.root
.in_neighbors(self.of_cells(at))
.filter_map(move |(d, i)| Some((d, self.of_root(i)?)))
}
fn metric(&self) -> Metric<C> {
self.root.metric()
}
fn root(&self) -> &FullGrid<C> {
self.root
}
fn to_root(&self, i: Idx) -> Idx {
let at = slot(self.len(), self.tag, i);
self.of_cells(at)
}
fn of_root(&self, i: Idx) -> Option<Idx> {
same_grid(self.root.tag(), i);
#[allow(clippy::cast_possible_truncation)]
self.cells
.binary_search(&i.raw())
.ok()
.map(|k| Idx::new(self.tag, k as u32))
}
}
#[cfg(test)]
mod tests {
use crate::coord::{Dir8, Idx, Sq};
use crate::full::{Adjacency, FullGrid};
use crate::grid::Grid;
use crate::path::{Cost, Movement, Step};
use alloc::vec::Vec;
fn corner() -> (FullGrid<Sq>, Vec<Idx>) {
let g = FullGrid::square(8, 8, Adjacency::Four);
let block: Vec<Idx> = g
.indices()
.filter(|&i| g.coord(i).x < 3 && g.coord(i).y < 3)
.collect();
(g, block)
}
fn survey<B: Grid<Cell = Sq>>(b: &B) -> (usize, Vec<Sq>, Vec<usize>, Vec<u32>, bool) {
(
b.len(),
b.cells().collect(),
b.indices().map(|i| b.neighbors(i).count()).collect(),
b.indices()
.map(|i| b.distance(b.at(Sq::new(0, 0)), i))
.collect(),
b.is_connected(|_| true),
)
}
#[test]
fn a_subgrid_answers_every_query_the_same_way_the_grid_it_came_from_does() {
let g = FullGrid::square(5, 4, Adjacency::Eight);
let all = g.subset(g.indices());
assert_eq!(survey(&g), survey(&all));
let (from, to) = (Sq::new(0, 0), Sq::new(4, 3));
assert_eq!(
g.path(g.at(from), g.at(to), &Movement::scan(&g, |_| Some(10)))
.unwrap(),
all.path(
all.at(from),
all.at(to),
&Movement::scan(&all, |_| Some(10))
)
.unwrap(),
);
}
#[test]
fn a_subgrid_maps_every_cell_back_to_the_root_it_came_from() {
let (g, block) = corner();
let sub = g.subset(block.iter().copied());
assert_eq!(sub.len(), 9);
for i in sub.indices() {
assert_eq!(sub.of_root(sub.to_root(i)), Some(i));
assert_eq!(sub.coord(i), g.coord(sub.to_root(i)));
}
let outside = g.at(Sq::new(7, 7));
assert_eq!(sub.of_root(outside), None, "not every root cell is in here");
}
#[test]
fn a_subgrid_numbers_its_cells_in_the_roots_order_whatever_order_it_is_given_them() {
let (g, mut block) = corner();
block.reverse();
block.push(block[4]);
let sub = g.subset(block);
assert_eq!(sub.len(), 9, "the duplicate collapsed");
assert!(
sub.indices()
.map(|i| sub.to_root(i))
.collect::<Vec<_>>()
.windows(2)
.all(|w| w[0] < w[1]),
);
}
#[test]
fn a_step_that_leaves_a_subgrid_is_the_edge_of_the_board() {
let (g, block) = corner();
let sub = g.subset(block);
let rim = sub.at(Sq::new(2, 1));
assert!(sub.step(rim, Dir8::E).is_none(), "east is off this board");
assert!(sub.step(rim, Dir8::W).is_some());
let m: Movement<fn(Step<Sq>) -> Option<Cost>> = Movement::new(|_| Some(10), 10);
let ends: Vec<Sq> = sub
.reachable(sub.at(Sq::new(0, 0)), 1000, &m)
.iter()
.map(|&(i, _)| sub.coord(i))
.collect();
assert_eq!(ends.len(), 9);
assert!(ends.iter().all(|c| c.x < 3 && c.y < 3));
}
#[test]
fn a_subgrid_keeps_the_distances_of_the_grid_it_came_from() {
let g = FullGrid::square(8, 8, Adjacency::Eight);
let ends = g.subset([g.at(Sq::new(0, 0)), g.at(Sq::new(5, 0))]);
assert_eq!(ends.len(), 2);
assert_eq!(
ends.distance(ends.at(Sq::new(0, 0)), ends.at(Sq::new(5, 0))),
5,
"and not 1, which is how far apart they are numbered"
);
}
}