1use bracket_algorithm_traits::prelude::BaseMap;
2#[cfg(feature = "threaded")]
3use rayon::prelude::*;
4#[allow(unused_imports)]
5use smallvec::SmallVec;
6use std::collections::VecDeque;
7use std::convert::TryInto;
8use std::f32::MAX;
9
10pub struct DijkstraMap {
15 pub map: Vec<f32>,
16 size_x: usize,
17 size_y: usize,
18 max_depth: f32,
19}
20
21#[cfg(feature = "threaded")]
23struct ParallelDm {
24 map: Vec<f32>,
25 max_depth: f32,
26 starts: Vec<usize>,
27}
28
29#[allow(dead_code)]
35const THREADED_REQUIRED_STARTS: usize = 4;
36
37#[derive(PartialEq)]
38enum RunThreaded {
39 True,
40 False,
41}
42
43#[allow(dead_code)]
44impl DijkstraMap {
45 pub fn new<T>(
48 size_x: T,
49 size_y: T,
50 starts: &[usize],
51 map: &dyn BaseMap,
52 max_depth: f32,
53 ) -> DijkstraMap
54 where
55 T: TryInto<usize>,
56 {
57 let sz_x: usize = size_x.try_into().ok().unwrap();
58 let sz_y: usize = size_y.try_into().ok().unwrap();
59 let result: Vec<f32> = vec![MAX; sz_x * sz_y];
60 let mut d = DijkstraMap {
61 map: result,
62 size_x: sz_x,
63 size_y: sz_y,
64 max_depth,
65 };
66 DijkstraMap::build(&mut d, starts, map);
67 d
68 }
69
70 pub fn new_weighted<T>(
75 size_x: T,
76 size_y: T,
77 starts: &[(usize, f32)],
78 map: &dyn BaseMap,
79 max_depth: f32,
80 ) -> DijkstraMap
81 where
82 T: TryInto<usize>,
83 {
84 let sz_x: usize = size_x.try_into().ok().unwrap();
85 let sz_y: usize = size_y.try_into().ok().unwrap();
86 let result: Vec<f32> = vec![MAX; sz_x * sz_y];
87 let mut d = DijkstraMap {
88 map: result,
89 size_x: sz_x,
90 size_y: sz_y,
91 max_depth,
92 };
93 DijkstraMap::build_weighted(&mut d, starts, map);
94 d
95 }
96
97 pub fn new_empty<T>(size_x: T, size_y: T, max_depth: f32) -> DijkstraMap
99 where
100 T: TryInto<usize>,
101 {
102 let sz_x: usize = size_x.try_into().ok().unwrap();
103 let sz_y: usize = size_y.try_into().ok().unwrap();
104 let result: Vec<f32> = vec![MAX; sz_x * sz_y];
105 DijkstraMap {
106 map: result,
107 size_x: sz_x,
108 size_y: sz_y,
109 max_depth,
110 }
111 }
112
113 #[cfg(feature = "threaded")]
115 pub fn clear(dm: &mut DijkstraMap) {
116 dm.map.par_iter_mut().for_each(|x| *x = MAX);
117 }
118
119 #[cfg(not(feature = "threaded"))]
120 pub fn clear(dm: &mut DijkstraMap) {
121 dm.map.iter_mut().for_each(|x| *x = MAX);
122 }
123
124 #[cfg(feature = "threaded")]
125 fn build_helper(dm: &mut DijkstraMap, starts: &[usize], map: &dyn BaseMap) -> RunThreaded {
126 if starts.len() >= THREADED_REQUIRED_STARTS {
127 DijkstraMap::build_parallel(dm, starts, map);
128 return RunThreaded::True;
129 }
130 RunThreaded::False
131 }
132
133 #[cfg(not(feature = "threaded"))]
134 fn build_helper(_dm: &mut DijkstraMap, _starts: &[usize], _map: &dyn BaseMap) -> RunThreaded {
135 RunThreaded::False
136 }
137
138 pub fn build(dm: &mut DijkstraMap, starts: &[usize], map: &dyn BaseMap) {
145 let threaded = DijkstraMap::build_helper(dm, starts, map);
146 if threaded == RunThreaded::True {
147 return;
148 }
149 let mapsize: usize = (dm.size_x * dm.size_y) as usize;
150 let mut open_list: VecDeque<(usize, f32)> = VecDeque::with_capacity(mapsize);
151
152 for start in starts {
153 open_list.push_back((*start, 0.0));
154 }
155
156 while let Some((tile_idx, depth)) = open_list.pop_front() {
157 let exits = map.get_available_exits(tile_idx);
158 for (new_idx, add_depth) in exits {
159 let new_depth = depth + add_depth;
160 let prev_depth = dm.map[new_idx];
161 if new_depth >= prev_depth {
162 continue;
163 }
164 if new_depth >= dm.max_depth {
165 continue;
166 }
167 dm.map[new_idx] = new_depth;
168 open_list.push_back((new_idx, new_depth));
169 }
170 }
171 }
172
173 pub fn build_weighted(dm: &mut DijkstraMap, starts: &[(usize, f32)], map: &dyn BaseMap) {
180 let mapsize: usize = (dm.size_x * dm.size_y) as usize;
181 let mut open_list: VecDeque<(usize, f32)> = VecDeque::with_capacity(mapsize);
182
183 for start in starts {
184 open_list.push_back(*start);
185 }
186
187 while let Some((tile_idx, depth)) = open_list.pop_front() {
188 let exits = map.get_available_exits(tile_idx);
189 for (new_idx, add_depth) in exits {
190 let new_depth = depth + add_depth;
191 let prev_depth = dm.map[new_idx];
192 if new_depth >= prev_depth {
193 continue;
194 }
195 if new_depth >= dm.max_depth {
196 continue;
197 }
198 dm.map[new_idx] = new_depth;
199 open_list.push_back((new_idx, new_depth));
200 }
201 }
202 }
203
204 #[cfg(feature = "threaded")]
206 fn build_parallel(dm: &mut DijkstraMap, starts: &[usize], map: &dyn BaseMap) {
207 let mapsize: usize = (dm.size_x * dm.size_y) as usize;
208 let mut layers: Vec<ParallelDm> = Vec::with_capacity(starts.len());
209 for start_chunk in starts.chunks(rayon::current_num_threads()) {
210 let mut layer = ParallelDm {
211 map: vec![MAX; mapsize],
212 max_depth: dm.max_depth,
213 starts: Vec::new(),
214 };
215 layer
216 .starts
217 .extend(start_chunk.iter().copied().map(|x| x as usize));
218 layers.push(layer);
219 }
220
221 let exits: Vec<SmallVec<[(usize, f32); 10]>> = (0..mapsize)
222 .map(|idx| map.get_available_exits(idx))
223 .collect();
224
225 layers.par_iter_mut().for_each(|l| {
227 let mut open_list: VecDeque<(usize, f32)> = VecDeque::with_capacity(mapsize);
228
229 for start in l.starts.iter().copied() {
230 open_list.push_back((start, 0.0));
231 }
232
233 while let Some((tile_idx, depth)) = open_list.pop_front() {
234 let exits = &exits[tile_idx];
235 for (new_idx, add_depth) in exits {
236 let new_idx = *new_idx;
237 let new_depth = depth + add_depth;
238 let prev_depth = l.map[new_idx];
239 if new_depth >= prev_depth {
240 continue;
241 }
242 if new_depth >= l.max_depth {
243 continue;
244 }
245 l.map[new_idx] = new_depth;
246 open_list.push_back((new_idx, new_depth));
247 }
248 }
249 });
250
251 for l in layers {
253 for i in 0..mapsize {
254 dm.map[i] = f32::min(dm.map[i], l.map[i]);
255 }
256 }
257 }
258
259 #[cfg(feature = "threaded")]
263 pub fn find_lowest_exit(dm: &DijkstraMap, position: usize, map: &dyn BaseMap) -> Option<usize> {
264 let mut exits = map.get_available_exits(position);
265
266 if exits.is_empty() {
267 return None;
268 }
269
270 exits.par_sort_by(|a, b| {
271 dm.map[a.0 as usize]
272 .partial_cmp(&dm.map[b.0 as usize])
273 .unwrap()
274 });
275
276 Some(exits[0].0)
277 }
278
279 #[cfg(not(feature = "threaded"))]
280 pub fn find_lowest_exit(dm: &DijkstraMap, position: usize, map: &dyn BaseMap) -> Option<usize> {
281 let mut exits = map.get_available_exits(position);
282
283 if exits.is_empty() {
284 return None;
285 }
286
287 exits.sort_by(|a, b| {
288 dm.map[a.0 as usize]
289 .partial_cmp(&dm.map[b.0 as usize])
290 .unwrap()
291 });
292
293 Some(exits[0].0)
294 }
295
296 #[cfg(feature = "threaded")]
301 pub fn find_highest_exit(
302 dm: &DijkstraMap,
303 position: usize,
304 map: &dyn BaseMap,
305 ) -> Option<usize> {
306 let mut exits = map.get_available_exits(position);
307
308 if exits.is_empty() {
309 return None;
310 }
311
312 exits.par_sort_by(|a, b| {
313 dm.map[b.0 as usize]
314 .partial_cmp(&dm.map[a.0 as usize])
315 .unwrap()
316 });
317
318 Some(exits[0].0)
319 }
320
321 #[cfg(not(feature = "threaded"))]
322 pub fn find_highest_exit(
323 dm: &DijkstraMap,
324 position: usize,
325 map: &dyn BaseMap,
326 ) -> Option<usize> {
327 let mut exits = map.get_available_exits(position);
328
329 if exits.is_empty() {
330 return None;
331 }
332
333 exits.sort_by(|a, b| {
334 dm.map[b.0 as usize]
335 .partial_cmp(&dm.map[a.0 as usize])
336 .unwrap()
337 });
338
339 Some(exits[0].0)
340 }
341}
342
343#[cfg(test)]
344mod test {
345 use crate::prelude::*;
346 use bracket_algorithm_traits::prelude::*;
347 struct MiniMap;
349 impl BaseMap for MiniMap {
350 fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]> {
351 match idx {
352 0 => smallvec![(1, 1.)],
353 2 => smallvec![(1, 1.)],
354 _ => smallvec![(idx - 1, 1.), (idx + 1, 2.)],
355 }
356 }
357 }
358 #[test]
359 fn test_highest_exit() {
360 let map = MiniMap {};
361 let exits_map = DijkstraMap::new(3, 1, &[0], &map, 10.);
362 let target = DijkstraMap::find_highest_exit(&exits_map, 0, &map);
363 assert_eq!(target, Some(1));
364 let target = DijkstraMap::find_highest_exit(&exits_map, 1, &map);
365 assert_eq!(target, Some(2));
366 }
367}