1use std::ops::Range;
2
3pub struct Around<'a> {
8 y: usize,
9 x: usize,
10 y_range: Range<usize>,
11 x_range: Range<usize>,
12 directions: &'a [(isize, isize)],
13}
14
15pub fn around<'a>(y: usize, x: usize) -> Around<'a> {
40 Around {
41 y,
42 x,
43 y_range: 0..usize::MAX,
44 x_range: 0..usize::MAX,
45 directions: &[],
46 }
47}
48
49impl<'a> Around<'a> {
50 pub fn y_range(self, r: Range<usize>) -> Self {
52 Self { y_range: r, ..self }
53 }
54 pub fn x_range(self, r: Range<usize>) -> Self {
56 Self { x_range: r, ..self }
57 }
58 pub fn directions(self, dirs: &'a [(isize, isize)]) -> Self {
60 Self {
61 directions: dirs,
62 ..self
63 }
64 }
65}
66
67impl<'a> Iterator for Around<'a> {
68 type Item = (usize, usize);
69 fn next(&mut self) -> Option<Self::Item> {
70 while let Some((&(dy, dx), rest)) = self.directions.split_first() {
71 self.directions = rest;
72 match (self.y.checked_add_signed(dy), self.x.checked_add_signed(dx)) {
73 (Some(ny), Some(nx))
74 if self.y_range.contains(&self.y)
75 && self.x_range.contains(&self.x)
76 && self.y_range.contains(&ny)
77 && self.x_range.contains(&nx) =>
78 {
79 return Some((ny, nx));
80 }
81 _ => {}
82 }
83 }
84 None
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 const NSEW: [(isize, isize); 4] = [(-1, 0), (1, 0), (0, 1), (0, -1)];
92
93 #[test]
94 fn simple() {
95 let mut a = around(5, 5).y_range(0..10).x_range(0..10).directions(&NSEW);
96 assert_eq!(a.next(), Some((4, 5)));
97 assert_eq!(a.next(), Some((6, 5)));
98 assert_eq!(a.next(), Some((5, 6)));
99 assert_eq!(a.next(), Some((5, 4)));
100 assert_eq!(a.next(), None);
101 }
102
103 #[test]
104 fn out_of_bounds_dest() {
105 let mut a = around(1, 1).y_range(1..10).x_range(1..10).directions(&NSEW);
106 assert_eq!(a.next(), Some((2, 1)));
107 assert_eq!(a.next(), Some((1, 2)));
108 assert_eq!(a.next(), None); }
110
111 #[test]
112 fn out_of_bounds_source() {
113 let mut a = around(9, 10)
114 .y_range(0..10)
115 .x_range(0..10)
116 .directions(&NSEW);
117 assert_eq!(a.next(), None); }
119
120 #[test]
121 fn no_directions() {
122 let mut a = around(5, 5).y_range(0..10).x_range(0..10);
123 assert_eq!(a.next(), None);
124 }
125}