7

https://vimeo.com/70999946

I implemented a recursive path finding algorithm. This recursive algorithm works based on a pre-set of nodes connected together. Each node has four pointers containing further direction: Top, Button, Left and Right. The recursive algorithm simply walks through each node and looking for each of these four directions one by one toward reaching its final destination; an illustration, consider the following 7 nodes: A, B, C, D, E, F, G, H.

    A (Button->D, Right->B)
    B (Right->C, Left->B)
    C (Left->B)
    D (Button->G, Right->E, Top->A)
    E (Right->F, Left->D)
    F (Left->E)
    G (Right->H, Top->D)
    H (Left->G)

These nodes when comes to an overall view will display the following figure.

A—B—C
|
D—E—F
|
G—H

In this example, suppose the walker started node is Node A and wants to go to Node H as its final destination. Node A looks at its own Right, Button, Left and Top by order; its right pointed to Node B as a result he chooses to go to Node B; Node B in same pattern chooses to go to its right, Node C. When the walker reaches node C; as its Right, Top and Button is blocked, Node C reverts back to Node B. As well node B reverts back Node A. The walker comes back to the start point again. Then Node A goes to its button node base on the order; which means it goes to Node D. Node D goes to its right Node E and then Node F. As Node F is blocked; it goes back to Node E and Node D. Afterwards, Node D chooses to go its button, Node G according to walker order. From there Node G goes to Node H. Finally, the walker reaches its final destination.

Pseudocode: Recursive Path Finding Algorithm
ArrayList findPath(GameObject currentPoint , GameObject targetPoint , ArrayList InputArrayList)
{
1-Duplicate InputArrayList as tempArrayList

2-If the currentPoint equals to target Point return inputArrayList
//*** End Condition found target

3-If the Right side of the currentPoint is empty goto step 4
3.1- Add currentPoint to tempArrayList
//*** Call Right
3.2- tempArrayList = findPath(currentpoint.Right, targetPoint, tempArrayList);
3.3- If tempArrayList is not null return tempArrayList
4-If the Button side of the currentPoint is empty goto step 5
4.1- Add currentPoint to tempArrayList
//*** Call Button
4.2- tempArrayList = findPath(currentpoint.Button, targetPoint, tempArrayList);
4.3- If tempArrayList is not null return tempArrayList
5-If the Left side of the currentPoint is empty goto step 6
5.1- Add currentPoint to tempArrayList
//*** Call Left
5.2- tempArrayList = findPath(currentpoint.Left, targetPoint, tempArrayList);
5.3- If tempArrayList is not null return tempArrayList
6-If the Top side of the currentPoint is empty goto step 7
6.1- Add currentPoint to tempArrayList
//*** Call Top
6.2- tempArrayList = findPath(currentpoint.Top, targetPoint, tempArrayList);
6.3- If tempArrayList is not null return tempArrayList
7-Return null;
//*** End Condition does not found target
}

Note: The actual code is in C#, You can download it from this link.

Rise of problem in the case study: As you understand this code; it has a weakness, to illustrate it; considering the following overall view of nodes, with the assumption that the start node is Node A and the final destination is Node H.

A—B—C
|
D—E—F—I
|   | |
G—H—J—K

Though the best path solution is (A, D, G, H), The explained recursive path finding algorithm finds (A, D, E, F, I, K, J, H) as its solution; this really seems the Robot is a stupid robot :D !

Figure 1: The recursive path finding algorithm enter image description here

Figure 2: The recursive path finding algorithm with the ability to learn enter image description here

I resolved the problem by adding the learning ability for the nodes. You could see from this link the details of issue. But, I would wonder if anybody could revise the recursive algorithm to found the shortest path.

Thank you,

Dan
  • 681
  • 2
  • 11
  • 23
  • 1
    +1 for excellent presentation :) – Kay Jul 25 '13 at 15:10
  • All your recursion needs to work is to actually store a shortest path. Right now it returns the first valid path to target based on your winding order (right, left, top, button (bottom?)). Instead of returning the path when `target` is found, compare currentPath + target to a class member `shortestPath`, if shorter, store as new `shortestPath`. That said, Geoffrey's answer is *way* more generalizable. – Jerdak Jul 25 '13 at 15:36
  • Thanks @Kay, actually I wrote this explanation on my weblog then past it here :D – Dan Jul 25 '13 at 18:10
  • @Jerdak thank for correcting me specially Button :)) . You are right, currently, the recursive algorithm return back the first possible solution. But, I means by bringing the question here, is that how could the recursive algorithm be modified to found all of the possible solution, with or without target device restriction (iPad). – Dan Jul 25 '13 at 18:16

3 Answers3

4

Why not simply compare it to Dijkstra and A* search?

Note by using recursion instead of a loop, you're likely to get a StackOverflow at 1025 recursions.

Geoffrey De Smet
  • 26,223
  • 11
  • 73
  • 120
  • Thanks for Dijkstra and A* search. As you said it is really heavily to implemented such an algorithm. My algorithms is quite simple and light. It is based on first come first service; any direction available base on the order; the walker chose the node of that direction for further investigation. I would like to found any light and recursion solution for it. – Dan Jul 25 '13 at 13:33
  • What I actually meant is: any recursion code can be rewritten to a loop code without changing the actual algorithm. So it behaves exactly the same except that it doesn't throw a stack overflow if it recurses more than 1024 levels deep. – Geoffrey De Smet Jul 25 '13 at 14:31
  • ...at 1024 recursive calls, the stack would be a few kilobytes. I highly doubt you'll get a stackoverflow just from that. – BlueRaja - Danny Pflughoeft Jul 25 '13 at 16:13
  • @GeoffreyDeSmet you are right, even I have faced a stack overflow few times with this simple algorithm. My total number of nodes are around 100. If you have noticed currently my "End Condition found target" is in the 2nd lines. At first I put in it on 7th line and shift the not found target on 8th line. But the algorithm was faced a stack overflow error. In all, I could say there is a no chance at all to implement Dijkstra and A* search for this project. thanks again. – Dan Jul 25 '13 at 18:32
  • 1
    @Daniel you can implement Dijkstra without hitting the stackoverflow problem. Create an unprocessedREcurrsionsQueue, then instead of calling a recursive method, add an item to that queue. Then simply do a while loop until the queue is empty. – Geoffrey De Smet Jul 26 '13 at 12:36
3

You are doing a depth-first search, when what you want to be doing is a breadth-first search. The latter will require a queue rather than recursion. The wiki page does a good job of explaining how to implement it, so I won't repeat that here.

From there, it would not be much work to implement A*, which should speed up your results. That will require a priority-queue rather than a queue; C# does not have a priority-queue in the base library, but, as luck would have it, I am the author of an optimized C# priority-queue implementation meant specifically for use in pathfinding.

Also, since you mentioned Unity, I'll point out that there are a number of pathfinding libraries built especially for Unity. This is probably the safest route, as efficient pathfinding in video games is non-trivial.

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • Wow, Thanks man, the explained algorithm is exactly depth-first search. I really need to think about your resolution, the only thing I understand here is I do not have any knowledge in the 4 different kind of algorithms that represented here :D !!!!? ( Dijkstra, A* search, depth-first search, breadth-first search) – Dan Jul 25 '13 at 19:05
  • Is it correct Breadth-first_search can not be written in recursion? – Dan Jul 25 '13 at 19:11
  • @Danial: BFS, Dijkstra's, and A\* are largely similar. See [this](http://gamedev.stackexchange.com/questions/56041) and [this](http://gamedev.stackexchange.com/questions/58800) question for more info. In response to your second question: You should use a queue to implement BFS. – BlueRaja - Danny Pflughoeft Jul 25 '13 at 19:24
  • I looked at it on glimpse. I think as you mentioned BFS is what I want as a another solution. I think if Dijkstra's and A* are implemented without any doubt I will face the stack overflow issue? am I right? – Dan Jul 25 '13 at 19:32
  • @Danial: No, please read the posts I linked to. BFS and Dijkstra's are the same thing *(at least, on unweighted graphs like yours)*. A\* is just a simple extension to those uses extra information (a "heuristic") to make the algorithm faster. – BlueRaja - Danny Pflughoeft Jul 25 '13 at 20:16
1

Here is a depth-first (quicker to write) version of what you need. I don't recommend this answer to path-finding. BlueRaja and Geoffrey's answers are much more generalizable, stable, and all around better algorithms for path finding. But I wanted to answer the OP's direct question.

To simplify my example, edges have cost/weight 1. The shortest path == path w/ least number of nodes to reach the target (Technically length == |path| - 1 since I'm counting nodes and not edges but whatever, the idea is the same). This code won't handle cycles, that's why other algorithms are a better idea.

public class PathNode {
    public string Id;
    public PathNode[] Children = new PathNode[4];
}

public class PathFinder : MonoBehaviour {
public List<PathNode> shortestPath = null;

/// <summary>
/// Sets shortest path to `candidatePath` if `candidatePath` is shorter
/// or no shortest path yet.
/// </summary>
public void SetShortestPath(List<PathNode> path){
    if(shortestPath == null){
        shortestPath = new List<PathNode>(path);
        return;
    }
    if(path.Count < shortestPath.Count)
        shortestPath = new List<PathNode>(path);
    }

/// <summary>
/// depth-first shortest path
/// </summary>
public void FindShortestPath(PathNode target, List<PathNode> path){
    PathNode next = path[path.Count-1]; //get next node from path
    if(target == next){
        SetShortestPath(path);
        return;
    }
    // dfs - iterate children
    foreach (PathNode node in next.Children){
        if(node!=null){
            path.Add(node);             
            FindShortestPath(target,path);
            path.Remove(node);          
        }
    }
}
public PathNode ExampleEdgeCreation(){
    PathNode a = new PathNode{Id="A"};
    a.Children[(int)Direction.Left] = new PathNode{Id="B"};
    return a;
}
}

Assume that PathNode.Children[0] == PathNode.Children[Left] etc. I enumerated them in code but I wanted to keep this example small for SO.

Jerdak
  • 3,997
  • 1
  • 24
  • 36