Further improvements by switching to keys.
This commit is contained in:
Frank
2020-07-31 00:09:21 +02:00
parent ba90176bb1
commit da2aa00442
2 changed files with 81 additions and 105 deletions

View File

@@ -21,6 +21,11 @@
-- @field #string lid Class id string for output to DCS log file. -- @field #string lid Class id string for output to DCS log file.
-- @field #table nodes Table of nodes. -- @field #table nodes Table of nodes.
-- @field #number counter Node counter. -- @field #number counter Node counter.
-- @field #number Nnodes Number of nodes.
-- @field #number nvalid Number of nvalid calls.
-- @field #number nvalidcache Number of cached valid evals.
-- @field #number ncost Number of cost evaluations.
-- @field #number ncostcache Number of cached cost evals.
-- @field #ASTAR.Node startNode Start node. -- @field #ASTAR.Node startNode Start node.
-- @field #ASTAR.Node endNode End node. -- @field #ASTAR.Node endNode End node.
-- @field Core.Point#COORDINATE startCoord Start coordinate. -- @field Core.Point#COORDINATE startCoord Start coordinate.
@@ -138,6 +143,11 @@ ASTAR = {
lid = nil, lid = nil,
nodes = {}, nodes = {},
counter = 1, counter = 1,
Nnodes = 0,
ncost = 0,
ncostcache = 0,
nvalid = 0,
nvalidcache = 0,
} }
--- Node data. --- Node data.
@@ -154,7 +164,7 @@ ASTAR.INF=1/0
--- ASTAR class version. --- ASTAR class version.
-- @field #string version -- @field #string version
ASTAR.version="0.3.0" ASTAR.version="0.4.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
@@ -233,7 +243,8 @@ end
-- @return #ASTAR self -- @return #ASTAR self
function ASTAR:AddNode(Node) function ASTAR:AddNode(Node)
table.insert(self.nodes, Node) self.nodes[Node.id]=Node
self.Nnodes=self.Nnodes+1
return self return self
end end
@@ -484,7 +495,7 @@ function ASTAR.LoS(nodeA, nodeB, corridor)
local Ap=UTILS.VecTranslate(cA, dx, heading+90) local Ap=UTILS.VecTranslate(cA, dx, heading+90)
local Bp=UTILS.VecTranslate(cB, dx, heading+90) local Bp=UTILS.VecTranslate(cB, dx, heading+90)
los=land.isVisible(Ap, Bp) --Ap:IsLOS(Bp, offset) los=land.isVisible(Ap, Bp)
if los then if los then
@@ -634,27 +645,34 @@ function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
local start=self.startNode local start=self.startNode
local goal=self.endNode local goal=self.endNode
-- Sets.
local openset = {}
local closedset = {} local closedset = {}
local openset = { start }
local came_from = {} local came_from = {}
local g_score = {}
local g_score, f_score = {}, {} local f_score = {}
g_score[start]=0 openset[start.id]=true
f_score[start]=g_score[start]+self:_HeuristicCost(start, goal) local Nopen=1
-- Initial scores.
g_score[start.id]=0
f_score[start.id]=g_score[start.id]+self:_HeuristicCost(start, goal)
-- Set start time. -- Set start time.
local T0=timer.getAbsTime() local T0=timer.getAbsTime()
-- Debug message. -- Debug message.
local text=string.format("Starting A* pathfinding") local text=string.format("Starting A* pathfinding with %d Nodes", self.Nnodes)
self:I(self.lid..text) self:I(self.lid..text)
MESSAGE:New(text, 10, "ASTAR"):ToAllIf(self.Debug) MESSAGE:New(text, 10, "ASTAR"):ToAllIf(self.Debug)
local Tstart=UTILS.GetOSTime() local Tstart=UTILS.GetOSTime()
while #openset > 0 do -- Loop while we still have an open set.
while Nopen > 0 do
-- Get current node.
local current=self:_LowestFscore(openset, f_score) local current=self:_LowestFscore(openset, f_score)
-- Check if we are at the end node. -- Check if we are at the end node.
@@ -678,39 +696,44 @@ function ASTAR:GetPath(ExcludeStartNode, ExcludeEndNode)
end end
-- Debug message. -- Debug message.
local text=string.format("Found path with %d nodes (%d total nodes)", #path, #self.nodes) local text=string.format("Found path with %d nodes (%d total)", #path, self.Nnodes)
if dT then if dT then
text=text..string.format(". OS Time %.6f seconds", dT) text=text..string.format(", OS Time %.6f sec", dT)
end end
text=text..string.format("\nNvalid = %d %d cached", self.nvalid, self.nvalidcache) text=text..string.format(", Nvalid=%d [%d cached]", self.nvalid, self.nvalidcache)
text=text..string.format("\nNcost = %d %d cached", self.ncost, self.ncostcache) text=text..string.format(", Ncost=%d [%d cached]", self.ncost, self.ncostcache)
self:I(self.lid..text) self:I(self.lid..text)
MESSAGE:New(text, 60, "ASTAR"):ToAllIf(self.Debug) MESSAGE:New(text, 60, "ASTAR"):ToAllIf(self.Debug)
return path return path
end end
self:_RemoveNode(openset, current) -- Move Node from open to closed set.
table.insert(closedset, current) openset[current.id]=nil
Nopen=Nopen-1
closedset[current.id]=true
-- Get neighbour nodes.
local neighbors=self:_NeighbourNodes(current, nodes) local neighbors=self:_NeighbourNodes(current, nodes)
-- Loop over neighbours. -- Loop over neighbours.
for _,neighbor in ipairs(neighbors) do for _,neighbor in pairs(neighbors) do
if self:_NotIn(closedset, neighbor) then if self:_NotIn(closedset, neighbor.id) then
local tentative_g_score=g_score[current]+self:_DistNodes(current, neighbor) local tentative_g_score=g_score[current.id]+self:_DistNodes(current, neighbor)
if self:_NotIn(openset, neighbor) or tentative_g_score < g_score[neighbor] then if self:_NotIn(openset, neighbor.id) or tentative_g_score < g_score[neighbor.id] then
came_from[neighbor]=current came_from[neighbor]=current
g_score[neighbor]=tentative_g_score g_score[neighbor.id]=tentative_g_score
f_score[neighbor]=g_score[neighbor]+self:_HeuristicCost(neighbor, goal) f_score[neighbor.id]=g_score[neighbor.id]+self:_HeuristicCost(neighbor, goal)
if self:_NotIn(openset, neighbor) then if self:_NotIn(openset, neighbor.id) then
table.insert(openset, neighbor) -- Add to open set.
openset[neighbor.id]=true
Nopen=Nopen+1
end end
end end
@@ -736,21 +759,14 @@ end
-- @param #ASTAR.Node nodeB Node B. -- @param #ASTAR.Node nodeB Node B.
-- @return #number "Cost" to go from node A to node B. -- @return #number "Cost" to go from node A to node B.
function ASTAR:_HeuristicCost(nodeA, nodeB) function ASTAR:_HeuristicCost(nodeA, nodeB)
if self.ncost then -- Counter.
self.ncost=self.ncost+1 self.ncost=self.ncost+1
else
self.ncost=1
end
-- Get chached cost if available. -- Get chached cost if available.
local cost=nodeA.cost[nodeB.id] local cost=nodeA.cost[nodeB.id]
if cost~=nil then if cost~=nil then
if self.ncostcache then self.ncostcache=self.ncostcache+1
self.ncostcache=self.ncostcache+1
else
self.ncostcache=1
end
return cost return cost
end end
@@ -774,20 +790,13 @@ end
-- @return #boolean If true, transition between nodes is possible. -- @return #boolean If true, transition between nodes is possible.
function ASTAR:_IsValidNeighbour(node, neighbor) function ASTAR:_IsValidNeighbour(node, neighbor)
if self.nvalid then -- Counter.
self.nvalid=self.nvalid+1 self.nvalid=self.nvalid+1
else
self.nvalid=1
end
local valid=node.valid[neighbor.id] local valid=node.valid[neighbor.id]
if valid~=nil then if valid~=nil then
--env.info(string.format("Node %d has valid=%s neighbour %d", node.id, tostring(valid), neighbor.id)) --env.info(string.format("Node %d has valid=%s neighbour %d", node.id, tostring(valid), neighbor.id))
if self.nvalidcache then self.nvalidcache=self.nvalidcache+1
self.nvalidcache=self.nvalidcache+1
else
self.nvalidcache=1
end
return valid return valid
end end
@@ -815,44 +824,25 @@ end
--- Function that calculates the lowest F score. --- Function that calculates the lowest F score.
-- @param #ASTAR self -- @param #ASTAR self
-- @param #table set The set of nodes. -- @param #table set The set of nodes IDs.
-- @param #number f_score F score.
-- @return #ASTAR.Node Best node.
function ASTAR:_LowestFscore2(set, f_score)
local lowest, bestNode = ASTAR.INF, nil
for _, node in ipairs ( set ) do
local score = f_score [ node ]
if score < lowest then
lowest, bestNode = score, node
end
end
return bestNode
end
--- Function that calculates the lowest F score.
-- @param #ASTAR self
-- @param #table set The set of nodes.
-- @param #number f_score F score. -- @param #number f_score F score.
-- @return #ASTAR.Node Best node. -- @return #ASTAR.Node Best node.
function ASTAR:_LowestFscore(set, f_score) function ASTAR:_LowestFscore(set, f_score)
local function sort(A, B) local lowest, bestNode = ASTAR.INF, nil
local a=A --#ASTAR.Node
local b=B --#ASTAR.Node
return f_score[a]<f_score[b]
end
table.sort(set, sort)
return set[1] for nid,node in pairs(set) do
local score=f_score[nid]
if score<lowest then
lowest, bestNode = score, nid
end
end
return self.nodes[bestNode]
end end
--- Function to get valid neighbours of a node. --- Function to get valid neighbours of a node.
-- @param #ASTAR self -- @param #ASTAR self
-- @param #ASTAR.Node theNode The node. -- @param #ASTAR.Node theNode The node.
@@ -861,7 +851,8 @@ end
function ASTAR:_NeighbourNodes(theNode, nodes) function ASTAR:_NeighbourNodes(theNode, nodes)
local neighbors = {} local neighbors = {}
for _, node in pairs ( nodes ) do
for _,node in pairs(nodes) do
if theNode.id~=node.id then if theNode.id~=node.id then
@@ -884,31 +875,7 @@ end
-- @param #ASTAR.Node theNode The node to check. -- @param #ASTAR.Node theNode The node to check.
-- @return #boolean If true, the node is not in the set. -- @return #boolean If true, the node is not in the set.
function ASTAR:_NotIn(set, theNode) function ASTAR:_NotIn(set, theNode)
return set[theNode]==nil
for _, node in pairs ( set ) do
if node.id == theNode.id then
return false
end
end
return true
end
--- Function to remove a node from a set.
-- @param #ASTAR self
-- @param #table set Set of nodes.
-- @param #ASTAR.Node theNode The node to check.
function ASTAR:_RemoveNode(set, theNode)
for i, node in pairs ( set ) do
if node.id == theNode.id then
--table.remove(set, i)
set [ i ] = set [ #set ]
set [ #set ] = nil
break
end
end
end end
--- Unwind path function. --- Unwind path function.
@@ -919,9 +886,9 @@ end
-- @return #table Unwinded path. -- @return #table Unwinded path.
function ASTAR:_UnwindPath( flat_path, map, current_node ) function ASTAR:_UnwindPath( flat_path, map, current_node )
if map [ current_node ] then if map [current_node] then
table.insert ( flat_path, 1, map [ current_node ] ) table.insert (flat_path, 1, map[current_node])
return self:_UnwindPath ( flat_path, map, map [ current_node ] ) return self:_UnwindPath(flat_path, map, map[current_node])
else else
return flat_path return flat_path
end end

View File

@@ -147,6 +147,15 @@ function ARMYGROUP:SetSpeedCruise(Speed)
end end
--- Get coordinate of the closest road.
-- @param #ARMYGROUP self
-- @param #boolean switch If true or nil, patrol until the end of time. If false, go along the waypoints once and stop.
-- @return #ARMYGROUP self
function ARMYGROUP:GetClosestRoad()
return self:GetCoordinate():GetClosestPointToRoad()
end
--- Add a *scheduled* task. --- Add a *scheduled* task.
-- @param #ARMYGROUP self -- @param #ARMYGROUP self
-- @param Core.Point#COORDINATE Coordinate Coordinate of the target. -- @param Core.Point#COORDINATE Coordinate Coordinate of the target.