hierarchical_pathfinding/
neighbors.rs

1//! A crate with the most common Neighborhoods
2
3use crate::Point;
4use std::fmt::Debug;
5
6/// Defines how a Path can move along the Grid.
7///
8/// Different use cases may have different conditions for Paths.
9/// For example if movement is restricted to the 4 cardinal directions, any Paths generated should
10/// reflect that by only containing those steps.
11///
12/// This Trait is a generalized solution to that problem. It provides a function to query all
13/// neighboring Points of an existing Point and a Heuristic for how long it might take to reach
14/// a goal from a Point.
15///
16/// The most common implementations of this Trait are already provided by this Module:
17/// - [`ManhattanNeighborhood`] for Agents that can move
18/// up, down, left or right
19/// - [`MooreNeighborhood`] for Agents that can move
20/// up, down, left, right, as well as the 4 diagonals (up-right, ...)
21pub trait Neighborhood: Clone + Debug {
22    /// Provides all the Neighbors of a Point.
23    ///
24    /// The Neighbors should be written into `target`
25    ///
26    /// Note that it is not necessary to check weather the Tile at a Point is solid or not.
27    /// That check is done later.
28    fn get_all_neighbors(&self, point: Point, target: &mut Vec<Point>);
29    /// Gives a Heuristic for how long it takes to reach `goal` from `point`.
30    ///
31    /// This is usually the Distance between the two Points in the Metric of your Neighborhood.
32    ///
33    /// The returned value _**must be**_ less than or equal to the real cost of walking from
34    /// `point` to `goal`. See [A*](https://en.wikipedia.org/wiki/A*_search_algorithm) for details.
35    ///
36    /// If there is no proper way of calculation how long it takes, simply return 0. This will
37    /// increase the time it takes to calculate the Path, but at least it will always be correct.
38    fn heuristic(&self, point: Point, goal: Point) -> usize;
39}
40
41/// A Neighborhood for Agents moving along the 4 cardinal directions.
42///
43/// Also known as [Von Neumann Neighborhood](https://en.wikipedia.org/wiki/Von_Neumann_neighborhood),
44/// Manhattan Metric or [Taxicab Geometry](https://en.wikipedia.org/wiki/Taxicab_geometry).
45///
46/// ```no_code
47/// A: Agent, o: reachable in one step
48///   o
49///   |
50/// o-A-o
51///   |
52///   o
53/// ```
54#[derive(Clone, Copy, Debug)]
55pub struct ManhattanNeighborhood {
56    width: usize,
57    height: usize,
58}
59
60impl ManhattanNeighborhood {
61    /// Creates a new ManhattanNeighborhood.
62    ///
63    /// `width` and `height` are the size of the Grid to move on.
64    pub fn new(width: usize, height: usize) -> ManhattanNeighborhood {
65        ManhattanNeighborhood { width, height }
66    }
67}
68
69impl Neighborhood for ManhattanNeighborhood {
70    fn get_all_neighbors(&self, point: Point, target: &mut Vec<Point>) {
71        let (width, height) = (self.width, self.height);
72
73        #[rustfmt::skip]
74        static ALL_DELTAS: [(isize, isize); 4] = [(0, -1), (1, 0), (0, 1), (-1, 0)];
75
76        for (dx, dy) in ALL_DELTAS.iter() {
77            let x = point.0 as isize + dx;
78            let y = point.1 as isize + dy;
79            if x >= 0 && x < width as isize && y >= 0 && y < height as isize {
80                target.push((x as usize, y as usize))
81            }
82        }
83    }
84    fn heuristic(&self, point: Point, goal: Point) -> usize {
85        let diff_0 = if goal.0 > point.0 {
86            goal.0 - point.0
87        } else {
88            point.0 - goal.0
89        };
90        let diff_1 = if goal.1 > point.1 {
91            goal.1 - point.1
92        } else {
93            point.1 - goal.1
94        };
95        diff_0 + diff_1
96    }
97}
98
99/// A Neighborhood for Agents moving along the 4 cardinal directions and the 4 diagonals.
100///
101/// Also known as [Moore Neighborhood](https://en.wikipedia.org/wiki/Moore_neighborhood),
102/// [Maximum Metric](https://en.wikipedia.org/wiki/Chebyshev_distance) or Chebyshev Metric.
103///
104/// ```no_code
105/// A: Agent, o: reachable in one step
106/// o o o
107///  \|/
108/// o-A-o
109///  /|\
110/// o o o
111/// ```
112#[derive(Clone, Copy, Debug)]
113pub struct MooreNeighborhood {
114    width: usize,
115    height: usize,
116}
117
118impl MooreNeighborhood {
119    /// Creates a new MooreNeighborhood.
120    ///
121    /// `width` and `height` are the size of the Grid to move on.
122    pub fn new(width: usize, height: usize) -> MooreNeighborhood {
123        MooreNeighborhood { width, height }
124    }
125}
126
127impl Neighborhood for MooreNeighborhood {
128    fn get_all_neighbors(&self, point: Point, target: &mut Vec<Point>) {
129        let (width, height) = (self.width, self.height);
130
131        #[rustfmt::skip]
132        static ALL_DELTAS: [(isize, isize); 8] = [(0, -1), (1, -1), (1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1)];
133
134        for (dx, dy) in ALL_DELTAS.iter() {
135            let x = point.0 as isize + dx;
136            let y = point.1 as isize + dy;
137            if x >= 0 && x < width as isize && y >= 0 && y < height as isize {
138                target.push((x as usize, y as usize))
139            }
140        }
141    }
142    fn heuristic(&self, point: Point, goal: Point) -> usize {
143        let diff_0 = if goal.0 > point.0 {
144            goal.0 - point.0
145        } else {
146            point.0 - goal.0
147        };
148        let diff_1 = if goal.1 > point.1 {
149            goal.1 - point.1
150        } else {
151            point.1 - goal.1
152        };
153        diff_0.max(diff_1)
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160    mod manhattan {
161        use super::*;
162        #[test]
163        fn get_all_neighbors() {
164            let neighborhood = ManhattanNeighborhood::new(5, 5);
165            let mut target = vec![];
166            neighborhood.get_all_neighbors((0, 2), &mut target);
167            assert_eq!(target, vec![(0, 1), (1, 2), (0, 3)],);
168        }
169
170        #[test]
171        fn heuristic() {
172            let neighborhood = ManhattanNeighborhood::new(5, 5);
173            assert_eq!(neighborhood.heuristic((3, 1), (0, 0)), 3 + 1);
174        }
175    }
176    mod moore {
177        use super::*;
178
179        #[test]
180        fn get_all_neighbors() {
181            let neighborhood = MooreNeighborhood::new(5, 5);
182            let mut target = vec![];
183            neighborhood.get_all_neighbors((0, 2), &mut target);
184            assert_eq!(target, vec![(0, 1), (1, 1), (1, 2), (1, 3), (0, 3)],);
185        }
186
187        #[test]
188        fn heuristic() {
189            let neighborhood = MooreNeighborhood::new(5, 5);
190            assert_eq!(neighborhood.heuristic((3, 1), (0, 0)), 3);
191        }
192    }
193}