1use std::collections::BinaryHeap;
2
3use movingai::Coords2D;
4use movingai::Map2D;
5
6use crate::node::Node;
7use crate::utils::{direction, distance, rewind};
8use crate::Route;
9
10#[derive(Copy, Clone)]
11enum Direction {
12 Vertical(i32),
13 Horizontal(i32),
14 Diagonal(i32, i32),
15}
16
17#[inline]
40pub fn jps_path<U, T: Map2D<U>>(map: &T, start: Coords2D, goal: Coords2D) -> Option<Route> {
41 if start == goal {
42 return Some(Route::from((0.0, vec![])));
43 }
44
45 let start_node = Node::new(0.0, distance(start, goal), start, start);
47
48 let prev_x = start_node.position.0 - 1;
51 let next_x = start_node.position.0 + 1;
52 let prev_y = start_node.position.1 - 1;
53 let next_y = start_node.position.1 + 1;
54
55 let capacity = (1 + next_x - prev_x) * (1 + next_y - prev_y);
57 let mut open = BinaryHeap::with_capacity(capacity);
58 let mut closed = Vec::with_capacity(capacity);
59
60 for x in prev_x..=next_x {
61 for y in prev_y..=next_y {
62 open.push(Node::from_parent(&start_node, (x, y), goal));
63 }
64 }
65
66 closed.push(start_node);
67
68 while let Some(node_current) = open.pop() {
70 if node_current.position == goal {
72 closed.append(&mut open.into_vec());
74
75 let path = rewind(&node_current, &closed);
77 let route = Route::from((node_current.g, path));
78 return Some(route);
79 }
80
81 if closed.contains(&node_current) {
83 continue;
84 }
85
86 let direction = direction(node_current.position, node_current.parent);
88
89 if let Some(nodes) = check_jump(&node_current, map, direction, goal) {
90 for node in nodes {
91 open.push(node);
92 }
93 }
94
95 closed.push(node_current);
97 }
98
99 None
100}
101
102#[inline]
103fn check_jump<U, T: Map2D<U>>(
104 parent: &Node,
105 map: &T,
106 (dx, dy): (i32, i32),
107 goal: Coords2D,
108) -> Option<Vec<Node>> {
109 if dx != 0 {
110 if dy != 0 {
111 expand(map, &parent, Direction::Diagonal(dx, dy), goal)
112 } else {
113 expand(map, &parent, Direction::Horizontal(dx), goal)
114 }
115 } else if dy != 0 {
116 expand(map, &parent, Direction::Vertical(dy), goal)
117 } else {
118 None
119 }
120}
121
122#[inline]
123fn forced_horizontal<U, T: Map2D<U>>(
124 nodes: &mut Vec<Node>,
125 map: &T,
126 check_node: &Node,
127 direction: i32,
128 goal: Coords2D,
129) {
130 let (check_x, check_y) = check_node.position;
131 let next_x = (check_x as i32 + direction) as usize;
132 let up_y = (check_y as i32 - 1) as usize;
133 let down_y = (check_y as i32 + 1) as usize;
134
135 if !map.is_traversable((check_x, up_y)) && map.is_traversable((next_x, up_y)) {
137 nodes.push(Node::from_parent(&check_node, (next_x, up_y), goal));
138 }
139
140 if !map.is_traversable((check_x, down_y)) && map.is_traversable((next_x, down_y)) {
142 nodes.push(Node::from_parent(&check_node, (next_x, down_y), goal));
143 }
144}
145
146#[inline]
147fn forced_vertical<U, T: Map2D<U>>(
148 nodes: &mut Vec<Node>,
149 map: &T,
150 check_node: &Node,
151 direction: i32,
152 goal: Coords2D,
153) {
154 let (check_x, check_y) = check_node.position;
155 let left_x = (check_x as i32 - 1) as usize;
156 let right_x = (check_x as i32 + 1) as usize;
157 let next_y = (check_y as i32 + direction) as usize;
158
159 if !map.is_traversable((left_x, check_y)) && map.is_traversable((left_x, next_y)) {
161 nodes.push(Node::from_parent(&check_node, (left_x, next_y), goal));
162 }
163
164 if !map.is_traversable((right_x, check_y)) && map.is_traversable((right_x, next_y)) {
166 nodes.push(Node::from_parent(&check_node, (right_x, next_y), goal));
167 }
168}
169
170#[inline]
171fn expand<U, T: Map2D<U>>(
172 map: &T,
173 start_node: &Node,
174 direction: Direction,
175 goal: Coords2D,
176) -> Option<Vec<Node>> {
177 let mut current = *start_node;
178 let mut nodes = Vec::new();
179 loop {
180 if current.position == goal {
182 nodes.push(current);
183
184 return Some(nodes);
185 }
186
187 if !map.is_traversable(current.position) {
189 return None;
190 }
191
192 let dir = match direction {
194 Direction::Vertical(vert) => {
195 forced_vertical(&mut nodes, map, ¤t, vert, goal);
197
198 (0, vert)
199 }
200 Direction::Horizontal(hor) => {
201 forced_horizontal(&mut nodes, map, ¤t, hor, goal);
203
204 (hor, 0)
205 }
206 Direction::Diagonal(hor, vert) => {
207 if let Some(mut hor_nodes) = expand(map, ¤t, Direction::Horizontal(hor), goal)
209 {
210 nodes.append(&mut hor_nodes);
211 }
212 if let Some(mut vert_nodes) = expand(map, ¤t, Direction::Vertical(vert), goal)
214 {
215 nodes.append(&mut vert_nodes);
216 }
217
218 (hor, vert)
219 }
220 };
221
222 let next_x = (current.position.0 as i32 + dir.0) as usize;
223 let next_y = (current.position.1 as i32 + dir.1) as usize;
224 let next_position = (next_x, next_y);
225
226 if !nodes.is_empty() {
228 let next_node = Node::from_parent(¤t, next_position, goal);
229 nodes.push(current);
230 nodes.push(next_node);
231
232 return Some(nodes);
233 }
234
235 current = Node::from_parent(start_node, next_position, goal);
237 }
238}