1use std::cmp::Reverse;
19use std::collections::{BTreeMap, BTreeSet, VecDeque};
20
21use crate::pathfind::WALKABLE;
22use banished_terrain::CellType;
23
24const SECTOR: i32 = 8;
25const CACHE_LIFETIME: i64 = 120_000_000;
27const CACHE_SIZE: usize = 0x20000;
29const MAX_NODES: usize = 0x2000;
31
32fn walk(t: CellType) -> bool {
33 (1u32 << (t.0 & 0x1f)) & WALKABLE != 0
34}
35
36fn length(a: (i32, i32), b: (i32, i32)) -> i32 {
38 let (dx, dy) = (a.0 - b.0, a.1 - b.1);
39 ((dx * dx + dy * dy) as f32).sqrt() as i32
40}
41
42#[derive(Debug, Clone, Default)]
43struct Node {
44 pos: (i32, i32),
46 #[allow(dead_code)]
47 sector: i32,
48 links: Vec<i32>,
50 border: Vec<i32>,
51}
52
53#[derive(Debug, Clone, Default)]
54struct Sector {
55 x: i32,
56 y: i32,
57 nodes: Vec<i32>,
58 node_dirty: bool,
59 link_dirty: bool,
60}
61
62#[derive(Clone, Copy, Default)]
63struct Search {
64 parent: i32,
65 open: i32,
66 closed: i32,
67 g: i32,
68 key: (i32, i32),
69}
70
71#[derive(Debug, Clone)]
73struct Cache {
74 time: i64,
75 head: usize,
76 tail: usize,
77 ring: Vec<Option<((i32, i32), i32, i64)>>,
79 index: BTreeMap<(i32, i32), usize>,
80}
81
82pub struct SectorGraph {
86 n: i32,
87 w: i32,
88 h: i32,
89 nodes: Vec<Node>,
90 sectors: Vec<Sector>,
91 dirty_nodes: Vec<usize>,
92 dirty_links: Vec<usize>,
93 free: Vec<i32>,
94 pub cell_node: Vec<i32>,
97 search: Vec<Search>,
98 search_id: i32,
99 open: BTreeSet<((i32, Reverse<i32>), i32)>,
100 counter: i32,
101 cache: Cache,
102}
103
104impl SectorGraph {
105 pub fn new(size: usize, types: &[CellType]) -> SectorGraph {
109 let n = size as i32;
110 let (w, h) = (n / SECTOR, n / SECTOR);
111 let mut sectors = Vec::new();
112 for y in 0..h {
113 for x in 0..w {
114 sectors.push(Sector {
115 x,
116 y,
117 nodes: Vec::new(),
118 node_dirty: true,
119 link_dirty: true,
120 });
121 }
122 }
123 let count = sectors.len();
124 let mut g = SectorGraph {
125 n,
126 w,
127 h,
128 nodes: Vec::new(),
129 sectors,
130 dirty_nodes: (0..count).collect(),
131 dirty_links: (0..count).collect(),
132 free: Vec::new(),
133 cell_node: vec![-1; size * size],
134 search: Vec::new(),
135 search_id: 1,
136 open: BTreeSet::new(),
137 counter: 0,
138 cache: Cache {
139 time: 0,
140 head: 0,
141 tail: 0,
142 ring: vec![None; CACHE_SIZE],
143 index: BTreeMap::new(),
144 },
145 };
146 g.rebuild(types);
147 g
148 }
149
150 pub fn mark(&mut self, x: i32, y: i32) {
154 let s = ((y / SECTOR) * self.w + x / SECTOR) as usize;
155 if self.sectors[s].node_dirty {
156 return;
157 }
158 self.sectors[s].node_dirty = true;
159 self.dirty_nodes.push(s);
160 let (sx, sy) = (self.sectors[s].x, self.sectors[s].y);
161 for yy in sy - 1..=sy + 1 {
162 if yy < 0 || yy >= self.h {
163 continue;
164 }
165 for xx in sx - 1..=sx + 1 {
166 if xx < 0 || xx >= self.w {
167 continue;
168 }
169 let t = (yy * self.w + xx) as usize;
170 if !self.sectors[t].link_dirty {
171 self.sectors[t].link_dirty = true;
172 self.dirty_links.push(t);
173 }
174 }
175 }
176 }
177
178 pub fn rebuild(&mut self, types: &[CellType]) {
182 if self.dirty_nodes.is_empty() && self.dirty_links.is_empty() {
183 return;
184 }
185 for s in std::mem::take(&mut self.dirty_nodes) {
186 self.build_sector(s, types);
187 self.sectors[s].node_dirty = false;
188 }
189 for s in std::mem::take(&mut self.dirty_links) {
190 self.link_sector(s);
191 self.sectors[s].link_dirty = false;
192 }
193 self.search = vec![Search::default(); self.nodes.len()];
194 }
195
196 fn build_sector(&mut self, s: usize, types: &[CellType]) {
198 for id in std::mem::take(&mut self.sectors[s].nodes) {
200 let node = &mut self.nodes[id as usize];
201 *node = Node {
202 pos: (-1, -1),
203 sector: -1,
204 ..Default::default()
205 };
206 self.free.push(id);
207 }
208 let (x0, y0) = (self.sectors[s].x * SECTOR, self.sectors[s].y * SECTOR);
209 let labels = self.label(x0, y0, types);
210 let count = labels.iter().copied().max().unwrap_or(0);
211 for label in 1..=count {
212 let id = self.create_node(s, x0, y0, &labels, label);
213 self.sectors[s].nodes.push(id);
214 }
215 for ly in 0..SECTOR {
217 for lx in 0..SECTOR {
218 let label = labels[(ly * SECTOR + lx) as usize];
219 let cell = ((y0 + ly) * self.n + x0 + lx) as usize;
220 self.cell_node[cell] = if label >= 1 {
221 self.sectors[s].nodes[label as usize - 1]
222 } else {
223 -1
224 };
225 }
226 }
227 }
228
229 fn label(&self, x0: i32, y0: i32, types: &[CellType]) -> Vec<i32> {
232 let mut labels = vec![0i32; (SECTOR * SECTOR) as usize];
233 let at = |lx: i32, ly: i32| types[((y0 + ly) * self.n + x0 + lx) as usize];
234 let mut next = 1;
235 for ly in 0..SECTOR {
236 for lx in 0..SECTOR {
237 if !walk(at(lx, ly)) || labels[(ly * SECTOR + lx) as usize] != 0 {
238 continue;
239 }
240 let mut queue = VecDeque::from([(lx, ly)]);
241 while let Some((qx, qy)) = queue.pop_front() {
242 if labels[(qy * SECTOR + qx) as usize] != 0 || !walk(at(qx, qy)) {
243 continue;
244 }
245 let mut left = qx;
246 while left - 1 >= 0 && walk(at(left - 1, qy)) {
247 left -= 1;
248 }
249 let mut right = qx;
250 while right + 1 <= SECTOR - 1 && walk(at(right + 1, qy)) {
251 right += 1;
252 }
253 for u in left..=right {
254 labels[(qy * SECTOR + u) as usize] = next;
255 }
256 if left - 1 >= 0 {
257 left -= 1;
258 }
259 if right + 1 <= SECTOR - 1 {
260 right += 1;
261 }
262 for (ok, row) in [(qy > 0, qy - 1), (qy < SECTOR - 1, qy + 1)] {
263 if !ok {
264 continue;
265 }
266 for u in left..=right {
267 if labels[(row * SECTOR + u) as usize] == 0 && walk(at(u, row)) {
268 queue.push_back((u, row));
269 }
270 }
271 }
272 }
273 next += 1;
274 }
275 }
276 labels
277 }
278
279 fn create_node(&mut self, s: usize, x0: i32, y0: i32, labels: &[i32], label: i32) -> i32 {
281 let id = match self.free.pop() {
283 Some(id) => id,
284 None => {
285 self.nodes.push(Node::default());
286 self.nodes.len() as i32 - 1
287 }
288 };
289 let (mut sx, mut sy, mut count) = (0, 0, 0);
290 let mut border = Vec::new();
291 for ly in 0..SECTOR {
292 for lx in 0..SECTOR {
293 if labels[(ly * SECTOR + lx) as usize] != label {
294 continue;
295 }
296 let (gx, gy) = (lx + x0, ly + y0);
297 count += 1;
298 sx += gx;
299 sy += gy;
300 if lx == 0 || ly == 0 || lx == SECTOR - 1 || ly == SECTOR - 1 {
301 for dy in -1..=1 {
303 for dx in -1..=1 {
304 let (cx, cy) = (gx + dx, gy + dy);
305 if cx < 0 || cy < 0 || cx >= self.n || cy >= self.n {
306 continue;
307 }
308 let outside =
309 cx < x0 || cy < y0 || cx > x0 + SECTOR - 1 || cy > y0 + SECTOR - 1;
310 let cell = cy * self.n + cx;
311 if outside && !border.contains(&cell) {
312 border.push(cell);
313 }
314 }
315 }
316 }
317 }
318 }
319 self.nodes[id as usize] = Node {
320 pos: (sx / count, sy / count),
321 sector: s as i32,
322 links: Vec::new(),
323 border,
324 };
325 id
326 }
327
328 fn link_sector(&mut self, s: usize) {
330 for &id in &self.sectors[s].nodes.clone() {
331 let border = self.nodes[id as usize].border.clone();
332 let mut links = Vec::new();
333 for cell in border {
334 let other = self.cell_node[cell as usize];
335 if other >= 0 && !links.contains(&other) {
336 links.push(other);
337 }
338 }
339 self.nodes[id as usize].links = links;
340 }
341 }
342
343 pub fn node_near(&self, cell: i32) -> i32 {
347 let n = self.n;
348 let id = self.cell_node[cell as usize];
349 if id >= 0 {
350 return id;
351 }
352 let (x, y) = (cell % n, cell / n);
353 let node = |cx: i32, cy: i32| -> Option<i32> {
354 (cx >= 0 && cy >= 0 && cx < n && cy < n)
355 .then(|| self.cell_node[(cy * n + cx) as usize])
356 .filter(|&v| v >= 0)
357 };
358 for r in 1..=3 {
359 for t in x - r..=x + r {
360 let row = y + (t - x);
361 if row >= 0 && row < n {
362 if let Some(v) = node(x - r, row).or_else(|| node(x + r, row)) {
363 return v;
364 }
365 }
366 if t >= 0 && t < n {
367 if let Some(v) = node(t, y - r).or_else(|| node(t, y + r)) {
368 return v;
369 }
370 }
371 }
372 }
373 -1
374 }
375
376 fn push(&mut self, node: i32, parent: i32, g: i32, f: i32) {
377 let s = self.search[node as usize];
378 if s.open == self.search_id {
379 self.open.remove(&((s.key.0, Reverse(s.key.1)), node));
380 }
381 let counter = self.counter;
382 self.counter += 1;
383 let s = &mut self.search[node as usize];
384 s.closed = s.closed.wrapping_sub(1);
385 s.parent = parent;
386 s.open = self.search_id;
387 s.g = g;
388 s.key = (f, counter);
389 self.open.insert(((f, Reverse(counter)), node));
390 }
391
392 fn node_path(&mut self, a: i32, b: i32) -> Option<Vec<i32>> {
394 self.search_id = self.search_id.wrapping_add(1);
395 if self.search.len() < self.nodes.len() {
396 self.search.resize(self.nodes.len(), Search::default());
397 }
398 self.open.clear();
399 self.counter = 0;
400 let h = length(self.nodes[b as usize].pos, self.nodes[a as usize].pos);
401 self.push(a, -1, 0, h);
402 let mut found = false;
403 while let Some(&first) = self.open.first() {
404 let cur = first.1;
405 if cur == b {
406 found = true;
407 break;
408 }
409 self.open.remove(&first);
410 let id = self.search_id;
411 let s = &mut self.search[cur as usize];
412 s.open = s.open.wrapping_sub(1);
413 s.closed = id;
414 let g_cur = s.g;
415 let pos = self.nodes[cur as usize].pos;
416 for k in 0..self.nodes[cur as usize].links.len() {
417 let nb = self.nodes[cur as usize].links[k];
418 if nb < 0 {
419 continue;
420 }
421 let npos = self.nodes[nb as usize].pos;
422 let g = length(npos, pos) + g_cur;
423 let s = &mut self.search[nb as usize];
424 if s.closed == id {
425 if s.g <= g {
426 continue;
427 }
428 s.closed = s.closed.wrapping_sub(1);
429 }
430 if s.open != id || g < s.g {
431 let f = length(self.nodes[b as usize].pos, npos) + g;
432 self.push(nb, cur, g, f);
433 }
434 }
435 }
436 if !found {
437 return None;
438 }
439 let mut path = Vec::new();
440 let mut c = b;
441 while c != -1 {
442 path.push(c);
443 c = self.search[c as usize].parent;
444 }
445 path.reverse();
446 path.truncate(MAX_NODES);
447 Some(path)
448 }
449
450 fn node_distance(&mut self, a: i32, b: i32, types: &[CellType]) -> i32 {
452 self.rebuild(types);
453 let Some(path) = self.node_path(a, b) else {
454 return 0;
455 };
456 path.windows(2)
457 .map(|w| length(self.nodes[w[1] as usize].pos, self.nodes[w[0] as usize].pos))
458 .sum()
459 }
460
461 pub fn distance(
467 &mut self,
468 a: i32,
469 b: i32,
470 reachable: bool,
471 store: bool,
472 types: &[CellType],
473 ) -> i32 {
474 if !reachable {
475 return -1;
476 }
477 let na = self.node_near(a);
478 let nb = self.node_near(b);
479 if na < 0 || nb < 0 {
480 return -1;
481 }
482 if na == nb {
483 return 1;
484 }
485 let key = (na.max(nb), na.min(nb));
486 if let Some(&slot) = self.cache.index.get(&key) {
487 if let Some((_, v, _)) = self.cache.ring[slot] {
488 return v;
489 }
490 }
491 let d = self.node_distance(na, nb, types);
492 if store {
493 self.cache_store(key, d);
494 }
495 d
496 }
497
498 fn cache_store(&mut self, key: (i32, i32), value: i32) {
500 if let Some(&slot) = self.cache.index.get(&key) {
501 if let Some(e) = &mut self.cache.ring[slot] {
502 e.1 = value;
503 return;
504 }
505 }
506 let slot = self.cache.head;
507 self.cache.head = (slot + 1) % CACHE_SIZE;
508 if self.cache.head == self.cache.tail {
509 self.evict_oldest();
510 }
511 self.cache.ring[slot] = Some((key, value, self.cache.time));
512 self.cache.index.insert(key, slot);
513 }
514
515 fn evict_oldest(&mut self) {
516 let t = self.cache.tail;
517 if let Some((k, _, _)) = self.cache.ring[t].take() {
518 if self.cache.index.get(&k) == Some(&t) {
519 self.cache.index.remove(&k);
520 }
521 }
522 self.cache.tail = (t + 1) % CACHE_SIZE;
523 }
524
525 pub fn age_cache(&mut self, dt: i64) {
530 self.cache.time += dt;
531 loop {
532 let t = self.cache.tail;
533 let entry_time = self.cache.ring[t].map_or(0, |e| e.2);
534 if !(CACHE_LIFETIME < self.cache.time - entry_time) {
535 return;
536 }
537 if t == self.cache.head {
538 return;
539 }
540 self.evict_oldest();
541 }
542 }
543}
544
545#[cfg(test)]
546mod tests {
547 use super::*;
548
549 #[test]
550 fn open_map_distances() {
551 let n = 32;
552 let types = vec![CellType(2); n * n];
553 let mut g = SectorGraph::new(n, &types);
554 assert_eq!(g.cell_node[0], g.cell_node[7 * n + 7]);
556 assert_ne!(g.cell_node[0], g.cell_node[8]);
557 assert_eq!(g.distance(0, 1, true, false, &types), 1);
558 let d = g.distance(0, (n * n - 1) as i32, true, true, &types);
559 assert!(d > 0);
560 assert_eq!(g.distance(0, (n * n - 1) as i32, true, false, &types), d);
561 }
562}