Graph::Easy - Manual

A*

If you haven't done so, please read the Overview first.

For a general overview of the A* algorithm please read Amit J. Patel's great page and then come back here.

The Good and The Bad Paths

The pathfinding algorithm has the goal to find a path between two nodes. It considers each starting port and each ending port and needs to return the best possible path. Here is a list of things that are considered good:

The best possible path goes straight and occupies only one cell.

Multi-celled Nodes

Consider the following two placed nodes with their appropriate ports:

...........................................
:     :     :     :     :     :     :     :
: 0,0 :North:North: 3,0 : 4,0 : 5,0 : 6,0 :
:     :     :     :     :     :     :     :
...........................................
:     :+---------+:     :     :     :     :
:West :|    A    |: East: 4,1 : 5,1 : 6,1 :
:     :+---------+:     :     :     :     :
...........................................
:     :     :     :     :     :     :     :
: 0,2 :South:South: 3,2 : 4,2 : 5,2 : 6,2 :
:     :     :     :     :     :     :     :
...........................................
:     :     :     :     :     :     :     :
: 0,3 : 1,3 : 2,3 : 3,3 : 4,3 : 5,3 : 6,3 :
:     :     :     :     :     :     :     :
...........................................
:     :     :     :     :     :     :     :
: 0,4 : 1,4 : 2,4 :North:North: 3,4 : 4,4 :
:     :     :     :     :     :     :     :
...........................................
:     :     :     :+---------+:     :     :
: 0,5 : 1,5 :West :|         |: East: 4,5 :
:     :     :     :|         |:     :     :
...................|    B    |.............
:     :     :     :|         |:     :     :
: 0,6 : 1,6 :West :|         |: East: 4,6 :
:     :     :     :+---------+:     :     :
...........................................
:     :     :     :     :     :     :     :
: 0,7 : 1,7 : 2,7 :South:South: 6,7 : 7,7 :
:     :     :     :     :     :     :     :
...........................................

There exist two optimal paths from A to B, one from 3,1 to 3,4, and the other one from 2,2 to 2,5. However, this holds only if there are no obstacles along these paths. With obstacles, every combination of each starting port and each ending port would need to be examined. This are 2+1+2+1=6 x 2+2+2+2=8 == 48 possible simple paths. Clearly just trying each of them would not scale very well. Just consider what happens if you have two nodes, each with 20 ports...

One of the strengths of the general A* algorithm is that it supports with ease multiple start- and endpoints.

Multiple Startpoints

Multiple start points are very easy to implement, instead of adding a single start point to the list of OPEN nodes, we add all possible starting points. We also apply a very small tie-breaker so that the algorithm favours a certain direction in case of ties.

In the example above, the algorithm would explore both equally likely paths at the same time, choosing the one that hits (arbitrarily) one end port first.
With the tie-breaker, it will follow only one of the paths (f.i. East, then South), until it either hits the endpoint, or finds an obstacle.

Multiple Endpoints

Multiple endpoints are easily implemented, too. Instead of stopping the algorithm when we hit the endpoint, we watch for all of them simultanously.
In addition, instead of calculating the distance from the current node to the endpoint, we calculate the distance to each endpoint, and then use the smallest one.

Thus the algorithm will automatically gravitate towards the nearest port, but still work around obstacles just fine.

Crossings

The pathfinding will also find crossings. However, since edge crossings are considered bad, each crossing bears a heavy penalty. The penalty is so high that if a possible path around the edge without a crossing exists, it will be found. However, the penalty is not so high as that the algorithm searches endless for a way around that does not exist.

Stopping

The algorithm will stop when it reaches one of the possible endpoints. However, if the path to the goal is blocked on all sides, it will run into trouble. The reason is that our layout plane is essential infinitely big, and the algorithm would spiral outwards and out of control, never finishing.

The current solution is to stop the algorithm after a pre-determined number of steps have been taken. These are currently 10,000 steps.
It should be possible for the code to watch when the runaway situation occurs and abort the pathfinding. That would enable us to get rid of a hard-coded constant (which are always bad) and allow much earlier aborting then having to wait for all steps to finish.

A* Map

Below is a diagnostic output of the A* algorithm finding a path from a one-cell node to a 2x2 celled node, which has a few ports blocked. Empty cells are lightgrey, blocked darkgrey, and nodes are white. The lime to orange cells were still in the to-be-done phase when the algorithm finished, the purple-blue-teal fields were already considered while finding the path. The found path (clearly not optimal, since it has too many bends) is outlined in white.

Nodes examined: 168
Nodes still to do (open): 50
Nodes in path: 21

                                                                                                        
                                                                                                        
                                                                                                        
                                                                                                        
                          2  3  5                 9  10  11       13  14                    
                     3 1+22 2+21 4+20  5  6  7 8+16 9+15 10+14  11 12+11 13+10  15               
                3 1+22 0+18 1+20 3+19 4+18 5+17 6+16 7+15 8+14 9+13 10+12 11+10 12+9  14  14          
           2 1+22 0+18      0+16 1+18 2+17 3+16 4+15 5+14 6+13 7+12 8+11 9+9 10+8 11+10 12+11  13     
           3 2+21 1+20 0+16 1+18 3+17 4+16 5+15 6+14 7+13 8+12 9+11 10+10 11+8 12+7 14+9  15          
           5 4+20 3+19 1+18 3+17 7+16 8+15 6+14 8+13 11+12 9+11 10+10 14+9 12+7 13+6 15+8  16          
           6 5+19 4+18 2+17 4+16 5+15 9+14 7+13 9+12 12+11 10+10 11+9 15+8 13+6 14+5 16+7  17          
           7 6+18 5+17 3+16 5+15 9+14 10+13 11+12 12+11 13+10 11+9 12+8 16+7 14+5 15+4 17+6  18          
           8 7+17 6+16 4+15 6+14 7+13 8+12 9+11 10+10 14+9 12+8 13+7 17+6 15+4 16+3 18+5  19          
           9 8+16 7+15 5+14 7+13 9+12 10+11 11+10 12+9 15+8 13+7 14+6           17+2 19+4  20          
           10 9+15 8+14 6+13 8+12 12+11 13+10 14+9 15+8 16+7 17+6                     21+3  23          
           11 10+13 9+12 7+11 9+10 10+9 11+8 12+7 13+6 14+5 15+4                24+0 22+1  24          
           12 11+12 10+11 8+10 10+9 11+8 12+7 13+6 14+5 15+4 16+3 17+2                 23               
                13  11 9+12 12+11 13+10 14+9 15+8 16+7 17+6 18+5 19+4  21                              
                     12 10+13  12  14  15  16  17  18  19  20                                   
                          11