-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathastar.go
89 lines (75 loc) · 2.28 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
// Copyright 2013 Frederik Zipp. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package astar implements the A* search algorithm for finding least-cost paths.
package astar
import (
"container/heap"
"iter"
)
// The Graph interface is the minimal interface a graph data structure
// must satisfy to be suitable for the A* algorithm.
type Graph[Node any] interface {
// Neighbours returns the neighbour nodes of node n in the graph.
Neighbours(n Node) iter.Seq[Node]
}
// A CostFunc is a function that returns a cost for the transition
// from node a to node b.
type CostFunc[Node any] func(a, b Node) float64
// A Path is a sequence of nodes in a graph.
type Path[Node any] []Node
// newPath creates a new path with one start node. More nodes can be
// added with append().
func newPath[Node any](start Node) Path[Node] {
return []Node{start}
}
// last returns the last node of path p. It is not removed from the path.
func (p Path[Node]) last() Node {
return p[len(p)-1]
}
// cont creates a new path, which is a continuation of path p with the
// additional node n.
func (p Path[Node]) cont(n Node) Path[Node] {
cp := make([]Node, len(p), len(p)+1)
copy(cp, p)
cp = append(cp, n)
return cp
}
// Cost calculates the total cost of path p by applying the cost function d
// to all path segments and returning the sum.
func (p Path[Node]) Cost(d CostFunc[Node]) (c float64) {
for i := 1; i < len(p); i++ {
c += d(p[i-1], p[i])
}
return c
}
// FindPath finds the least-cost path between start and dest in graph g
// using the cost function d and the cost heuristic function h.
// Returns nil if no path was found.
func FindPath[Node comparable](g Graph[Node], start, dest Node, d, h CostFunc[Node]) Path[Node] {
var closed set[Node]
pq := &priorityQueue[Path[Node]]{}
heap.Init(pq)
heap.Push(pq, &item[Path[Node]]{value: newPath(start)})
for pq.Len() > 0 {
p := heap.Pop(pq).(*item[Path[Node]]).value
n := p.last()
if closed.Contains(n) {
continue
}
if n == dest {
// Path found
return p
}
closed.Add(n)
for nb := range g.Neighbours(n) {
cp := p.cont(nb)
heap.Push(pq, &item[Path[Node]]{
value: cp,
priority: -(cp.Cost(d) + h(nb, dest)),
})
}
}
// No path found
return nil
}