1use std::cmp::Reverse;
22use std::collections::{BTreeSet, VecDeque};
23
24use banished_terrain::CellType;
25
26pub const WALKABLE: u32 = 0xe1f;
28pub const WATER: u32 = 0x1e0;
30pub const MAX_PATH: usize = 0x4000;
34
35pub fn bit(t: CellType) -> u32 {
37 1u32 << (t.0 & 0x1f)
38}
39
40pub fn walkable(t: CellType) -> bool {
42 bit(t) & WALKABLE != 0
43}
44
45pub fn regions(size: usize, types: &[CellType]) -> Vec<u16> {
49 let mut region = vec![0u16; types.len()];
50 let mut id: u16 = 1;
51 for i in 0..types.len() {
52 if bit(types[i]) & WALKABLE != 0 && region[i] == 0 {
53 fill(size, types, &mut region, i, id);
54 id = id.wrapping_add(1);
55 }
56 }
57 region
58}
59
60fn fill(size: usize, types: &[CellType], region: &mut [u16], start: usize, id: u16) {
64 let n = size as i32;
65 let walk = |c: i32| bit(types[c as usize]) & WALKABLE != 0;
66 let mut queue = VecDeque::from([start as i32]);
67 while let Some(c) = queue.pop_front() {
68 let row = c / n;
69 let (row_start, row_end) = (n * row, (row + 1) * n - 1);
70 if region[c as usize] != 0 || !walk(c) {
71 continue;
72 }
73 let mut left = c;
74 while left - 1 >= row_start && walk(left - 1) {
75 left -= 1;
76 }
77 let mut right = c;
78 while right + 1 <= row_end && walk(right + 1) {
79 right += 1;
80 }
81 for k in left..=right {
82 region[k as usize] = id;
83 }
84 if row_start < left - 1 {
85 left -= 1;
86 }
87 if right + 1 < row_end {
88 right += 1;
89 }
90 for (ok, step) in [(row > 0, -n), (row < n - 1, n)] {
91 if !ok {
92 continue;
93 }
94 for u in left..=right {
95 let k = u + step;
96 if region[k as usize] == 0 && walk(k) {
97 queue.push_back(k);
98 }
99 }
100 }
101 }
102}
103
104pub fn mode_costs(mode: u32) -> Option<(&'static [(u8, i32)], bool)> {
109 const CITIZEN: [(u8, i32); 11] = [
110 (0, 3),
111 (1, 3),
112 (2, 4),
113 (3, 8),
114 (4, 16),
115 (5, 16),
116 (6, 16),
117 (7, 16),
118 (8, 16),
119 (9, 3),
120 (10, 4),
121 ];
122 const MODE1: [(u8, i32); 11] = [
123 (0, 16),
124 (1, 16),
125 (2, 4),
126 (3, 4),
127 (4, 16),
128 (5, 16),
129 (6, 16),
130 (7, 4),
131 (8, 4),
132 (9, 4),
133 (10, 4),
134 ];
135 const UNIFORM: [(u8, i32); 11] = [
136 (0, 1),
137 (1, 1),
138 (2, 1),
139 (3, 1),
140 (4, 1),
141 (5, 1),
142 (6, 1),
143 (7, 1),
144 (8, 1),
145 (9, 1),
146 (10, 1),
147 ];
148 const MODE3: [(u8, i32); 11] = [
149 (0, 10),
150 (1, 10),
151 (2, 10),
152 (3, 10),
153 (4, 10),
154 (5, 10),
155 (6, 10),
156 (7, 5),
157 (8, 1),
158 (9, 5),
159 (10, 10),
160 ];
161 match mode {
162 0 => Some((&CITIZEN, true)),
163 1 => Some((&MODE1, true)),
164 2 => Some((&UNIFORM, false)),
165 3 => Some((&MODE3, true)),
166 _ => None,
167 }
168}
169
170#[derive(Clone, Copy, Default)]
171struct Node {
172 parent: i32,
173 open: i32,
175 closed: i32,
176 g: i32,
177 key: (i32, i32),
179}
180
181pub struct Pathfinder {
186 size: i32,
187 cells: Vec<u8>,
189 costs: [i32; 32],
191 pub mask: u32,
194 diagonal: bool,
196 manhattan: bool,
197 check_blocked: bool,
198 search: i32,
199 counter: i32,
200 nodes: Vec<Node>,
201 open: BTreeSet<((i32, Reverse<i32>), i32)>,
202}
203
204impl Pathfinder {
205 pub fn new(size: usize, types: &[CellType]) -> Pathfinder {
209 Pathfinder {
210 size: size as i32,
211 cells: types.iter().map(|t| t.0 & 0x7f).collect(),
212 costs: [1; 32],
213 mask: 0,
214 diagonal: true,
215 manhattan: false,
216 check_blocked: false,
217 search: 0,
218 counter: 0,
219 nodes: vec![Node::default(); types.len()],
220 open: BTreeSet::new(),
221 }
222 }
223
224 pub fn set_type(&mut self, cell: usize, t: CellType) {
226 self.cells[cell] = self.cells[cell] & 0x80 | t.0 & 0x7f;
227 }
228
229 pub fn set_blocked(&mut self, cell: usize, blocked: bool) {
231 self.cells[cell] = self.cells[cell] & 0x7f | if blocked { 0x80 } else { 0 };
232 }
233
234 pub fn set_mode(&mut self, mode: u32) {
238 if let Some((costs, diagonal)) = mode_costs(mode) {
239 for &(t, c) in costs {
240 self.costs[t as usize] = c;
241 }
242 self.diagonal = diagonal;
243 self.manhattan = false;
244 self.check_blocked = false;
245 }
246 }
247
248 fn neighbour(&self, cell: i32, dir: usize) -> Option<i32> {
250 let n = self.size;
251 let (mut x, mut y) = (cell % n, cell / n);
252 match dir {
253 0 => x += 1,
254 1 => x -= 1,
255 2 => y += 1,
256 3 => y -= 1,
257 4 => (x, y) = (x + 1, y + 1),
258 5 => (x, y) = (x + 1, y - 1),
259 6 => (x, y) = (x - 1, y - 1),
260 _ => (x, y) = (x - 1, y + 1),
261 }
262 if x < 0 || x >= n || y < 0 || y >= n {
263 return None;
264 }
265 let k = n * y + x;
266 let byte = self.cells[k as usize];
267 if self.check_blocked && byte & 0x80 != 0 {
268 return None;
269 }
270 (self.mask >> (byte & 0x1f) & 1 != 0).then_some(k)
271 }
272
273 fn heuristic(&self, a: i32, b: i32) -> i32 {
275 let n = self.size;
276 let dx = (a % n - b % n).abs();
277 let dy = (a / n - b / n).abs();
278 if self.diagonal {
279 (dy + dx) * 10 - 6 * dx.min(dy)
280 } else if self.manhattan {
281 (dy + dx) * 10
282 } else {
283 dy.max(dx) * 10
284 }
285 }
286
287 fn push(&mut self, cell: i32, parent: i32, g: i32, f: i32) {
289 let node = self.nodes[cell as usize];
290 if node.open == self.search {
291 self.open.remove(&((node.key.0, Reverse(node.key.1)), cell));
292 }
293 let counter = self.counter;
294 self.counter += 1;
295 let node = &mut self.nodes[cell as usize];
296 node.closed = node.closed.wrapping_sub(1);
297 node.parent = parent;
298 node.open = self.search;
299 node.g = g;
300 node.key = (f, counter);
301 self.open.insert(((f, Reverse(counter)), cell));
302 }
303
304 fn solve(&mut self, from: i32, to: i32) -> bool {
307 self.open.clear();
308 self.counter = 0;
309 let h = self.heuristic(from, to);
310 self.push(from, -1, 0, h);
311 let n = self.size;
312 while let Some(&first) = self.open.first() {
313 let cur = first.1;
314 if cur == to {
315 return true;
316 }
317 self.open.remove(&first);
318 let search = self.search;
319 let node = &mut self.nodes[cur as usize];
320 node.open = node.open.wrapping_sub(1);
321 node.closed = search;
322 let g_cur = node.g;
323 for dir in 0..if self.diagonal { 8 } else { 4 } {
324 let Some(nb) = self.neighbour(cur, dir) else {
325 continue;
326 };
327 let straight = (cur % n - nb % n).abs() + (cur / n - nb / n).abs() == 1;
328 let cost = self.costs[(self.cells[nb as usize] & 0x7f) as usize & 31]
329 * if straight { 10 } else { 14 };
330 let g = g_cur + cost;
331 let node = &mut self.nodes[nb as usize];
332 if node.closed == search {
333 if node.g <= g {
334 continue;
335 }
336 node.closed = node.closed.wrapping_sub(1);
337 }
338 if node.open != search || g < node.g {
339 let f = self.heuristic(nb, to) + g;
340 self.push(nb, cur, g, f);
341 }
342 }
343 }
344 false
345 }
346
347 pub fn find_road(
353 &mut self,
354 from: i32,
355 to: i32,
356 mask: u32,
357 manhattan: bool,
358 blocked: &[bool],
359 ) -> Option<Vec<i32>> {
360 let saved = (
361 self.costs,
362 self.diagonal,
363 self.manhattan,
364 self.check_blocked,
365 self.mask,
366 );
367 self.costs = [1; 32];
368 self.diagonal = false;
369 self.manhattan = manhattan;
370 self.check_blocked = true;
371 self.mask = mask;
372 let old: Vec<u8> = self.cells.clone();
373 for (c, &b) in self.cells.iter_mut().zip(blocked) {
374 if b {
375 *c |= 0x80;
376 }
377 }
378 let path = self.find(from, to);
379 self.cells = old;
380 (
381 self.costs,
382 self.diagonal,
383 self.manhattan,
384 self.check_blocked,
385 self.mask,
386 ) = saved;
387 path
388 }
389
390 pub fn keeping_state<T>(&mut self, search: impl FnOnce(&mut Self) -> T) -> T {
392 let saved = (
393 self.costs,
394 self.diagonal,
395 self.manhattan,
396 self.check_blocked,
397 self.mask,
398 );
399 let out = search(self);
400 (
401 self.costs,
402 self.diagonal,
403 self.manhattan,
404 self.check_blocked,
405 self.mask,
406 ) = saved;
407 out
408 }
409
410 pub fn find(&mut self, from: i32, to: i32) -> Option<Vec<i32>> {
414 self.search = self.search.wrapping_add(1);
415 if !self.solve(from, to) {
416 return None;
417 }
418 let mut path = Vec::new();
419 let mut c = to;
420 while c != -1 {
421 path.push(c);
422 c = self.nodes[c as usize].parent;
423 }
424 path.reverse();
425 path.truncate(MAX_PATH);
426 Some(path)
427 }
428}
429
430pub fn request(
435 finder: &mut Pathfinder,
436 types: &[CellType],
437 region: &[u16],
438 flags: u32,
439 from: i32,
440 to: i32,
441) -> Option<Vec<i32>> {
442 let (a, b) = (bit(types[from as usize]), bit(types[to as usize]));
443 let mut flags = flags | a | b;
444 if flags & WATER == 0 {
445 let (ra, rb) = (region[from as usize], region[to as usize]);
446 if ra == 0 || rb == 0 || ra != rb {
447 return None;
448 }
449 }
450 if flags & 0x180 != 0 {
451 flags |= 0x180;
452 }
453 finder.mask = flags;
454 finder.find(from, to)
455}
456
457pub fn nearest_neighbour(
462 size: usize,
463 types: &[CellType],
464 cell: i32,
465 toward: i32,
466 mask: u32,
467) -> Option<i32> {
468 let n = size as i32;
469 let (cx, cy) = (cell % n, cell / n);
470 let mut best: Option<(i32, i32)> = None;
471 for dx in -1..=1 {
472 for dy in -1..=1 {
473 let (x, y) = (cx + dx, cy + dy);
474 if (dx == 0 && dy == 0) || x < 0 || y < 0 || x >= n || y >= n {
475 continue;
476 }
477 let k = n * y + x;
478 if mask & bit(types[k as usize]) == 0 {
479 continue;
480 }
481 let (ex, ey) = (k / n - toward / n, k % n - toward % n);
482 let d = ex * ex + ey * ey;
483 if best.is_none_or(|(_, bd)| d < bd) {
484 best = Some((k, d));
485 }
486 }
487 }
488 best.map(|(k, _)| k)
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494
495 fn grid(rows: &[&str]) -> (usize, Vec<CellType>) {
496 let n = rows.len();
498 let mut t = vec![CellType(2); n * n];
499 for (r, row) in rows.iter().enumerate() {
500 let y = n - 1 - r;
501 for (x, ch) in row.chars().enumerate() {
502 t[y * n + x] = match ch {
503 '#' => CellType(8),
504 '=' => CellType(1),
505 _ => CellType(2),
506 };
507 }
508 }
509 (n, t)
510 }
511
512 #[test]
513 fn regions_split_by_water() {
514 let (n, t) = grid(&["..#..", "..#..", "..#..", "..#..", "..#.."]);
515 let r = regions(n, &t);
516 assert_eq!(r[0], 1);
517 assert_eq!(r[3], 2);
518 assert_eq!(r[2], 0);
519 }
520
521 #[test]
522 fn path_goes_around_water_and_prefers_roads() {
523 let (n, t) = grid(&[".....", ".###.", ".....", ".....", "....."]);
524 let r = regions(n, &t);
525 let mut pf = Pathfinder::new(n, &t);
526 pf.set_mode(0);
527 let from = (3 * n) as i32; let to = (3 * n + 4) as i32; let p = request(&mut pf, &t, &r, WALKABLE, from, to).unwrap();
530 assert_eq!(*p.first().unwrap(), from);
531 assert_eq!(*p.last().unwrap(), to);
532 assert!(p.iter().all(|&c| t[c as usize] != CellType(8)));
533 let (n, t) = grid(&["..#..", "..#..", "..#..", "..#..", "..#.."]);
535 let r = regions(n, &t);
536 let mut pf = Pathfinder::new(n, &t);
537 pf.set_mode(0);
538 assert!(request(&mut pf, &t, &r, WALKABLE, 0, 4).is_none());
539 }
540}