I wrote this A* Shortest Path algorithm using a Priority Queue which works when I map nodes a certain way, but not when I do it another way. Here is the base code for the algorithm:
My heuristic is the distance from the node to the goal node.
-- Example node = {v = "N0", c = {}} -- v is the node value, also the name -- c is the children/neighbors of the node in a table function aStarSearch(start, goal) local queue = {} local visited = {} local function testVisited(node) for i,v in pairs(visited) do if v == node then return true end end return false end start.d = 0 table.insert(queue, start) local current = nil while #queue > 0 do current = queue[1] table.remove(queue, 1) if not testVisited(current) then table.insert(visited, current) print(current.v, goal.v) if current.v == goal.v then table.insert(path, current) return path end local neighbors = current.c for i,v in pairs(neighbors) do if not testVisited(v) then local predictedDistance = (parts[v.v].Position - parts[goal.v].Position).magnitude local neighborDistance = (parts[current.v].Position - parts[v.v].Position).magnitude local totalDistance = (parts[current.v].Position - parts[start.v].Position).magnitude + neighborDistance + predictedDistance if predictedDistance < totalDistance then --print(current.v, v.v) if #current.c > 0 then table.insert(path, current) end table.insert(queue, v) end end end end end end
The current way it get's the shortest path is by adding nodes to a path variable here:
if predictedDistance < totalDistance then --print(current.v, v.v) if #current.c > 0 then table.insert(path, current) end table.insert(queue, v) end
This works for when no node has the same neighbor as any other node, or in visual form, this:
I already know the issue with this is that if it takes a wrong path and has to backtrack, it has already added the wrong nodes to the path variable.
This happens 95% of the time when I add loops:
This is how I map the nodes initially:
for i = 1,10 do local newNode = createNode("N"..i, {}) if i == 1 then addChild(findNode(start.v), newNode) else addChild(findNode(lastNode.v), newNode) local r = math.random(1,2) if r == 1 then lastNode = newNode end end end
And if I want to add loops I do this:
for i,v in pairs(nodes) do local r = math.random(1,4) local n = math.random(1,10) while n == i do n = math.random(1,10) end addChild(findNode(v.v), nodes[n]) end
So in short my question is, how do I return the CORRECT path and not include any nodes that perhaps ended in a dead end, or were wrong for another way? Perhaps the way I wrote the algorithm is incorrect, which it very well might be since this is my first A* algorithm, so if you see any weird things I do please point them out.
What you want to construct is a list that maps a node to the best node to reach it from. Such value might be getting replaced while the algorithm is running, so you then reconstruct the path from the list after the algorithm has finished running.
Currently you are missing this step so you get stuck because there is no way to replace a node with a better guess. Instead of inserting the current node to the path table, insert it to a hash table where the neighbor is the key and the current node the value, then you can reconstruct the path iterating that table.
Essentially
if predictedDistance < totalDistance then --print(current.v, v.v) if #current.c > 0 then path[v] = current end table.insert(queue, v) end --Then reconstruct after returning path function reconstructPath(path, goal) local path = {} local current = goal repeat table.insert(path, 1, current) current = path[current] until not current end
Since path[current] gives the best node to reach current from. You start at the end, the goal, and work your way up.