1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#[cfg(test)]
use std::collections::HashMap;

use crate::{graph::Graph, path::PathFinding};
#[cfg(test)]
use crate::graph::Edge;
use crate::grid::{Direction, Grid};
use crate::node::{Node, Position3D};
use crate::search::dijkstra;

pub fn get_position(node_id: usize, graph: &Graph) -> &Position3D {
    match &graph.node_position_lookup {
        None => panic!("You must offer node positions to the graph before using this heuristic."),
        Some(positions) => {
            return match positions.get(&node_id) {
                None => panic!("Node position missing for given node id: {node_id}"),
                Some(position) => position
            };
        }
    };
}

pub fn euclidean_distance(source: usize, destination: usize, graph: &Graph) -> f32 {
    let src = get_position(source, graph);
    let dest = get_position(destination, graph);

    return src.euclidean_dist(dest);
}

pub fn manhattan_distance(source: usize, destination: usize, graph: &Graph) -> f32 {
    let src = get_position(source, graph);
    let dest = get_position(destination, graph);

    return src.manhattan_dist(dest);
}

pub struct AStar {
    pub heuristic: Box<dyn Fn(usize, usize, &Graph) -> f32>,
}

impl PathFinding for AStar {
    fn graph(&self, source: Node, target: Node, graph: &Graph) -> Graph {
        return dijkstra(source, target, graph, &self.heuristic);
    }

    fn grid(&self, _source: (usize, usize), _target: (usize, usize), _grid: &Grid, _directions: &[Direction]) -> Graph {
        return Graph::from(Vec::new());
    }
}


#[test]
#[should_panic(expected = "You must offer node positions to the graph before using this heuristic.")]
fn missing_node_positions_should_cause_panic() {
    get_position(1, &Graph::from(Vec::from([
        Edge::from(0, 0, 1, 4.0)
    ])));
}

#[test]
#[should_panic(expected = "Node position missing for given node id: 1")]
fn missing_node_position_should_cause_panic() {
    let mut graph = Graph::from(Vec::from([
        Edge::from(0, 0, 1, 4.0)
    ]));

    graph.offer_positions(HashMap::from([(0, Position3D::from(0.0, 0.0, 0.0))]));

    get_position(1, &graph);
}

#[test]
fn node_position_should_be_returned() {
    let mut graph = Graph::from(Vec::from([
        Edge::from(0, 0, 1, 4.0)
    ]));

    graph.offer_positions(HashMap::from([(1, Position3D::from(0.1, 0.2, 0.3))]));
    let pos = get_position(1, &graph);

    assert_eq!(0.1, pos.x);
    assert_eq!(0.2, pos.y);
    assert_eq!(0.3, pos.z);
}

#[test]
fn euclidean_heuristic_should_return_dist() {
    let mut graph = Graph::from(Vec::from([
        Edge::from(0, 0, 1, 4.0)
    ]));

    graph.offer_positions(HashMap::from([
        (0, Position3D::from(20.0, 30.0, 90.0)),
        (1, Position3D::from(80.0, 44.0, 40.0))
    ]));

    assert_eq!(79.347336, euclidean_distance(0, 1, &graph));
}

#[test]
fn manhattan_heuristic_should_return_dist() {
    let mut graph = Graph::from(Vec::from([
        Edge::from(0, 0, 1, 4.0)
    ]));

    graph.offer_positions(HashMap::from([
        (0, Position3D::from(2.0, 9.0, 0.0)),
        (1, Position3D::from(3.0, 5.0, 0.0))
    ]));

    assert_eq!(5.0, manhattan_distance(0, 1, &graph));
}