Skip to content

Commit ebd4190

Browse files
author
Peter Vaiko
committed
feat: hybrid pathfinder with JPS
1 parent 071d536 commit ebd4190

4 files changed

Lines changed: 105 additions & 122 deletions

File tree

scripts/pathfinder/HybridAStar.lua

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -467,9 +467,14 @@ function HybridAStar.PenaltyCache:inSameCell(n1, n2)
467467
end
468468

469469
---@param node State3D
470-
function HybridAStar.PenaltyCache:get(node)
470+
function HybridAStar.PenaltyCache:get(node, constraints)
471471
local x, y, t = self:getNodeIndexes(node)
472-
return self.nodes[x] and self.nodes[x][y]
472+
local cachedPenalty = self.nodes[x] and self.nodes[x][y]
473+
if cachedPenalty == nil then
474+
cachedPenalty = constraints:getNodePenalty(node)
475+
self:add(node, cachedPenalty)
476+
end
477+
return cachedPenalty
473478
end
474479

475480
---@param node State3D
@@ -675,12 +680,7 @@ function HybridAStar:run(start, goal, turnRadius, allowReverse, constraints, hit
675680
-- we end up being in overlap with another vehicle when we start the pathfinding and all we need is
676681
-- an iteration or two to bring us out of that position
677682
if (self.ignoreValidityAtStart and self.iterations < 3) or self.constraints:isValidNode(succ) then
678-
local penalty = self.penaltyCache:get(succ)
679-
if penalty == nil then
680-
penalty = self.constraints:getNodePenalty(succ)
681-
self.penaltyCache:add(succ, penalty)
682-
end
683-
succ:updateG(primitive, penalty)
683+
succ:updateG(primitive, self.penaltyCache:get(succ, self.constraints))
684684
local analyticSolutionCost = 0
685685
if self.analyticSolverEnabled then
686686
local analyticSolution = self.analyticSolver:solve(succ, self.goal, self.turnRadius)

scripts/pathfinder/HybridAStarWithAStarInTheMiddle.lua

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function HybridAStarWithAStarInTheMiddle:init(vehicle, yieldAfter, maxIterations
1313
-- path generation phases
1414
self.vehicle = vehicle
1515
self.START_TO_MIDDLE = 1
16-
self.ASTAR = 2
16+
self.FAST = 2
1717
self.MIDDLE_TO_END = 3
1818
self.ALL_HYBRID = 4 -- start and goal close enough, we only need a single phase with hybrid
1919
self.hybridRange = 20 -- default range around start/goal to use hybrid A *
@@ -22,12 +22,12 @@ function HybridAStarWithAStarInTheMiddle:init(vehicle, yieldAfter, maxIterations
2222
-- the only reason we have a separate instance for start and end is to be able to draw the nodes after
2323
-- the pathfinding is done for debug purposes
2424
self.startHybridAStarPathfinder = HybridAStar(vehicle, self.yieldAfter, maxIterations, mustBeAccurate)
25-
self.aStarPathfinder = self:getAStar()
25+
self.middlePathfinder = self:getFastPathfinder()
2626
self.endHybridAStarPathfinder = HybridAStar(vehicle, self.yieldAfter, maxIterations, mustBeAccurate)
2727
self.analyticSolver = analyticSolver
2828
end
2929

30-
function HybridAStarWithAStarInTheMiddle:getAStar()
30+
function HybridAStarWithAStarInTheMiddle:getFastPathfinder()
3131
return AStar(self.vehicle, self.yieldAfter, self.maxIterations)
3232
end
3333

@@ -53,10 +53,10 @@ function HybridAStarWithAStarInTheMiddle:start(start, goal, turnRadius, allowRev
5353
self.hybridRange = self.hybridRange and self.hybridRange or turnRadius * 3
5454
-- how far is start/goal apart?
5555
self.startNode:updateH(self.goalNode, turnRadius)
56-
self.phase = self.ASTAR
57-
self:debug('Finding fast A* path between start and goal...')
58-
self.coroutine = coursePlayCoroutine.create(self.aStarPathfinder.run)
59-
self.currentPathfinder = self.aStarPathfinder
56+
self.phase = self.FAST
57+
self:debug('Start first pass, fast pathfinding between start and goal...')
58+
self.coroutine = coursePlayCoroutine.create(self.middlePathfinder.run)
59+
self.currentPathfinder = self.middlePathfinder
6060
-- strict mode for the middle part, stay close to the field, for future improvements, disabled for now
6161
-- self.constraints:setStrictMode()
6262
return self:resume(self.startNode, self.goalNode, turnRadius, false, constraints, hitchLength)
@@ -118,10 +118,10 @@ function HybridAStarWithAStarInTheMiddle:resume(...)
118118
return PathfinderResult(true, nil, result.goalNodeInvalid, self.currentPathfinder:getHighestDistance(),
119119
self.constraints)
120120
end
121-
elseif self.phase == self.ASTAR then
121+
elseif self.phase == self.FAST then
122122
self.constraints:resetStrictMode()
123123
if not result.path then
124-
self:debug('fast A*: no path found')
124+
self:debug('First pass: no path found')
125125
return PathfinderResult(true, nil, result.goalNodeInvalid, self.currentPathfinder:getHighestDistance(),
126126
self.constraints)
127127
end
@@ -191,7 +191,7 @@ end
191191

192192
function HybridAStarWithAStarInTheMiddle:nodeIterator()
193193
local startIt = self.startHybridAStarPathfinder:nodeIterator()
194-
local middleIt = self.aStarPathfinder:nodeIterator()
194+
local middleIt = self.middlePathfinder:nodeIterator()
195195
local endIt = self.endHybridAStarPathfinder:nodeIterator()
196196
return function()
197197
local node, lowestCost, highestCost = startIt()
@@ -211,7 +211,7 @@ function HybridAStarWithAStarInTheMiddle:nodeIteratorStart()
211211
end
212212

213213
function HybridAStarWithAStarInTheMiddle:nodeIteratorMiddle()
214-
return self.aStarPathfinder:nodeIterator()
214+
return self.middlePathfinder:nodeIterator()
215215
end
216216

217217
function HybridAStarWithAStarInTheMiddle:nodeIteratorEnd()
@@ -257,6 +257,6 @@ function HybridAStarWithPathInTheMiddle:start(...)
257257
return HybridAStarWithAStarInTheMiddle.start(self, ...)
258258
end
259259

260-
function HybridAStarWithPathInTheMiddle:getAStar()
260+
function HybridAStarWithPathInTheMiddle:getFastPathfinder()
261261
return DummyAStar(self.vehicle, self.path)
262262
end

0 commit comments

Comments
 (0)