basic_pathfinding/
search.rs

1use std::collections::BinaryHeap;
2use std::collections::HashMap;
3
4use crate::coord::Coord;
5use crate::grid::Grid;
6use crate::node::Node;
7
8#[derive(Default, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct SearchOpts {
11  pub cost_threshold: Option<i32>,
12  pub end_on_unstoppable: bool,
13  pub path_to_threshold: bool,
14  pub path_adjacent: bool,
15}
16
17pub struct Search {
18  pub start: Coord,
19  pub end: Vec<Coord>,
20  pub dest: Option<Coord>,
21  pub heap: BinaryHeap<Node>,
22  pub cache: HashMap<i32, HashMap<i32, Node>>,
23  pub opts: SearchOpts,
24}
25
26impl Search {
27  pub fn new(start: Coord, end: Vec<Coord>, dest: Option<Coord>, opts: SearchOpts) -> Search {
28    Search {
29      start: start,
30      dest: dest,
31      end: end,
32      heap: BinaryHeap::new(),
33      cache: HashMap::new(),
34      opts: opts,
35    }
36  }
37
38  pub fn reached_destination(&self) -> bool {
39    match self.peek() {
40      Some(curr) => self
41        .end
42        .iter()
43        .find(|c| c.matches(curr.x, curr.y))
44        .is_some(),
45      _ => false,
46    }
47  }
48
49  pub fn push(&mut self, node: Node) {
50    self.heap.push(node.clone());
51    self.cache(node);
52  }
53
54  pub fn cache(&mut self, node: Node) {
55    match self.cache.remove(&node.y) {
56      None => {
57        let mut inner_hash = HashMap::new();
58        inner_hash.insert(node.x, node);
59        self.cache.insert(node.y, inner_hash);
60      }
61      Some(mut inner_hash) => {
62        inner_hash.insert(node.x, node);
63        self.cache.insert(node.y, inner_hash);
64      }
65    }
66  }
67
68  pub fn peek(&self) -> Option<&Node> {
69    match self.heap.peek() {
70      None => None,
71      Some(node) => self.get_node(node.x, node.y),
72    }
73  }
74
75  pub fn pop(&mut self) -> Option<Node> {
76    self.heap.pop()
77  }
78
79  pub fn size(&self) -> usize {
80    self.heap.len()
81  }
82
83  pub fn update(&mut self, node: Node) {
84    self.cache(node);
85  }
86
87  pub fn get_node(&self, x: i32, y: i32) -> Option<&Node> {
88    match self.cache.get(&y) {
89      None => None,
90      Some(inner_hash) => inner_hash.get(&x),
91    }
92  }
93
94  pub fn is_pathing(&self) -> bool {
95    self.dest.is_some()
96  }
97
98  pub fn coordinate_to_node(&self, parent: Option<&Node>, x: i32, y: i32, cost: i32) -> Node {
99    match self.get_node(x, y) {
100      Some(&node) => node,
101      None => {
102        let distance = match !self.is_pathing() {
103          true => 1,
104          false => {
105            let dest = self.dest.unwrap();
106            get_distance(x, y, dest.x, dest.y)
107          }
108        };
109
110        Node::new(
111          parent,
112          x,
113          y,
114          match parent {
115            None => cost,
116            Some(parent) => parent.cost + cost,
117          },
118          distance,
119        )
120      }
121    }
122  }
123
124  pub fn check_adjacent_node(&mut self, grid: &Grid, source_node: &Node, x: i32, y: i32) {
125    let adjacent_x = source_node.x + x;
126    let adjacent_y = source_node.y + y;
127    let adjacent_cost = grid.get_coord_cost(adjacent_x, adjacent_y);
128
129    if grid.is_coord_walkable(adjacent_x, adjacent_y)
130      & can_afford(
131        source_node,
132        adjacent_cost,
133        match self.opts.path_to_threshold {
134          true => None,
135          _ => self.opts.cost_threshold,
136        },
137      )
138    {
139      let mut adjacent_node =
140        self.coordinate_to_node(Some(source_node), adjacent_x, adjacent_y, adjacent_cost);
141
142      if !adjacent_node.visited {
143        self.push(adjacent_node);
144      } else if (source_node.cost + adjacent_cost) < adjacent_node.cost {
145        adjacent_node.cost = source_node.cost + adjacent_cost;
146        adjacent_node.parent = Some(Coord::new(source_node.x, source_node.y));
147        self.update(adjacent_node);
148      }
149    }
150  }
151
152  pub fn traversed_nodes(&self) -> Vec<&Node> {
153    let nodes = &mut vec![];
154    for list in self.cache.values() {
155      nodes.extend(list.values());
156    }
157    nodes.to_vec()
158  }
159}
160
161fn can_afford(node: &Node, cost: i32, cost_threshold: Option<i32>) -> bool {
162  match cost_threshold {
163    None => true,
164    Some(cost_threshold) => node.cost + cost <= cost_threshold,
165  }
166}
167
168fn get_distance(x1: i32, y1: i32, x2: i32, y2: i32) -> i32 {
169  let dx = (x1 - x2).abs();
170  let dy = (y1 - y2).abs();
171  dx + dy
172}