Graph::Easy - Manual

Layouter - A* (A-star)

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 (colored yellow), and the other one from 2,2 to 2,5 (colored cyan). 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.

To protect against run-away situations (occuring mainly due to bugs), the algorithm stops after a hard-coded number of steps. This is currently set to 10,000 steps as to not interfere with normal working conditions, and yet stop the algorithm if it runs into a cycle (which should not happen, but experience has shown that some bugs always creep in :)

To prevent the algorithm from spiraling out of control, we define a working space:

.............................
:                           :
:   +-------+               :
:   | Start | ------+       :
:   +-------+       |       :
:                   |       : 
:                   |       :
:                   v       :
:                 +------+  :
:                 | Dest |  :             
:                 +------+  :
:                           : 
.............................

The working space is the rectangle that encompasses all used cells at the start of the algorithm, plus one cell in each direction.
Since it is possible to reach any cell in the outer stripe from any other cell in the same region, the algorithm never needs to leave that working space to find a path. By confining the algorithm into this region (simple dropping all OPEN cells outside this area), we make sure that there is a finite number of cells to check and visit.
When A* runs out of cells to check (OPEN is empty), we know we have reached every possible location reachable from our start position(s), and if no stop position was among them, the target is not reachable at all. The pathfinding thus stops without a result, but after a small finite amount of steps.

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