-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpath_finder.go
More file actions
116 lines (96 loc) · 3.03 KB
/
path_finder.go
File metadata and controls
116 lines (96 loc) · 3.03 KB
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
113
114
115
116
package pathfind
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"math"
"slices"
)
const (
FUDGING = 1.5
)
// FindPath builds a pathfind.Path from passed args.
func FindPath(evaluator NodeEvaluator, source world.BlockSource, pos, target cube.Pos, maxVisitedNodes int, maxDistanceFromStart float64, reachRange int) *Path {
evaluator.Prepare(source, pos)
startNode := evaluator.StartNode()
actualTarget := evaluator.Goal(target)
result := findPath(evaluator, startNode, actualTarget, maxVisitedNodes, maxDistanceFromStart, reachRange)
evaluator.Done()
return result
}
// findPath finds from startNode to target.
func findPath(evaluator NodeEvaluator, startNode *Node, target *Target, maxVisitedNodes int, maxDistanceFromStart float64, reachRange int) *Path {
openSet := NewBinaryHeap()
startNode.g = 0
startNode.h = bestHeuristic(startNode, target)
startNode.f = startNode.h
openSet.Insert(startNode)
visitedNodes := 0
maxDistanceFromStartSqr := math.Pow(maxDistanceFromStart, 2)
for !openSet.IsEmpty() {
visitedNodes++
if visitedNodes >= maxVisitedNodes {
break
}
current := openSet.Pop()
current.Closed = true
if current.distanceManhattan(target.Pos) <= reachRange {
target.SetReached(true)
break
}
if current.distanceSquared(startNode) < maxDistanceFromStartSqr {
for _, neighbor := range evaluator.Neighbors(current) {
distance := current.distance(neighbor)
neighbor.walkedDistance = current.walkedDistance + distance
newNeighborG := current.g + distance + neighbor.CostMalus
if neighbor.walkedDistance < maxDistanceFromStart && (!neighbor.OpenSet() || newNeighborG < neighbor.g) {
neighbor.cameFrom = current
neighbor.g = newNeighborG
neighbor.h = bestHeuristic(neighbor, target) * FUDGING
if neighbor.OpenSet() {
openSet.ChangeCost(neighbor, neighbor.g+neighbor.h)
} else {
neighbor.f = neighbor.g + neighbor.h
openSet.Insert(neighbor)
}
}
}
}
}
return reconstructPath(target.BestNode(), target.Pos, target.Reached())
}
// bestHeuristic returns best heuristics.
func bestHeuristic(node *Node, targets ...*Target) float64 {
bestH := math.Inf(1)
for _, target := range targets {
h := node.Vec3().Sub(target.Vec3()).Len()
target.UpdateBest(h, node)
if h < bestH {
bestH = h
}
}
return bestH
}
// reconstructPath...
func reconstructPath(startNode *Node, target cube.Pos, reached bool) *Path {
var nodes []*Node
currentNode := startNode
for currentNode.cameFrom != nil {
nodes = append(nodes, currentNode)
currentNode = currentNode.cameFrom
}
slices.Reverse(nodes)
return NewPath(nodes, reached, target)
}
// NodeEvaluator interface that can be used to construct path using pathfind.FindPath.
type NodeEvaluator interface {
// Prepare prepares Node evaluator.
Prepare(source world.BlockSource, pos cube.Pos)
// Done ...
Done()
// StartNode returns start Node.
StartNode() *Node
// Goal returns target.
Goal(pos cube.Pos) *Target
// Neighbors ...
Neighbors(node *Node) []*Node
}