Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
5

How do you return nodes from shortest path A* algorithm?

Asked by 5 years ago
Edited 5 years ago

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:

No Loops

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:

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.

0
dam TheluaBanana 946 — 5y
0
this might take a day to answer TheluaBanana 946 — 5y
0
You just wrote math.random() with two parameter, math.random() uses 3 parameters, math.random(startvalue endvalue, breakvalue) shinferno -53 — 5y

1 answer

Log in to vote
5
Answered by
Destrings 406 Moderation Voter
5 years ago
Edited 5 years ago

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.

0
dam TheluaBanana 946 — 5y
0
Took me hours to fix my code, then implement this part of it, thank you. climethestair 1663 — 5y
0
also, can i ask why u decided to create this instead of using the built in pathfinding service? is it not good to use the built in pathfinding server? TheluaBanana 946 — 5y
0
I am pretty sure the built in path finding is faster. Zafirua 1348 — 5y
View all comments (2 more)
0
xd TheluaBanana 946 — 5y
0
@Zafirua it's for a school project actually lol climethestair 1663 — 5y
Ad

Answer this question