Click here to Skip to main content
15,887,294 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
Hi!
I am writing a game in XNA game studio,
it's a 2d game where the AI needs some type of path finding. I have choose to use Dijkstra's algorithm and it seems to work very good, except when the goal is at the opposite part of the map. Then I get some serious performance problem and it can take up to a minute or even worse. (A+ means that you can go in four directions and not like a cross A*.) I guess it's the priority queue that is the bottle neck but I don't know for sure.

The priority queue I use can be find at:
http://blogs.msdn.com/b/ericlippert/archive/2007/10/08/path-finding-using-a-in-c-3-0-part-three.aspx[^]

The map consist of a multi array which have some 'gameObjects' in it.
All 'gameObjects' inheriting the interface IPathSquare which can tell if the position is walkable/free.

Here's the interface.
C#
namespace PathLib
{
    interface IPathSquere
    {
        bool isFree();
    }
}


Here's the path finding code, note that the PriorityQueue comes from the link above.
C#
namespace PathLib
{
    class PathFinding
    {
        private IPathSquere[,] map;

        public Point[] findPath(Point start, Point finish)
        {
            // are the points within the map's bounds
            if (!isInMap(start) || !isInMap(finish))
                return null;

            // create priority queue
            PriorityQueue<int, LinkedList<Point>> pq = 
                                new PriorityQueue<int, LinkedList<Point>>();
            
            // setup a init list
            LinkedList<Point> lst = new LinkedList<Point>();
            lst.AddLast(start);
            pq.Enqueue(1, lst); // the priority value doesn't really matter here

            // find path, loop while 'pq' isn't empty
            while (!pq.IsEmpty)
            {
                lst = pq.Dequeue(); // get top of priority queue
                Point curPos = lst.Last.Value;

                if(curPos.Equals(finish)) // finish position?
                    return lst.ToArray<Point>(); // return the path as an array

                // add new nodes
                Point[] temp = { new Point(curPos.X-1, curPos.Y), // left
                                 new Point(curPos.X+1, curPos.Y), // right
                                 new Point(curPos.X, curPos.Y-1), // down
                                 new Point(curPos.X, curPos.Y+1) // up
                               };

                // check that they are within the map's bounds and free
                foreach (Point p in temp)
                {
                    if(isInMap(p) && // within the map's bounds
                        map[p.X,p.Y].isFree() && // free position in map
                        !lst.Contains(p)) // visited before (if so, then throw p)
                    {
                        // copy constructor
                        LinkedList<Point> tLst = new LinkedList<Point>(lst);
                        tLst.AddLast(p); // add our new point to the path

                        pq.Enqueue(tLst.Count, tLst); // add to 'pq' queue
                    }
                }
            }

            return null; // no path found ...
        }
        
        public IPathSquere[,] Map 
        {
            set { map = value; }
        }

        //help functions

        private bool isInMap(Point pos)
        {
            int rows = map.GetLength(0);
            int cols = map.GetLength(1);

            return 0 <= pos.X && pos.X < cols
                && 0 <= pos.Y && pos.Y < rows;
        }
    }
}


Maybe I should mention that the lag starts to be very heavy with a map like 20*15 squares if there is many walkable/free. So is it possible to get better performance (yes it must be..) and how can I achieve that.

Thanks in advance // WaZoX
Posted
Updated 20-Nov-10 23:15pm
v6

1 solution

I see a couple of things in there that are very bad from a performance perspective. One of them is using Contains (especially on a linked list), the other is creating a copy of a potentially large structure (a linked list again, so lots of pointer chasing). Creating lots of temporary array's isn't great either.
I've implemented A* a couple of times, in performance settings, it shouldn't be that much different than A+..
So I'm going to say what I did there.
I have to ask though, when you find a node that you already handled, you don't check whether the new path is shorter than the old one. Is that correct behaviour? In A* it wouldn't be,
Anyway, I implemented a min-heap with decreasable-keys (so when a shorter path is found to a node that is already in the Open list, it doesn't have to be removed&reinserted, it can be updated which is faster in practice), and for the closed list a "semi sparse grid" (because the maps were a bit big) it was just a grid of blocks of 32x32 "bools" (implemented as an array of uints), the blocks would only be present when needed (when a true is written to them). Testing whether a node is in the closed list is then O(1). I also used yet an other "semi sparse grid" to look up nodes from the open-list in O(1) instead of the usual O(n).
In all, I traded in quite some memory space for a lot of time, and it worked out rather well.
Also, the path was not "build up", the "parent" would be stored and at the end the path would be traced back.

I hope some of that can help you.
 
Share this answer
 
Comments
WaZoX 23-Nov-10 9:13am    
Thanks a lot, you have many good points that I will fix. It's sounds very logical now when I read it. What I didn't understood was why the "LinkedList" would give extra poor performance, should an "ArrayList" work better even if it still would be
very slow?
[no name] 23-Nov-10 9:22am    
A List<t> may work better yes, but it would still be very slow so it's hardly worth trying - instead I'd look into storing just the parent and tracing the path back when you've found the target
El_Codero 5-Mar-12 18:36pm    
Thanks four sharing your experiences. 5 and best regards

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900