Roy Triesscheijn’s Weblog

My programming world

Another faster version of A* (2D+3D) in C#

Posted by Roy Triesscheijn on 24th September 2011

As you might know I once wrote an A* sample for C# more than 2 years ago. The first version worked but was very slow because of a bug. The second version was faster, and a third version (made by one of the readers of this blog) was even faster.

Btw did you know that the excellent game Dysomnia shipped with (a modified) version of my C#/A* code? Ok it’s just a few lines to get people started but it’s really cool to see how people use this little start for their cool projects!

Anyway, back on topic: during my internship at Nixxes an interested co-worker (thanks Marcel!) tweaked the heuristic, removed some unnecessary checks, and added a faster way to check if a tile was processed yet.

There is not much else to say, it’s still A*, it supports 2D and 3D, it uses a MinHeap and is faster than ever! (Note: this is a pure C# solution, so it doesn’t require XNA, but it will work nicely with XNA even on WP7 and the Xbox360).

Download the latest version here

Older relevant blog posts about A*  (from older to newer)
http://roy-t.nl/index.php/2009/02/24/implementing-a-path-finding-in-c-and-xna-source-code-can-we-cut-the-corner/
http://roy-t.nl/index.php/2009/07/07/new-version-a-pathfinding-in-3d/
http://roy-t.nl/index.php/2010/06/27/more-a-improvements/

Tags: , , , , , ,
Posted in Blog, General Coding, General Gamedesign, Tips, XNA | 7 Comments »

More A* improvements

Posted by Roy Triesscheijn on 27th June 2010

As you might know I’ve written quite a lot of articles on the A* algorithm.

This week I was contacted by Roman Kazakov (fourfor[AT]hotmail.com). He had a look at my previous A* articles and came up with a number of ways to speed up my latest version even more! Unfortunately he doesn’t have a blog, so he asked me to publish his enhancements here, so that everyone can use them. So I’ll forego the rest of this introduction and let the man speak.

(Note: the following quote is the last unedited part of an e-mail of Roman Kazakov).

Hi Roy, my name is Roman Kazakov and I am Russian guy, but live in Latin America. My Spanish is perfect, but my English is “so so”, I hope you understand me. :) I am not a professional programmer. This is just my hobby from university. And as “free lancer” I work with VS 2008 C# + DevXpress + SQL Server 2008.
I would like to present you my optimization for you program AStar. Oh, by the way, my game is 2D, and this example is in 2D dimension, but this version will by work and 3D too and more faster.
Here is code of FindPathReversed:

 private static BreadCrumb3 FindPathReversed5Final(bool[,] worldBlocked, Point2D start, Point2D end)
        {
            // Here we dont use the class Poin2D. It is bad idea use Class just for two int values (X Y)
            List<BreadCrumb3> openList2 = new List<BreadCrumb3>(256);
            BreadCrumb3[,] brWorld = new BreadCrumb3[worldBlocked.GetLength(0), worldBlocked.GetLength(1)];
            BreadCrumb3 node;
            int t1, t2;
            int tmpX, tmpY;
            int cost;
            int diff;
            t1 = worldBlocked.GetLength(0);
            t2 = worldBlocked.GetLength(1);
            BreadCrumb3 current = new BreadCrumb3(start.X, start.Y);
            current.cost = 0;

            BreadCrumb3 finish = new BreadCrumb3(end.X, end.Y);
            brWorld[current.X, current.Y] = current;
            openList2.Add(current);

            while (openList2.Count > 0)
            {
                //Find best item and switch it to the 'closedList'
                current = openList2[0];
                openList2.RemoveAt(0);
                int position = 0;
                cost = openList2.Count;
                do
                {
                    int left = ((position << 1) + 1);
                    int right = left + 1;
                    int minPosition;

                    if (left < cost && openList2[left].cost < openList2[position].cost)
                    {
                        minPosition = left;
                    }
                    else
                    {
                        minPosition = position;
                    }

                    if (right < cost && openList2[right].cost < openList2[minPosition].cost)
                    {
                        minPosition = right;
                    }

                    if (minPosition != position)
                    {
                        node = openList2[position];
                        openList2[position] = openList2[minPosition];
                        openList2[minPosition] = node;
                        position = minPosition;
                    }
                    else
                    {
                        break;
                    }

                } while (true);

                current.onClosedList = true;

                //Find neighbours
                for (int i = 0; i < 8; i++)
                {
                    tmpX = current.X + surrounding2[i, 0];
                    tmpY = current.Y + surrounding2[i, 1];
                 //   tmp = current.position + surrounding[i]; //This is is a really slow!!!
                    /*if (tmp.X >= 0 && tmp.X < t1 &&
                tmp.Y >= 0 && tmp.Y < t2 &&
                !worldBlocked[tmp.X, tmp.Y])*/
                    if (tmpX >= 0 && tmpX < t1 &&
             tmpY >= 0 && tmpY < t2 &&
             !worldBlocked[tmpX, tmpY])
                    {
                        //Check if we've already examined a neighbour, if not create a new node for it.
                        if (brWorld[tmpX, tmpY] == null)
                        {
                          //  node = new BreadCrumb3(tmpX,tmpY);
                           // brWorld[tmpX, tmpY] = node;
                            brWorld[tmpX, tmpY] = node = new BreadCrumb3(tmpX, tmpY); // This is more fast!
                        }
                        else
                        {
                            node = brWorld[tmpX, tmpY];
                        }

                        //If the node is not on the 'closedList' check it's new score, keep the best
                        if (!node.onClosedList)
                        {
                            diff = 0;
                            if (current.X != node.X)
                            {
                                diff += 1;
                            }
                            if (current.Y != node.Y)
                            {
                                diff += 1;
                            }
                            cost = current.cost + diff + ((node.X - end.X) * (node.X - end.X)) + ((node.Y - end.Y) * (node.Y - end.Y));//node.position.GetDistanceSquared(end);

                            if (cost < node.cost)
                            {
                                node.cost = cost;
                                node.next = current;
                            }

                            //If the node wasn't on the openList yet, add it
                            if (!node.onOpenList)
                            {
                                //Check to see if we're done
                               // if (node.Equals(finish)) // This is slow too!!!
                                if (node.X == finish.X && node.Y == finish.Y)
                                {
                                    node.next = current;
                                    return node;
                                }
                                node.onOpenList = true;
                                openList2.Add(node);

                                int position2 = openList2.Count - 1;

                                int parentPosition = ((position2 - 1) >> 1);

                                while (position2 > 0 && openList2[parentPosition].cost > openList2[position2].cost)
                                {
                                    node = openList2[position2];
                                    openList2[position2] = openList2[parentPosition];
                                    openList2[parentPosition] = node;
                                    position2 = parentPosition;
                                    parentPosition = ((position2 - 1) >> 1);
                                }

                            }
                        }
                    }
                }
            }
            return null; //no path found
        }

Comments:
Look in my screenshots which i made it for you. For looking performance I used ANTS Performance Profiler 5.2. This is a great “stuff”!
Also you can use your benchmark. Check the file zip attached. There is all source and information for compiled.
Comments about code:
The general “thing” in programming in C# is do not use Obj Class and etc. where really dont need to use that! The best performance is just “Value type” and List<>
Array, maybe Struct.
In my optimized code I deleting class MinHeap where T : IComparable. Why?! Because is really slow, and I dont know why. Also use class Point2D just for 2(3) types X and Y( and Z), it was a bad idea. And many thing more. Looked my code and you understand which changes I made it. I hope this is help you. And please put this optimization on you wonderful web page http://roy-t.nl, as New Optimized Version A*.
When we made the games, we looking for more faster algorithm and code. This article will be help a many people as us.
We made the games of our dreams…

Roman also included a couple of pictures to show the speed gain of his code
Original code (by me)

New implementations (by Roman)

Tags: , , , , , ,
Posted in Blog, General Gamedesign, XNA | 3 Comments »

Collision avoidance, path finding and smooth following, 3D snippet (C#/XNA)

Posted by Roy Triesscheijn on 13th January 2010

Today I converted my “Swarm Like collision avoidance, path finding and smooth following”- project (what a mouth full) to work with 3D coordinates, if you understood the code, you would know this wasn’t much of a problem. Anyway I did it for you today :) . (original article here).

public static void Attract(GameTime gameTime, Object3D ship, Object3D[] obstacles, Object3D target)
        {
            float pullDistance = Vector3.Distance(target.Position, ship.Position);

            //Only do something if we are not already there
            if (pullDistance > ((ship.Radius + target.Radius) * 1.5f))
            {
                Vector3 pull = (target.Position - ship.Position) * (1 / pullDistance); //the target tries to 'pull us in'
                Vector3 totalPush = Vector3.Zero;

                int contenders = 0;
                for (int i = 0; i < obstacles.Length; ++i)
                {

                    //draw a vector from the obstacle to the ship, that 'pushes the ship away'
                    Vector3 push = ship.Position - obstacles[i].Position;

                    //calculate how much we are pushed away from this obstacle, the closer, the more push
                    float distance = (Vector3.Distance(ship.Position, obstacles[i].Position) - obstacles[i].Radius) - ship.Radius;
                    //only use push force if this object is close enough such that an effect is needed
                    if (distance < ship.Radius * 3)
                    {
                        ++contenders; //count that this object is actively pushing

                        if (distance < 0.0001f) //prevent division by zero errors and extreme pushes
                        {
                            distance = 0.0001f;
                        }
                        float weight = 1 / distance;
                        totalPush += push * weight;
                    }
                }

                pull *= Math.Max(1, 4 * contenders); //4 * contenders gives the pull enough force to pull stuff trough (tweak this setting for your game!)
                pull += totalPush;

                //Normalize the vector so that we get a vector that points in a certain direction, which we van multiply by our desired speed
                pull.Normalize();
                //Set the ships new position;
                ship.Position += (pull * ship.Speed) * (float)gameTime.ElapsedGameTime.TotalSeconds;
            }
        }

Tags: , , , , , , , , ,
Posted in Blog, General Gamedesign, XNA | 6 Comments »

Swarm Like collision avoidance, path finding and smooth following

Posted by Roy Triesscheijn on 29th December 2009

Intro

There are many different algorithms for finding paths, for avoiding collision and for smoothly following a path.  A normal approach in game would be the following:

  • Construct an algorithm that detects objects that are on our path and calculates new routes around them.
    • (Maybe some ‘look ahead’ and A* again, question: do you calculate from the
      obstruction to the finish, or to the next free tile, or both?)

Although this looks like a lot of work, most of these algorithms are freely available, how to implement them is well know, and most of these algorithms are reasonably fast.  So for a little bit of work we get a solid, well known, always working, pretty fast algorithm that in a pretty constant world finds nice paths.

Problem

For my current project I’m trying to emulate a galaxy (no small taskJ) ships move from planet to planet, but these planets are not static, they are moving in ellipses around a sun. Calculating the paths ships have to take would be troublesome, because paths close the planets would constantly change, and even more important, once we get to the finish line the planet already moved a bit further. We could update our path every x-amount of time, but this would give our pour CPU too much to do, and would give us a strange and jerky path.

Solution

After a bit of whining on #XNA (see the links section) someone (I wish I remembered who, so I could give credit where it’s due) suggested taking a look at swarming algorithms. (Here computer models try to simulate how swarms of birds or fish interact with each other so that they don’t collide and keep following their leaders). Although I didn’t directly see a connection, I was interested in how these models kept birds from colliding with each other. Apparently when a bird ‘senses’ that another bird comes too close it tries to move in a direction so that the minimum space between them is restored, however it also tries to keep follow his targets, the result is a new direction vector  where the evasion and the following are calculated and weighted in. When a collision is imminent the weight of the evasion grows and when there is no danger at all the weight of evasion drops.

Another way to look at it is like a set of magnets. The bird is a little iron ball, the obstacles are negatively charged and try to push the ball away, while the finish is positively charged and tries to reel the ball in. Can you see it in your head now?

After I realized this it was pretty simple to create some code that produced the following video:

And now with moving obstacles:

As you can see the path is fairly smooth, nothing is hit, and we reach our end goal. All I had to do was write these 50 lines of code (including comments and white space)

 //Simple class that represents 2D objects
public class Object2D
{
    public Vector2 Position;
    public float Radius;
    public Color Color;
    public float MetersPerSecond;
}
        //All we need is this static method, here generically called update.
        public static void Update(GameTime gameTime, Object2D ship, Object2D[] obstacles, Object2D target)
        {
            float pullDistance = Vector2.Distance(target.Position, ship.Position);

            //Only do something if we are not already there
            if (pullDistance > 1)
            {
                Vector2 pull = (target.Position - ship.Position) * (1 /  pullDistance); //the target tries to 'pull us in'
                Vector2 totalPush = Vector2.Zero;

                int contenders = 0;
                for (int i = 0; i < obstacles.Length; ++i)
                {

                    //draw a vector from the obstacle to the ship, that 'pushes the ship away'
                    Vector2 push = ship.Position - obstacles[i].Position;

                    //calculate how much we are pushed away from this obstacle, the closer, the more push
                    float distance = (Vector2.Distance(ship.Position, obstacles[i].Position) - obstacles[i].Radius) - ship.Radius ;
                    //only use push force if this object is close enough such that an effect is needed
                    if (distance < ship.Radius * 3)
                    {
                        ++contenders; //note that this object is actively pushing

                        if (distance < 0.0001f) //prevent division by zero errors and extreme pushes
                        {
                            distance = 0.0001f;
                        }
                        float weight = 1 / distance;

                        totalPush += push * weight;
                    }
                }

                pull *= Math.Max(1, 4 * contenders); //4 * contenders gives the pull enough force to pull stuff trough (tweak this setting for your game!)
                pull += totalPush;

                //Normalize the vector so that we get a vector that points in a certain direction, which we van multiply by our desired speed
                pull.Normalize();
                //Set the ships new position;
                ship.Position += (pull * ship.MetersPerSecond) * (float)gameTime.ElapsedGameTime.TotalSeconds;
            }
        }

As you can see that is very little and simple code for path finding, collision avoidance and smooth following. However there are some drawbacks.

Take a note

Of course there is a good reason why not everyone is using this. The path finding is not optimal (with many objects the path is not always the shortest), there is a chance to get stuck if the finish is hard to reach and this code would fail horribly in mazes. However in space there are not many objects to avoid, it’s hard to get stuck and the paths might not be optimal, but after dodging a planet or two we can go to our target in a pretty much straight line. Other scenarios where it might be interesting to try this out is in racing game AI’s (where the target is for example a dot moving across the ideal line, a meter ahead of the car) where I think this would make a very interesting algorithm. (Of course you need a braking algorithm as well, maybe adjust throttle/braking on the change in direction compared to the current speed?)  Another genre might be surfing games, strategy games with little obstacles, or other ‘stupid’ AIs, like a pet AI for cute pets like in world of warcraft.

Conclusion

We made a simple and fun algorithm with much potential, when used wisely this algorithm can make your games code a lot easier. Take note of the drawbacks though, A* is not perfect in all situations (else we would’ve used A*) and this algorithm should only be used when it is appropriate. (If you have trouble deciding this please post a comment, I’ll try to help out, however sometimes it’s just a matter of experimenting).

Oh btw, after some browsing I found some fun alikeness  with steering behavior, although that does work a bit differently the same concept lays at the basics. You can find out more about steering behavior here: http://www.red3d.com/cwr/steer/

Tags: , , , , , , , ,
Posted in Blog, General Gamedesign, XNA | 5 Comments »

New Version A* pathfinding in 3D

Posted by Roy Triesscheijn on 7th July 2009

Intro and thanks.

Ok so let’s quickly forget the debacle around the first release of this code sample, I wan’t to delete the entire post about that one but I would’nt  have know that I made an error (until much later on) if it wasn’t for some people spotting it instantly! So I would first like to thank some of my visitors who spotted the errors:

-Oscar for spotting possible performance problems.

-Ryan for suggesting possible HashSet’s (altough I didn’t use them in the end, see below why).

I would also like to thank posters from www.tweakers.net  who helped me get this version of A* so fast, especially (on post order):

-Phyxion & Mammon for their one line version of Equals()

-Nick The Heazk for spotting that I totally forgot the Heuristic part for scoring (doh, I swear I had it in my draft)

-pedorus &  jip_86 for suggesting to use a Binary(Min)Heap

-Marcj & NotPingu for pointing out a 3D version of Pythagoras

-Soultaker for letting me think more about efficiency of containers (O(?) of add, extract and contains))

-.oisyn & pedorus for suggesting to combine a binary heap and a HashSet (altough I didn’t use it in the end, see why below)

-PrisonerOfPain for suggesting to use binary flags instead of extra HashSet’s (that’s why I didn’t use them).

Why is this version faster?

A lot was changed from the previous version, let’s start with the openList, checking if an item is in the openList costs O(n) iterations and checking which item has the lowest score costs O(n) and removing an item costs O(log(n)). So I’ve replaced the openList with a BinaryHeap for checking the lowest score and removing/adding. These operations cost O(1), O(log(n)). That’s allot faster already.

As for checking if an item is in an openList (or closedList) all I did was add a boolean flag to the BreadCrumb class (onOpenList and onClosedList).  Checking this flagg is O(1) so that really makes checks allot faster.

Also all I needed the closedList for was checking if items where already in there, so with the boolean flag I could completely remove the closedList.

Another new feature is that we immediatly return from the FindPath function when we find the finish, this also saves some operations.

I also made sure that I don’t  create to much new objects but re-use them after they where first indexed (this was  also the cause for a small ‘score’ bug) and I’ve re-aranged some ifs.

For the algorithm itself I now use DistanceSquared for the (forgotten) Heuristic part of the scoring which is allot better than ManHattan distance which is slightly biased. I’ve also changed the cost (G) to move from the current node to the next node to incorporate these squared cost  (so instead of 1,  1.4 and 1.7 I can now use 1,2,3 for these scores. I also don’t have to multiply all these numbers by 10 since 1, 2 and 3 are integers.

A final additon is the class Point3D which allows us to use only Integers instead of floats (which are slower for a cpu).

All in all this made this code about 200x faster (yes really!). But that is not completely fair since the first code was broken. If you count from after I fixed the heuristic part of the code the code is ‘only’ about 30x faster.

Benchmark results.

And before we get to the exciting part (the code!).  Let’s first show some benchmarks.

Situation:

World: 10x10x10

Start: 0,0,0

End: 9,5,8

Nodes blocked: 300

Time:

Total (100 iterations) : 134ms

Average:  1.34ms

Lowest:   < 1ms

Highest:  17ms

Note: as you can see because we did this test 100 times there is quite allot off difference between the lowest and highest time we needed to calculate this route, that’s because sometimes the cpu stops executing this program, and starts handleing an irq or doing something else, that’s why it’s important to always take a big number of benches to be representative. We can see from the average that this code is extremely fast.

Efficiency:

A* is an extremely efficient algorithm, consider we would’ve liked to brute-force this problem instead of using A*. That would’ve cost us 10^10^10 = 1,e+100 iterations. However with an (this) A* implementation we only needed 119 iterations (in which we checked 3094 nodes).

So, on to the code!

The code.

Well since I’ve divided every thing in much neater classes it’s a bit of a problem adding showing off all these as code-listings as I usually do. So I will only show the most important method here, the rest of the classes, and an example  you can download at the bottom of this article or by clicking here

        /// <summary>
        /// Method that switfly finds the best path from start to end. Doesn't reverse outcome
        /// </summary>
        /// <returns>The end breadcrump where each .next is a step back)</returns>
        private static BreadCrumb FindPathReversed(World world, Point3D start, Point3D end)
        {
            MinHeap<breadCrumb> openList = new MinHeap<breadCrumb>(256);
            BreadCrumb[, ,] brWorld = new BreadCrumb[world.Right, world.Top, world.Back];
            BreadCrumb node;
            Point3D tmp;
            int cost;
            int diff;

            BreadCrumb current = new BreadCrumb(start);
            current.cost = 0;

            BreadCrumb finish = new BreadCrumb(end);
            brWorld[current.position.X, current.position.Y, current.position.Z] = current;
            openList.Add(current);

            while (openList.Count > 0)
            {
                //Find best item and switch it to the 'closedList'
                current = openList.ExtractFirst();
                current.onClosedList = true;

                //Find neighbours
                for (int i = 0; i < surrounding.Length; i++)
                {
                    tmp = current.position + surrounding[i];
                    if (world.PositionIsFree(tmp))
                    {
                        //Check if we've already examined a neighbour, if not create a new node for it.
                        if (brWorld[tmp.X, tmp.Y, tmp.Z] == null)
                        {
                            node = new BreadCrumb(tmp);
                            brWorld[tmp.X, tmp.Y, tmp.Z] = node;
                        }
                        else
                        {
                            node = brWorld[tmp.X, tmp.Y, tmp.Z];
                        }

                        //If the node is not on the 'closedList' check it's new score, keep the best
                        if (!node.onClosedList)
                        {
                            diff = 0;
                            if (current.position.X != node.position.X)
                            {
                                diff += 1;
                            }
                            if (current.position.Y != node.position.Y)
                            {
                                diff += 1;
                            }
                            if (current.position.Z != node.position.Z)
                            {
                                diff += 1;
                            }
                            cost = current.cost + diff + node.position.GetDistanceSquared(end);

                            if (cost < node.cost)
                            {
                                node.cost = cost;
                                node.next = current;
                            }

                            //If the node wasn't on the openList yet, add it
                            if (!node.onOpenList)
                            {
                                //Check to see if we're done
                                if (node.Equals(finish))
                                {
                                    node.next = current;
                                    return node;
                                }
                                node.onOpenList = true;
                                openList.Add(node);
                            }
                        }
                    }
                }
            }
            return null; //no path found
        }

(note the class also has a normal FindPath() method that switches start and end for you).

Download.

In case you missed the download link in the middle of the text, download the entire example:  here

kick it on GameDevKicks.com

Tags: , , , , , , , , , ,
Posted in General Gamedesign, XNA | 26 Comments »

A* 3D update

Posted by Roy Triesscheijn on 7th July 2009

Just a small update so to keep you all who’ve been anxiously waiting for the updated version of A* happy :) .

I’ve been working on implementing A* more efficiently using a MinHeap for storage instead of Lists<> add this time I made sure that I didn’t forget the H (heuristic) part of the scoring (silly me). To give you a bit of an idea how much better the algorithm is now.

Situation 10x10x10 grid, 1/3 of the nodes blocked, find a path from 0,0,0 to 9,5,8

Old code: +-600ms (If I’d just have profiled it before I would’ve known something was wrong)

New code: 8.86ms 6.82ms 5.27ms averaged over 100 runs.

So yeah that is allot better, however when decreasing the number of blocked nodes the time goes up slightly (average 19ms for 0 nodes blocked). And I really want to get that lower, so I’ve asked some people at http://gathering.tweakers.net if they saw more room for optimalisation and there are still a few tips that I haven’t explored there. So I’ll be releasing the new (correct/fast) source code soon, but not untill I’ve made sure that I’ve cramped out all the speed that I can.

There is atleast one method that will speed everything up significantly. A player can now make a step over 3 axis at the same time (x,y,z) ofcourse that is more expensive but sometimes gives a better path, however this means that each node has to consider 26 neighbours instead of just 18. I’m playing with the idea to restrict motion a bit for a faster algorithm.

Maybe I’ll build in some different motion types for speed, best path, or some average.

Tags: , , ,
Posted in General Coding, General Gamedesign, XNA | No Comments »

Upcomming A* pathfinding in 2D and 3D code updated

Posted by Roy Triesscheijn on 29th June 2009

So allot of people have commented on my A* tutorial I posted here a while ago, but they’ve also pointed out a few flaws in the design so I’ve been working on a new version.

The new version also incorperates 2D AND 3D worlds so you can use this class for both, I’ve also optimized a bunch, made everything allot more clear replaced those dodgy ints with nice structs and fixed an unseen bug.

I hope to be able to get it online tomorrow, there’s just to much to polish atm to post it right now, but I’ll keep you guys updated!

Tags: , , , , , ,
Posted in General Gamedesign, XNA | 2 Comments »

Implementing A* path finding in C# (and XNA): source-code (can we cut the corner?)

Posted by Roy Triesscheijn on 24th February 2009

Update: there is a new fully 2D and 3D version of this article now available that fixes many issues some of you where having, and having a much faster and more readable codebase, get it here!

Please excuse my grammar in my last post, I was quite tired and well, let’s not talk about it anymore :-) .

Anyway, as I’ve said I’ve been working on an A* path finding algorithm in C# for one of my XNA projects. I’ve cleaned up the garbage and refactored the algorithm into one nice .cs file (2classes)

Today I will give a short explanation of the A* path finding algorithm and my implementation of it, specifically the extra point about cutting corners. You can download the source code at the end of this article. The source-code can be used in any C# project, and doesn’t use specific XNA classes. (All it really uses are Point’s, generic Lists and a couple of ints and bools).

Note: For more in depth information check out the following links

- http://www.policyalmanac.org/games/aStarTutorial.htm

- http://en.wikipedia.org/wiki/A*_search_algorithm

- http://en.wikipedia.org/wiki/Dijkstra’s_algorithm (A* is an extension on Dijkstra’s algorithm. (You could view Dijkstra’s algorithm as A* where H is always 0, more about H later))

Note 2: I will use the terms square and node interchangeable in this article because in my A* implementation my nodes are square, however you can use A* for any kind of shapes for the node, and my code is easily adjustable to accommodate that.

A* generally works the following way  (source: Patrick Lester from policyalmanac.org )

1) Add the starting square (or node) to the open list.

2) Repeat the following:

a) Look for the lowest F cost square on the open list. We refer to this as the current square.

b) Switch it to the closed list.

c) For each of the 8 squares adjacent to this current square …

- If it is not walkable or if it is on the closed list, ignore it. Otherwise do the following.

- If it isn’t on the open list, add it to the open list. Make the current square the parent of this square. Record the F, G, and H costs of the square.

- If it is on the open list already, check to see if this path to that square is better, using G cost as the measure. A lower G cost means that this is a better path. If so, change the parent of the square to the current square, and recalculate the G and F scores of the square. If you are keeping your open list sorted by F score, you may need to resort the list to account for the change.

d) Stop when you:

Add the target square to the closed list, in which case the path has been found (see note below), or

Fail to find the target square, and the open list is empty. In this case, there is no path.

3) Save the path. Working backwards from the target square, go from each square to its parent square until you reach the starting square. That is your path.

The costs F which is  G+H might not be evident at first. But is calculated the following way:

G is the movement cost from the start point to that square, and H is the estimated cost from there to the end square.

G is calculated as  TargetSquare.G =  parent.G + 10  or + 14 if the square is diagonal from the parent. (That’s because the square root of 2 is 1.4 and we try to keep the numbers integers here)

H is calculated (in my implementation) as the Manhattan distance from the target to the end.  Which is something like (Math.Abs(G.x – H.x) + Math.Abs(G.y – H.y) ) * 10.

The algorithm keeps checking of squares that are on the open list can be reached cheaper from the current square.

Corner Cutting.

Now about the corner cutting: my implementation adds one new situation before the first point of 2.C.

-If the node is diagonal from the current node check if we can cut the corners of the 2 others nodes we will cross. If so this square is walkable, else it isn’t.

A picture might explain better why this is important:

Art

square A is our current square and we are considering if we can walk 2 square B. Square B’s walkable attribute is set to true, so we might think that we can continue to (now point 2) in c, adding it to the open list etc… However if the object that is going to walk the path is going to get to B, a part of it will be at with red indicated areas of squares C and D.  Imagine square D represents a house, that exactly fills the square, this way our object is going to traverse trough a house! However if squares C and D represents a well, centred in a square filled with grass, we can easily cut the corner to get to B.

The rest of my algorithm isn’t any different than general A*. The code is well commented and all the pitfalls are avoided as much as possible. However why the code works might not be evident if you haven’t studied A* first. I suggest you check the link to policyalmanac.org at the top of this article for a very detailed explanation of A*

Speed.

A* is a very fast algorithm, I’ve tested it on a grid with 64 nodes and the general search time was under 1ms.

Download.

You can download the source code via this link: (My Skydrive)

Don’t be afraid to post your optimizations, notes, questions or other comments here!

kick it on GameDevKicks.com

Tags: , , , , , , , , , , ,
Posted in General Gamedesign, XNA | 33 Comments »

Implementing A* into XNA

Posted by Roy Triesscheijn on 22nd February 2009

Edit: the full article + source-code is now available here.

Most of the day I’ve been toying around with the implementation details of A*.  A* is both easy and hard at the same time, small errors in the function that calculates the cost of each node can really break the algorithm (and especially an ‘<’ instead of an ‘>’ and a faulty initialisation can break it, of which I’m, after a good old debugger session, am now painfully aware.

I’m not ready to post my code yet (it still has a nasty quirk, diagonal tiles are somehow very rarely considered even if you try to go from 0,0 to 5,5 without obstacles where a diagonal path (1,1  2,2 3,3 4,4) would be the fastest.  I think I’ve found the piece of code where it goes wrong though)  But I’ll soon do once I eliminated all the bugs and optimized/refactored the code.

Meanwhile have a look at http://www.policyalmanac.org/games/aStarTutorial.htm it’s a very good beginners tutorial (not tailored to any language) be sure to read it front-to-end before implementing it .

Tags: , , , ,
Posted in Blog, General Gamedesign | No Comments »