forked from beefsack/go-astar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
astar.go
96 lines (87 loc) · 2.38 KB
/
astar.go
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
package astar
import "container/heap"
// astar is an A* pathfinding implementation.
// Pather is an interface which allows A* searching on arbitrary objects which
// can represent a weighted graph.
type Pather interface {
// PathNeighbors returns the direct neighboring nodes of this node which
// can be pathed to.
PathNeighbors() []Pather
// PathNeighbourCost calculates the exact movement cost to neighbor nodes.
PathNeighborCost(to Pather) float64
// PathEstimatedCost is a heuristic method for estimating movement costs
// between non-adjacent nodes.
PathEstimatedCost(to Pather) float64
}
// node is a wrapper to store A* data for a Pather node.
type node struct {
pather Pather
cost float64
rank float64
parent *node
open bool
closed bool
index int
}
// nodeMap is a collection of nodes keyed by Pather nodes for quick reference.
type nodeMap map[Pather]*node
// get gets the Pather object wrapped in a node, instantiating if required.
func (nm nodeMap) get(p Pather) *node {
n, ok := nm[p]
if !ok {
n = &node{
pather: p,
}
nm[p] = n
}
return n
}
// Path calculates a short path and the distance between the two Pather nodes.
//
// If no path is found, found will be false.
func Path(from, to Pather) (path []Pather, distance float64, found bool) {
nm := nodeMap{}
nq := &priorityQueue{}
heap.Init(nq)
fromNode := nm.get(from)
fromNode.open = true
heap.Push(nq, fromNode)
for {
if nq.Len() == 0 {
// There's no path, return found false.
return
}
current := heap.Pop(nq).(*node)
current.open = false
current.closed = true
if current == nm.get(to) {
// Found a path to the goal.
p := []Pather{}
curr := current
for curr != nil {
p = append(p, curr.pather)
curr = curr.parent
}
return p, current.cost, true
}
// examine neighbors of current
for _, neighbor := range current.pather.PathNeighbors() {
cost := current.cost + current.pather.PathNeighborCost(neighbor)
neighborNode := nm.get(neighbor)
if cost < neighborNode.cost {
if neighborNode.open {
heap.Remove(nq, neighborNode.index)
}
neighborNode.open = false
neighborNode.closed = false
}
if !neighborNode.open && !neighborNode.closed {
neighborNode.cost = cost
neighborNode.open = true
neighborNode.rank = cost + neighbor.PathEstimatedCost(to)
neighborNode.parent = current
heap.Push(nq, neighborNode)
}
}
}
}