1pub mod prelude;
6#[cfg(test)]
7mod test;
8
9pub mod grid;
10mod node;
11use node::Node;
12
13use crate::grid::Grid;
14use int_math::VectorU;
15use std::collections::{BinaryHeap, HashMap, HashSet};
16
17pub type Cost = u8;
19type Score = u16;
20
21pub const IMPASSABLE: u8 = u8::MAX;
23
24#[allow(unused)]
25
26fn neighbors(pos: VectorU) -> Vec<VectorU> {
27 let mut v = vec![
28 VectorU {
29 x: pos.x + 1,
30 y: pos.y,
31 },
32 VectorU {
33 x: pos.x,
34 y: pos.y + 1,
35 },
36 ];
37
38 if pos.x > 0 {
39 v.push(VectorU {
40 x: pos.x - 1,
41 y: pos.y,
42 });
43 }
44
45 if pos.y > 0 {
46 v.push(VectorU {
47 x: pos.x,
48 y: pos.y - 1,
49 });
50 }
51
52 v
53}
54
55#[allow(unused)]
59pub struct AStar {}
60
61impl AStar {
62 #[allow(unused)]
65 fn heuristic(p1: VectorU, p2: VectorU) -> Score {
66 ((p1.x as i32 - p2.x as i32).abs() + (p1.y as i32 - p2.y as i32).abs()) as Score
67 }
68
69 #[allow(unused)]
98 pub fn search(start: VectorU, goal: VectorU, grid: &Grid) -> Option<Vec<VectorU>> {
99 let mut open_set = BinaryHeap::new();
100 let mut open_set_positions = HashMap::new();
101 let mut closed_set = HashSet::new();
102
103 let start_node = Node {
104 position: start,
105 g: 0,
106 f: Self::heuristic(start, goal),
107 parent: None,
108 };
109
110 open_set.push(start_node.clone());
111 open_set_positions.insert(start, start_node);
112
113 while let Some(current_node) = open_set.pop() {
114 let current_position = current_node.position;
115 open_set_positions.remove(¤t_position);
116
117 if current_position == goal {
118 let mut path = vec![current_position];
119 let mut current = current_node;
120 while let Some(parent_node) = current.parent {
121 path.push(parent_node.position);
122 current = *parent_node;
123 }
124 path.reverse();
125 return Some(path);
126 }
127
128 closed_set.insert(current_position);
129
130 for neighbor_position in neighbors(current_position) {
131 if !grid.in_bounds(neighbor_position) {
132 continue;
133 }
134 if closed_set.contains(&neighbor_position) {
135 continue;
136 }
137
138 let neighbor_cost = grid.cost(neighbor_position);
139 if neighbor_cost == IMPASSABLE {
140 continue;
141 }
142
143 let tentative_g_score = ¤t_node.g + 1 + neighbor_cost as Score;
144
145 if let Some(open_neighbor_node) = open_set_positions.get(&neighbor_position) {
146 if tentative_g_score >= open_neighbor_node.g {
148 continue;
149 }
150 }
151
152 if open_set_positions.contains_key(&neighbor_position) {
153 continue;
154 }
155
156 let f_score = tentative_g_score + Self::heuristic(neighbor_position, goal);
157
158 let neighbor_node = Node {
159 position: neighbor_position,
160 g: tentative_g_score,
161 f: f_score,
162 parent: Some(Box::new(current_node.clone())),
163 };
164
165 open_set.push(neighbor_node.clone());
166 open_set_positions.insert(neighbor_position, neighbor_node);
167 }
168 }
169
170 None
171 }
172}