hexagon_map/sparse_map/
path_finder.rs

1use super::*;
2
3use crate::HexPoint;
4use ordered_float::OrderedFloat;
5use pathfinding::prelude::astar;
6
7/// A* path finder on a hexagon map.
8pub struct PathFinder<'a, T> {
9    map: &'a HexagonMap<T>,
10    start: CubicPoint,
11    end: CubicPoint,
12    passable: Box<dyn Fn(&CubicPoint, &T) -> bool>,
13    action_cost: Box<dyn Fn(&CubicPoint, &T) -> f64>,
14}
15
16impl<T> HexagonMap<T> {
17    /// Set the passable function.
18    ///
19    /// # Arguments
20    ///
21    /// * `passable`:  A function that returns true if the point is passable.
22    ///
23    /// returns: PathFinder<T>
24    ///
25    /// # Examples
26    ///
27    /// ```
28    /// # use hexagon_map::HexagonMap;
29    /// ```
30    pub fn path_finder(&self, start: CubicPoint, end: CubicPoint) -> PathFinder<T> {
31        PathFinder { map: self, start, end, passable: Box::new(|_, _| true), action_cost: Box::new(|_, _| 1.0) }
32    }
33}
34
35impl<'a, T> PathFinder<'a, T> {
36    /// Set the passable function.
37    ///
38    /// # Arguments
39    ///
40    /// * `passable`:  A function that returns true if the point is passable.
41    ///
42    /// returns: PathFinder<T>
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// # use hexagon_map::HexagonMap;
48    /// ```
49    pub fn with_passable<F>(mut self, passable: F) -> Self
50    where
51        F: Fn(&CubicPoint, &T) -> bool + 'static,
52    {
53        self.passable = Box::new(passable);
54        self
55    }
56    /// Set the passable function.
57    ///
58    /// # Arguments
59    ///
60    /// * `passable`:  A function that returns true if the point is passable.
61    ///
62    /// returns: PathFinder<T>
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// # use hexagon_map::HexagonMap;
68    /// ```
69    pub fn with_cost<F>(mut self, cost: F) -> Self
70    where
71        F: Fn(&CubicPoint, &T) -> f64 + 'static,
72    {
73        self.action_cost = Box::new(cost);
74        self
75    }
76    pub fn get_point(&self, point: &CubicPoint) -> Option<&T> {
77        self.map.sparse.get(point)
78    }
79    pub fn has_point(&self, point: &CubicPoint) -> bool {
80        self.map.sparse.contains_key(point)
81    }
82    fn distance(&self, point: &CubicPoint) -> OrderedFloat<f64> {
83        OrderedFloat(self.end.taxicab_distance(*point) as f64)
84    }
85    /// Get all passable neighbors from a point
86    pub fn neighbors(&self, point: &CubicPoint) -> Vec<(CubicPoint, OrderedFloat<f64>)> {
87        let mut neighbors = vec![];
88        for direction in Orientation::all() {
89            if let Some(target) = self.map.sparse.get(&point.go(direction)) {
90                if (self.passable)(point, target) {
91                    let cost = (self.action_cost)(point, target);
92                    neighbors.push((point.go(direction), OrderedFloat(cost)));
93                }
94            }
95        }
96        neighbors
97    }
98    pub fn solve_points(self) -> (Vec<CubicPoint>, f64) {
99        astar(&self.start, |point| self.neighbors(point), |point| self.distance(point), |point| self.end.eq(point))
100            .map(|(path, cost)| (path, cost.0))
101            .unwrap_or((vec![], f64::INFINITY))
102    }
103    pub fn solve_joint(self) -> (Vec<Joint>, f64) {
104        todo!()
105    }
106}