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}