add neighbour and cardinal iterators to grid

This commit is contained in:
2025-12-04 15:49:37 -08:00
parent 2d9abb841b
commit faa452149a
2 changed files with 127 additions and 26 deletions

View File

@@ -7,6 +7,21 @@ use std::{
str::FromStr,
};
/// NW, N, NE, W, E, SW, S, SE
const ADJACENT_OFFSETS: [&(i64, i64); 8] = [
&(-1, -1),
&(0, -1),
&(1, -1),
&(-1, 0),
&(1, 0),
&(-1, 1),
&(0, 1),
&(1, 1),
];
/// NESW
const CARDINAL_OFFSETS: [&(i64, i64); 4] = [&(0, -1), &(-1, 0), &(1, 0), &(0, 1)];
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Coord2d {
pub x: i64,
@@ -29,7 +44,10 @@ impl<T: AsCoord2d> Sub<T> for &Coord2d {
}
}
impl<T: AsCoord2d> Add<T> for &Coord2d {
impl<T: AsCoord2d> Add<T> for &Coord2d
where
T: Copy,
{
type Output = Coord2d;
fn add(self, rhs: T) -> Self::Output {
Coord2d {
@@ -39,7 +57,7 @@ impl<T: AsCoord2d> Add<T> for &Coord2d {
}
}
impl<T: AsCoord2d> Add<&T> for Coord2d {
impl<T: AsCoord2d + ?Sized> Add<&T> for Coord2d {
type Output = Coord2d;
fn add(self, rhs: &T) -> Self::Output {
Coord2d {
@@ -150,7 +168,7 @@ impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for CoordIter<'a, T> {
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.grid.data.len() {
self.pos += 1;
self.grid.coord(self.pos as i64 - 1).into()
self.grid.coord(self.pos as i64 - 1)
} else {
None
}
@@ -163,6 +181,7 @@ pub struct ItemIter<'a, T> {
grid: &'a Grid<T>,
}
#[derive(Debug, PartialEq, Eq)]
pub struct Item<'a, T> {
pub pos: Coord2d,
pub value: &'a T,
@@ -216,6 +235,31 @@ impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for GridColIter<'a, T> {
}
}
#[derive(Debug)]
pub struct OffsetsIter<'a, T> {
grid: &'a Grid<T>,
origin: Coord2d,
cur: usize,
offsets: &'a [&'a (i64, i64)],
}
impl<'a, T: Clone + Eq + PartialEq + Debug> Iterator for OffsetsIter<'a, T> {
type Item = Item<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
while self.cur < self.offsets.len() {
let pos = self.origin + self.offsets[self.cur];
self.cur += 1;
if let Some(value) = self.grid.get(&pos) {
return Some(Item { pos, value });
}
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.offsets.len() - self.cur))
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Grid<T> {
pub data: Vec<T>,
@@ -266,7 +310,7 @@ impl<T: Clone + Eq + PartialEq + Debug> Grid<T> {
if c.y() < 0 || c.y() as usize >= self.height() {
return false;
}
return true;
true
}
fn valid_pos<C: AsCoord2d>(&self, c: &C) -> Option<usize> {
if c.x() < 0 || c.x() >= self.width {
@@ -331,11 +375,7 @@ impl<T: Clone + Eq + PartialEq + Debug> Grid<T> {
}
pub fn col(&self, x: i64) -> Option<Vec<&T>> {
if let Some(iter) = self.col_iter(x) {
Some(iter.collect())
} else {
None
}
self.col_iter(x).map(|iter| iter.collect())
}
pub fn col_iter<'a>(&'a self, x: i64) -> Option<GridColIter<'a, T>> {
@@ -390,27 +430,37 @@ impl<T: Clone + Eq + PartialEq + Debug> Grid<T> {
}
/// Return the count of neighbours (8 directions) matching predicate p
pub fn neighbours_count<C: AsCoord2d, P>(&self, c: C, mut p: P) -> usize
pub fn neighbours_count<C: AsCoord2d + Copy, P>(&self, c: &C, mut p: P) -> usize
where
P: FnMut(&T) -> bool,
{
const DIRECTIONS: [(i64, i64); 8] = [
(-1, -1),
(0, -1),
(1, -1),
(-1, 0),
(1, 0),
(1, 1),
(0, 1),
(-1, 1),
];
DIRECTIONS
ADJACENT_OFFSETS
.iter()
.map(|d| (c.x() + d.0, c.y() + d.1))
.filter(|c| self.get(c).is_some_and(|x| p(x)))
.map(|d| c.to_coord() + *d)
.filter(|c| self.get(c).is_some_and(&mut p))
.count()
}
/// Return an iterator over the 8 neighbours of c. The iterator skips neighbouring positions outside of the grid.
pub fn neighbours_iter<C: AsCoord2d + Copy>(&self, c: &C) -> OffsetsIter<T> {
OffsetsIter {
grid: self,
origin: c.to_coord(),
cur: 0,
offsets: &ADJACENT_OFFSETS,
}
}
/// Return an iterator over the 4 cardinal neighbours of c. The iterator skips neighbouring positions outside of the grid.
pub fn cardinal_iter<C: AsCoord2d + Copy>(&self, c: &C) -> OffsetsIter<T> {
OffsetsIter {
grid: self,
origin: c.to_coord(),
cur: 0,
offsets: &CARDINAL_OFFSETS,
}
}
// fn window_compare_impl<const REV: bool>(&self, needle: &[T]) -> Vec<(i64, i64)> {
// if (self.width as usize) < needle.len() {
// return Vec::new();
@@ -582,6 +632,58 @@ FBCG";
)
}
#[test]
fn neighbours_iter() {
let grid = unchecked_load();
assert_eq!(
grid.neighbours_iter(&(0, 1)).collect::<Vec<_>>(),
[
Item {
pos: Coord2d { x: 0, y: 0 },
value: &b'A'
},
Item {
pos: Coord2d { x: 1, y: 0 },
value: &b'B'
},
Item {
pos: Coord2d { x: 1, y: 1 },
value: &b'F'
},
Item {
pos: Coord2d { x: 0, y: 2 },
value: &b'I'
},
Item {
pos: Coord2d { x: 1, y: 2 },
value: &b'J'
},
]
)
}
#[test]
fn cardinal_iter() {
let grid = unchecked_load();
assert_eq!(
grid.cardinal_iter(&(0, 1)).collect::<Vec<_>>(),
[
Item {
pos: Coord2d { x: 0, y: 0 },
value: &b'A'
},
Item {
pos: Coord2d { x: 1, y: 1 },
value: &b'F'
},
Item {
pos: Coord2d { x: 0, y: 2 },
value: &b'I'
}
]
)
}
// #[test]
// fn window_compare() {
// let grid = unchecked_load();