screeps_utils/
room_xy.rs

1use std::iter::FusedIterator;
2
3use screeps::{RoomCoordinate, RoomXY};
4
5use crate::room_coordinate::{range_exclusive, range_inclusive};
6
7// An iterator over ordered pairs of RoomCoordinates; first coordinate is the
8// major axis.
9#[derive(Debug, Clone)]
10struct PairIter {
11    // SAFETY INVARIANT: forward.1 and backward.1 are within b_min..=b_max when !done
12    b_min: RoomCoordinate,
13    b_max: RoomCoordinate,
14    // SAFETY INVARIANT: forward <= backward, by lex order, when !done
15    forward: (RoomCoordinate, RoomCoordinate),
16    backward: (RoomCoordinate, RoomCoordinate),
17    done: bool,
18}
19
20impl PairIter {
21    fn new(min: (RoomCoordinate, RoomCoordinate), max: (RoomCoordinate, RoomCoordinate)) -> Self {
22        Self {
23            b_min: min.1,
24            b_max: max.1,
25            forward: min,
26            backward: max,
27            // SAFETY INVARIANT: if this is false, then the b_min/b_max criterion is true,
28            // and max (backward) is lex-order >= min (forward).
29            done: max.0 < min.0 || max.1 < min.1,
30        }
31    }
32}
33
34impl Iterator for PairIter {
35    type Item = (RoomCoordinate, RoomCoordinate);
36
37    fn next(&mut self) -> Option<Self::Item> {
38        if self.done {
39            return None;
40        }
41
42        let res = Some(self.forward);
43
44        if self.forward == self.backward {
45            self.done = true;
46        } else if self.forward.1 == self.b_max {
47            // SAFETY: self.backward.1 <= self.b_max, so self.forward.0 < self.backward.0,
48            // meaning we can increment by 1.
49            self.forward = (
50                unsafe { RoomCoordinate::unchecked_new(self.forward.0.u8() + 1) },
51                self.b_min,
52            );
53        } else {
54            // SAFETY: self.forward.1 < self.b_max, so we can step up by 1.
55            self.forward.1 = unsafe { RoomCoordinate::unchecked_new(self.forward.1.u8() + 1) };
56        }
57
58        res
59    }
60
61    fn size_hint(&self) -> (usize, Option<usize>) {
62        let len = self.len();
63        (len, Some(len))
64    }
65
66    fn fold<B, F>(self, init: B, mut f: F) -> B
67    where
68        Self: Sized,
69        F: FnMut(B, Self::Item) -> B,
70    {
71        if self.done {
72            return init;
73        }
74
75        #[cold]
76        fn cold_call<B>(
77            this: PairIter,
78            init: B,
79            mut f: impl FnMut(B, <PairIter as Iterator>::Item) -> B,
80        ) -> B {
81            if this.forward.0 == this.backward.0 {
82                return range_inclusive(this.forward.1, this.backward.1)
83                    .map(|b| (this.forward.0, b))
84                    .fold(init, f);
85            }
86
87            let forward_partial_acc = range_inclusive(this.forward.1, this.b_max)
88                .map(|b| (this.forward.0, b))
89                .fold(init, &mut f);
90
91            let middle_partials_acc = range_exclusive(this.forward.0, this.backward.0).fold(
92                forward_partial_acc,
93                |inner_acc, a| {
94                    range_inclusive(this.b_min, this.b_max)
95                        .map(|b| (a, b))
96                        .fold(inner_acc, &mut f)
97                },
98            );
99
100            range_inclusive(this.b_min, this.backward.1)
101                .map(|b| (this.backward.0, b))
102                .fold(middle_partials_acc, f)
103        }
104
105        if self.forward.1 == self.b_min && self.backward.1 == self.b_max {
106            range_inclusive(self.forward.0, self.backward.0).fold(init, |acc, a| {
107                range_inclusive(self.b_min, self.b_max)
108                    .map(|b| (a, b))
109                    .fold(acc, &mut f)
110            })
111        } else {
112            cold_call(self, init, f)
113        }
114    }
115}
116
117impl FusedIterator for PairIter {}
118
119impl ExactSizeIterator for PairIter {
120    fn len(&self) -> usize {
121        if self.done {
122            return 0;
123        }
124
125        let forward = (self.forward.0.u8(), self.forward.1.u8());
126        let backward = (self.backward.0.u8(), self.backward.1.u8());
127
128        if forward.0 == backward.0 {
129            return (backward.1 - forward.1 + 1) as usize;
130        }
131
132        let full_ranges = (backward.0 - forward.0 - 1) as usize;
133        full_ranges * (self.b_max.u8() - self.b_min.u8() + 1) as usize
134            + (self.b_max.u8() - forward.1 + 1) as usize
135            + (backward.1 - self.b_min.u8() + 1) as usize
136    }
137}
138
139impl DoubleEndedIterator for PairIter {
140    fn next_back(&mut self) -> Option<Self::Item> {
141        if self.done {
142            return None;
143        }
144
145        let res = Some(self.backward);
146
147        if self.backward == self.forward {
148            self.done = true;
149        } else if self.backward.1 == self.b_min {
150            // SAFETY: self.forward.1 >= self.b_min, so self.forward.0 < self.backward.0,
151            // meaning we can decrement by 1.
152            self.backward = (
153                unsafe { RoomCoordinate::unchecked_new(self.backward.0.u8() - 1) },
154                self.b_max,
155            );
156        } else {
157            // SAFETY: self.backward.1 > self.b_min, so we can step down by 1.
158            self.backward.1 = unsafe { RoomCoordinate::unchecked_new(self.backward.1.u8() - 1) };
159        }
160
161        res
162    }
163
164    fn rfold<B, F>(self, init: B, mut f: F) -> B
165    where
166        Self: Sized,
167        F: FnMut(B, Self::Item) -> B,
168    {
169        if self.done {
170            return init;
171        }
172
173        #[cold]
174        fn cold_call<B>(
175            this: PairIter,
176            init: B,
177            mut f: impl FnMut(B, <PairIter as Iterator>::Item) -> B,
178        ) -> B {
179            if this.forward.0 == this.backward.0 {
180                return range_inclusive(this.forward.1, this.backward.1)
181                    .map(|b| (this.forward.0, b))
182                    .rfold(init, f);
183            }
184
185            let backward_partial_acc = range_inclusive(this.b_min, this.backward.1)
186                .map(|b| (this.backward.0, b))
187                .rfold(init, &mut f);
188
189            let middle_partials_acc = range_exclusive(this.forward.0, this.backward.0).rfold(
190                backward_partial_acc,
191                |inner_acc, a| {
192                    range_inclusive(this.b_min, this.b_max)
193                        .map(|b| (a, b))
194                        .rfold(inner_acc, &mut f)
195                },
196            );
197
198            range_inclusive(this.forward.1, this.b_max)
199                .map(|b| (this.forward.0, b))
200                .rfold(middle_partials_acc, f)
201        }
202
203        if self.forward.1 == self.b_min && self.backward.1 == self.b_max {
204            range_inclusive(self.forward.0, self.backward.0).rfold(init, |acc, a| {
205                range_inclusive(self.b_min, self.b_max)
206                    .map(|b| (a, b))
207                    .rfold(acc, &mut f)
208            })
209        } else {
210            cold_call(self, init, f)
211        }
212    }
213}
214
215/// An enum for controlling the iteration order of a [`GridIter`]. Thinking of a
216/// [`GridIter`] as a nested for-loop, `XMajor` would correspond to
217///
218/// ```
219/// for x in 0..=10 {
220///     for y in 0..=10 {
221///         // Do things
222///     }
223/// }
224/// ```
225///
226/// whereas `YMajor` would coorespond to
227///
228/// ```
229/// for y in 0..=10 {
230///     for x in 0..=10 {
231///         // Do things
232///     }
233/// }
234/// ```
235#[derive(Clone, Copy, PartialEq, Eq, Debug)]
236pub enum Order {
237    XMajor,
238    YMajor,
239}
240
241/// An iterator over a grid of [`RoomXY`], inclusive of the boundary edges.
242#[derive(Debug, Clone)]
243pub struct GridIter {
244    inner: PairIter,
245    order: Order,
246}
247
248impl GridIter {
249    /// Creates a `GridIter` over the rectangular grid of `RoomXY` specified by
250    /// the top-left and bottom-right corners provided. Will determine
251    /// whether to iterate `x` or `y` first using the passed-in [`Order`].
252    ///
253    /// It is safe to pass in invalid corner specifications (e.g. `top_left.x >
254    /// bottom_right.x`), the returned `GridIter` will be immediately
255    /// completed.
256    ///
257    /// # Example
258    ///
259    /// ```
260    /// use screeps::local::{RoomCoordinate, RoomXY};
261    /// use screeps_utils::room_xy::{GridIter, Order};
262    ///
263    /// for xy in GridIter::new(
264    ///     RoomXY {
265    ///         x: RoomCoordinate::new(0).unwrap(),
266    ///         y: RoomCoordinate::new(0).unwrap(),
267    ///     },
268    ///     RoomXY {
269    ///         x: RoomCoordinate::new(1).unwrap(),
270    ///         y: RoomCoordinate::new(2).unwrap(),
271    ///     },
272    ///     Order::XMajor,
273    /// ) {
274    ///     // Will print (x: 0, y: 0), then (x: 0, y: 1), (x: 0, y: 2), (x: 1, y: 0), etc.
275    ///     println!("{:?}", xy);
276    /// }
277    /// ```
278    pub fn new(top_left: RoomXY, bottom_right: RoomXY, order: Order) -> Self {
279        let top = top_left.y;
280        let bottom = bottom_right.y;
281        let left = top_left.x;
282        let right = bottom_right.x;
283        let (a_min, a_max, b_min, b_max) = match order {
284            Order::XMajor => (left, right, top, bottom),
285            Order::YMajor => (top, bottom, left, right),
286        };
287        Self {
288            inner: PairIter::new((a_min, b_min), (a_max, b_max)),
289            order,
290        }
291    }
292
293    fn get_xy(&self, a: RoomCoordinate, b: RoomCoordinate) -> RoomXY {
294        match self.order {
295            Order::XMajor => RoomXY { x: a, y: b },
296            Order::YMajor => RoomXY { x: b, y: a },
297        }
298    }
299}
300
301impl Iterator for GridIter {
302    type Item = RoomXY;
303
304    fn next(&mut self) -> Option<Self::Item> {
305        self.inner.next().map(|(a, b)| self.get_xy(a, b))
306    }
307
308    fn size_hint(&self) -> (usize, Option<usize>) {
309        self.inner.size_hint()
310    }
311
312    fn fold<B, F>(self, init: B, mut f: F) -> B
313    where
314        Self: Sized,
315        F: FnMut(B, Self::Item) -> B,
316    {
317        match self.order {
318            Order::XMajor => self
319                .inner
320                .fold(init, move |acc, (x, y)| f(acc, RoomXY { x, y })),
321            Order::YMajor => self
322                .inner
323                .fold(init, move |acc, (y, x)| f(acc, RoomXY { x, y })),
324        }
325    }
326}
327
328impl FusedIterator for GridIter {}
329
330impl ExactSizeIterator for GridIter {
331    fn len(&self) -> usize {
332        self.inner.len()
333    }
334}
335
336impl DoubleEndedIterator for GridIter {
337    fn next_back(&mut self) -> Option<Self::Item> {
338        self.inner.next_back().map(|(a, b)| self.get_xy(a, b))
339    }
340
341    fn rfold<B, F>(self, init: B, mut f: F) -> B
342    where
343        Self: Sized,
344        F: FnMut(B, Self::Item) -> B,
345    {
346        match self.order {
347            Order::XMajor => self
348                .inner
349                .rfold(init, move |acc, (x, y)| f(acc, RoomXY { x, y })),
350            Order::YMajor => self
351                .inner
352                .rfold(init, move |acc, (y, x)| f(acc, RoomXY { x, y })),
353        }
354    }
355}
356
357/// Creates an iterator over all [`RoomXY`] around the designated centre
358/// (including the centre) within the given [Chebyshev distance](https://en.wikipedia.org/wiki/Chebyshev_distance).
359///
360/// This is the same distance measure used for attack ranges, or for road
361/// lengths between two points, etc.
362///
363/// # Iteration order
364///
365/// The order over which points are iterated within the range is unspecified,
366/// and may change at any time.
367pub fn chebyshev_range_iter(centre: RoomXY, radius: u8) -> impl Iterator<Item = RoomXY> {
368    let signed_radius = radius.min(50) as i8;
369    let top_left = RoomXY {
370        x: centre.x.saturating_add(-signed_radius),
371        y: centre.y.saturating_add(-signed_radius),
372    };
373    let bottom_right = RoomXY {
374        x: centre.x.saturating_add(signed_radius),
375        y: centre.y.saturating_add(signed_radius),
376    };
377    GridIter::new(top_left, bottom_right, Order::YMajor)
378}
379
380/// Creates an iterator over all [`RoomXY`] around the designated centre
381/// (including the centre) within the given [Manhattan distance](https://en.wikipedia.org/wiki/Taxicab_geometry).
382///
383/// This would be used for, e.g., measuring the number of walls needed between 2
384/// points.
385///
386/// # Iteration order
387///
388/// The order over which points are iterated within the range is unspecified,
389/// and may change at any time.
390pub fn manhattan_range_iter(centre: RoomXY, radius: u8) -> impl Iterator<Item = RoomXY> {
391    let signed_radius = radius.min(100) as i8;
392    let min_x = centre.x.saturating_add(-signed_radius);
393    let min_x_offset = min_x.u8() as i8 - centre.x.u8() as i8;
394    let max_x = centre.x.saturating_add(signed_radius);
395    let max_x_offset = max_x.u8() as i8 - centre.x.u8() as i8;
396    range_inclusive(min_x, max_x)
397        .zip(min_x_offset..=max_x_offset)
398        .flat_map(move |(x, x_offset)| {
399            let y_radius = signed_radius - x_offset.abs();
400            let min_y = centre.y.saturating_add(-y_radius);
401            let max_y = centre.y.saturating_add(y_radius);
402            range_inclusive(min_y, max_y).map(move |y| RoomXY { x, y })
403        })
404}
405
406#[cfg(test)]
407mod test {
408    use super::*;
409
410    use std::collections::HashSet;
411
412    fn make_xy(x: u8, y: u8) -> RoomXY {
413        RoomXY {
414            x: RoomCoordinate::new(x).unwrap(),
415            y: RoomCoordinate::new(y).unwrap(),
416        }
417    }
418
419    #[test]
420    fn test_chebyshev_basic() {
421        let expected: HashSet<_> = [
422            make_xy(10, 10),
423            make_xy(9, 10),
424            make_xy(9, 9),
425            make_xy(9, 11),
426            make_xy(10, 9),
427            make_xy(10, 11),
428            make_xy(11, 9),
429            make_xy(11, 10),
430            make_xy(11, 11),
431        ]
432        .into_iter()
433        .collect();
434        let actual: HashSet<_> = chebyshev_range_iter(make_xy(10, 10), 1).collect();
435        assert_eq!(expected, actual);
436    }
437
438    #[test]
439    fn test_chebyshev_0_radius() {
440        let expected: HashSet<_> = [make_xy(11, 11)].into_iter().collect();
441        let actual: HashSet<_> = chebyshev_range_iter(make_xy(11, 11), 0).collect();
442        assert_eq!(expected, actual);
443    }
444
445    #[test]
446    fn test_chebyshev_boundary() {
447        let expected: HashSet<_> = [
448            make_xy(0, 0),
449            make_xy(0, 1),
450            make_xy(0, 2),
451            make_xy(1, 0),
452            make_xy(1, 1),
453            make_xy(1, 2),
454            make_xy(2, 0),
455            make_xy(2, 1),
456            make_xy(2, 2),
457        ]
458        .into_iter()
459        .collect();
460        let actual: HashSet<_> = chebyshev_range_iter(make_xy(0, 0), 2).collect();
461        assert_eq!(expected, actual);
462    }
463
464    #[test]
465    fn test_manhattan_basic() {
466        let expected: HashSet<_> = [
467            make_xy(9, 10),
468            make_xy(10, 10),
469            make_xy(11, 10),
470            make_xy(10, 9),
471            make_xy(10, 11),
472        ]
473        .into_iter()
474        .collect();
475        let actual: HashSet<_> = manhattan_range_iter(make_xy(10, 10), 1).collect();
476        assert_eq!(expected, actual);
477    }
478
479    #[test]
480    fn test_manhattan_0_radius() {
481        let expected: HashSet<_> = [make_xy(10, 10)].into_iter().collect();
482        let actual: HashSet<_> = manhattan_range_iter(make_xy(10, 10), 0).collect();
483        assert_eq!(expected, actual);
484    }
485
486    #[test]
487    fn test_manhattan_boundary() {
488        let expected: HashSet<_> = [
489            make_xy(0, 1),
490            make_xy(0, 2),
491            make_xy(0, 3),
492            make_xy(1, 0),
493            make_xy(1, 1),
494            make_xy(1, 2),
495            make_xy(1, 3),
496            make_xy(1, 4),
497            make_xy(2, 1),
498            make_xy(2, 2),
499            make_xy(2, 3),
500            make_xy(3, 2),
501        ]
502        .into_iter()
503        .collect();
504        let actual: HashSet<_> = manhattan_range_iter(make_xy(1, 2), 2).collect();
505        assert_eq!(expected, actual);
506    }
507
508    #[test]
509    fn test_grid_iter_basic() {
510        let mut iter = GridIter::new(make_xy(0, 0), make_xy(1, 1), Order::XMajor);
511        assert_eq!(make_xy(0, 0), iter.next().unwrap());
512        assert_eq!(make_xy(0, 1), iter.next().unwrap());
513        assert_eq!(make_xy(1, 0), iter.next().unwrap());
514        assert_eq!(make_xy(1, 1), iter.next().unwrap());
515        assert_eq!(None, iter.next());
516
517        iter = GridIter::new(make_xy(0, 0), make_xy(1, 1), Order::YMajor);
518        assert_eq!(make_xy(0, 0), iter.next().unwrap());
519        assert_eq!(make_xy(1, 0), iter.next().unwrap());
520        assert_eq!(make_xy(0, 1), iter.next().unwrap());
521        assert_eq!(make_xy(1, 1), iter.next().unwrap());
522        assert_eq!(None, iter.next());
523    }
524
525    #[test]
526    fn test_grid_iter_reverse() {
527        let mut iter = GridIter::new(make_xy(0, 0), make_xy(1, 1), Order::XMajor);
528        assert_eq!(make_xy(1, 1), iter.next_back().unwrap());
529        assert_eq!(make_xy(1, 0), iter.next_back().unwrap());
530        assert_eq!(make_xy(0, 1), iter.next_back().unwrap());
531        assert_eq!(make_xy(0, 0), iter.next_back().unwrap());
532        assert_eq!(None, iter.next_back());
533
534        iter = GridIter::new(make_xy(0, 0), make_xy(1, 1), Order::YMajor);
535        assert_eq!(make_xy(1, 1), iter.next_back().unwrap());
536        assert_eq!(make_xy(0, 1), iter.next_back().unwrap());
537        assert_eq!(make_xy(1, 0), iter.next_back().unwrap());
538        assert_eq!(make_xy(0, 0), iter.next_back().unwrap());
539        assert_eq!(None, iter.next_back());
540    }
541
542    #[test]
543    fn test_grid_iter_len() {
544        let mut iter = GridIter::new(make_xy(0, 0), make_xy(2, 1), Order::XMajor);
545        for i in (1..=6).rev() {
546            assert_eq!(iter.len(), i);
547            iter.next().unwrap();
548        }
549        assert_eq!(iter.len(), 0);
550        assert_eq!(iter.next(), None);
551
552        iter = GridIter::new(make_xy(0, 0), make_xy(2, 1), Order::XMajor);
553        for i in (1..=6).rev() {
554            assert_eq!(iter.len(), i);
555            iter.next_back().unwrap();
556        }
557        assert_eq!(iter.len(), 0);
558        assert_eq!(iter.next_back(), None);
559    }
560
561    #[test]
562    fn test_grid_iter_bad_corners() {
563        let mut iter = GridIter::new(make_xy(10, 10), make_xy(10, 9), Order::XMajor);
564        assert_eq!(iter.len(), 0);
565        assert_eq!(iter.next(), None);
566
567        iter = GridIter::new(make_xy(10, 10), make_xy(9, 10), Order::XMajor);
568        assert_eq!(iter.len(), 0);
569        assert_eq!(iter.next(), None);
570
571        iter = GridIter::new(make_xy(10, 10), make_xy(9, 9), Order::XMajor);
572        assert_eq!(iter.len(), 0);
573        assert_eq!(iter.next(), None);
574    }
575
576    #[test]
577    fn test_grid_iter_single_square() {
578        let coords: Vec<_> =
579            GridIter::new(make_xy(25, 25), make_xy(25, 25), Order::XMajor).collect();
580        assert_eq!(coords, [make_xy(25, 25)]);
581    }
582
583    #[test]
584    fn test_grid_iter_mixing_forward_and_back() {
585        let mut iter = GridIter::new(make_xy(0, 0), make_xy(2, 1), Order::XMajor);
586        assert_eq!(iter.next().unwrap(), make_xy(0, 0));
587        assert_eq!(iter.next_back().unwrap(), make_xy(2, 1));
588        assert_eq!(iter.next_back().unwrap(), make_xy(2, 0));
589        assert_eq!(iter.next().unwrap(), make_xy(0, 1));
590        assert_eq!(iter.next().unwrap(), make_xy(1, 0));
591        assert_eq!(iter.next_back().unwrap(), make_xy(1, 1));
592        assert_eq!(iter.next_back(), None);
593        assert_eq!(iter.next(), None);
594    }
595
596    #[test]
597    fn test_grid_iter_fold() {
598        let expected = [
599            make_xy(0, 0),
600            make_xy(0, 1),
601            make_xy(0, 2),
602            make_xy(1, 0),
603            make_xy(1, 1),
604            make_xy(1, 2),
605        ];
606        let actual = GridIter::new(make_xy(0, 0), make_xy(1, 2), Order::XMajor).fold(
607            Vec::new(),
608            |mut v, xy| {
609                v.push(xy);
610                v
611            },
612        );
613        assert_eq!(expected.as_slice(), actual);
614    }
615
616    #[test]
617    fn test_grid_iter_rfold() {
618        let expected = [
619            make_xy(1, 2),
620            make_xy(0, 2),
621            make_xy(1, 1),
622            make_xy(0, 1),
623            make_xy(1, 0),
624            make_xy(0, 0),
625        ];
626        let actual = GridIter::new(make_xy(0, 0), make_xy(1, 2), Order::YMajor).rfold(
627            Vec::new(),
628            |mut v, xy| {
629                v.push(xy);
630                v
631            },
632        );
633        assert_eq!(expected.as_slice(), actual);
634    }
635
636    #[test]
637    fn test_grid_iter_single_row_fold() {
638        let mut base_iter = GridIter::new(make_xy(0, 0), make_xy(0, 10), Order::XMajor);
639        base_iter.next().unwrap();
640        base_iter.next_back().unwrap();
641        let expected: Vec<_> = (1..=9).map(|y| make_xy(0, y)).collect();
642        let actual = base_iter.clone().fold(Vec::new(), |mut v, xy| {
643            v.push(xy);
644            v
645        });
646        assert_eq!(expected, actual);
647
648        let expected: Vec<_> = (1..=9).rev().map(|y| make_xy(0, y)).collect();
649        let actual = base_iter.rfold(Vec::new(), |mut v, xy| {
650            v.push(xy);
651            v
652        });
653        assert_eq!(expected, actual);
654    }
655
656    #[test]
657    fn test_grid_iter_fold_general_case() {
658        let mut base_iter = GridIter::new(make_xy(0, 0), make_xy(3, 1), Order::XMajor);
659        base_iter.next().unwrap();
660        base_iter.next_back().unwrap();
661        let mut expected = [
662            make_xy(0, 1),
663            make_xy(1, 0),
664            make_xy(1, 1),
665            make_xy(2, 0),
666            make_xy(2, 1),
667            make_xy(3, 0),
668        ];
669        let actual = base_iter.clone().fold(Vec::new(), |mut v, xy| {
670            v.push(xy);
671            v
672        });
673        assert_eq!(expected.as_slice(), actual);
674
675        expected.reverse();
676        let actual = base_iter.rfold(Vec::new(), |mut v, xy| {
677            v.push(xy);
678            v
679        });
680        assert_eq!(expected.as_slice(), actual);
681    }
682}