40

Which should be the way to get a simple implementation of A* (A star) algorithm in C#?

Braiam
  • 1
  • 11
  • 47
  • 78
A New Chicken
  • 703
  • 1
  • 8
  • 18
  • 113
    Pointers are unsafe in C#. /joke – kennytm Jan 26 '10 at 10:02
  • 3
    Thanks you, I think I found it, here: http://www.policyalmanac.org/games/aStarTutorial.htm – A New Chicken Jan 26 '10 at 10:19
  • 21
    A year and a half later, type that exact sentence into Google and you should find this page and a snarky comment telling you to go do exactly what you just did. – Isabelle Wedin Jun 18 '12 at 14:22
  • -1 for give-me-the-code/find-me-a-resource question. Please clarify your problem (for example showing an attempted solution). – Bakuriu Apr 24 '14 at 08:23
  • 6
    4 years later and we still type this exact sentence into Google and find this page with your snarky comment telling us to do exactly what you just said we should do. – edthethird Jul 27 '14 at 15:05
  • Take a look at http://www.codeguru.com/csharp/csharp/cs_misc/designtechniques/article.php/c12527/AStar-A-Implementation-in-C-Path-Finding-PathFinder.htm, http://www.codeproject.com/Articles/4391/C-A-Star-is-born – MZaragoza May 03 '15 at 02:20
  • 1
    [Here](http://www.codeguru.com/csharp/csharp/cs_misc/designtechniques/article.php/c12527/), [here](http://www.codeproject.com/KB/recipes/graphs_astar.aspx), [here](http://www.codeproject.com/KB/recipes/csharppathfind.aspx) and [here](http://www.tanis.dk/Articles/CSharpPathfind/). – Nikola Smiljanić Jan 26 '10 at 10:04
  • [This](http://www.codeguru.com/csharp/csharp/cs_misc/designtechniques/article.php/c12527/) is a nice implementation from codeguru. –  Jan 26 '10 at 10:10
  • 8
    6 yeas later and the exact same Google query leads us to this useless thread, complete with snarky comments and dead links. Hurrah guys, we've effectively built a time machine to 1996. – Paul Lammertsma Jan 22 '16 at 22:48
  • 7 and half years later, the snarky comment has been deleted. Hooray, and good day to all readers. :) – Shadow The GPT Wizard Nov 09 '17 at 16:08

2 Answers2

8

This article explains the basic implementation in length:

The goal of this blog post is to show the fundamentals of A* through a really simple C# implementation.

It also points to better implementations, more suitable for production use:

As for ways to find better routes, there are plenty of C# examples around that are far better and richer than this one. CastorTiu has a really nice demo solution on CodeProject, A* algorithm implementation in C#, that animates the search algorithm and allows the user to tweak a few settings. [...]

EpPathFinding.cs- A Fast Path Finding Algorithm (Jump Point Search) in C# (grid-based). It has a nice, clear GUI and allows a few settings to be tweaked.

Community
  • 1
  • 1
Derlin
  • 9,572
  • 2
  • 32
  • 53
2

In the function AStar, we start by creating a new matrixNode, with the parameters fromX and fromY. A matrixNode has properties, "fr" which is the distance of any given matrixNode from the starting node, a "to" property which is the distance of a given matrixNode from the destination matrixNode (would be 'E' at coordinates (3,3) in the unitTest's example), and a property "sum" which is the sum of "to" and "fr". The property parent, is a reference to the matrixNode that the given node was moved to in the path for reaching from the start node to the end node. The dictionaries greens and reds, are the openSet and closedSet respectively as described in the A* search algorithm page on Wikipedia. The general idea with these sets, is that we are trying to find the matrixNode in the green/open set which has the lowest "sum" value, as "sum" was the sum of the distances of the node from the start node at (fromX,fromY) and the end node at (toX, toY)

    public static void unitTest_AStar()
    {
        char[][] matrix = new char[][] { new char[] {'-', 'S', '-', '-', 'X'},
                                         new char[] {'-', 'X', 'X', '-', '-'},
                                         new char[] {'-', '-', '-', 'X', '-'},
                                         new char[] {'X', '-', 'X', 'E', '-'},
                                         new char[] {'-', '-', '-', '-', 'X'}};

        //looking for shortest path from 'S' at (0,1) to 'E' at (3,3)
        //obstacles marked by 'X'
        int fromX = 0, fromY = 1, toX = 3, toY = 3;
        matrixNode endNode = AStar(matrix, fromX, fromY, toX, toY);

        //looping through the Parent nodes until we get to the start node
        Stack<matrixNode> path = new Stack<matrixNode>();

        while (endNode.x != fromX || endNode.y != fromY)
        {
            path.Push(endNode);
            endNode = endNode.parent;
        }
        path.Push(endNode);

        Console.WriteLine("The shortest path from  " +
                          "(" + fromX + "," + fromY + ")  to " +
                          "(" + toX + "," + toY + ")  is:  \n");

        while (path.Count > 0)
        {
            matrixNode node = path.Pop();
            Console.WriteLine("(" + node.x + "," + node.y + ")");
        }
    }

    public class matrixNode
    {
        public int fr = 0, to = 0, sum = 0;
        public int x, y;
        public matrixNode parent;
    }

    public static matrixNode AStar(char[][] matrix, int fromX, int fromY, int toX, int toY)
    {
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // in this version an element in a matrix can move left/up/right/down in one step, two steps for a diagonal move.
        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        //the keys for greens and reds are x.ToString() + y.ToString() of the matrixNode 
        Dictionary<string, matrixNode> greens = new Dictionary<string, matrixNode>(); //open 
        Dictionary<string, matrixNode> reds = new Dictionary<string, matrixNode>(); //closed 

        matrixNode startNode = new matrixNode { x = fromX, y = fromY };
        string key = startNode.x.ToString() + startNode.x.ToString();
        greens.Add(key, startNode);

        Func<KeyValuePair<string, matrixNode>> smallestGreen = () =>
        {
            KeyValuePair<string, matrixNode> smallest = greens.ElementAt(0);

            foreach (KeyValuePair<string, matrixNode> item in greens)
            {
                if (item.Value.sum < smallest.Value.sum)
                    smallest = item;
                else if (item.Value.sum == smallest.Value.sum
                        && item.Value.to < smallest.Value.to)
                    smallest = item;
            }

            return smallest;
        };


        //add these values to current node's x and y values to get the left/up/right/bottom neighbors
        List<KeyValuePair<int, int>> fourNeighbors = new List<KeyValuePair<int, int>>()
                                            { new KeyValuePair<int, int>(-1,0),
                                              new KeyValuePair<int, int>(0,1),
                                              new KeyValuePair<int, int>(1, 0),
                                              new KeyValuePair<int, int>(0,-1) };

        int maxX = matrix.GetLength(0);
        if (maxX == 0)
            return null;
        int maxY = matrix[0].Length;

        while (true)
        {
            if (greens.Count == 0)
                return null;

            KeyValuePair<string, matrixNode> current = smallestGreen();
            if (current.Value.x == toX && current.Value.y == toY)
                return current.Value;

            greens.Remove(current.Key);
            reds.Add(current.Key, current.Value);

            foreach (KeyValuePair<int, int> plusXY in fourNeighbors)
            {
                int nbrX = current.Value.x + plusXY.Key;
                int nbrY = current.Value.y + plusXY.Value;
                string nbrKey = nbrX.ToString() + nbrY.ToString();
                if (nbrX < 0 || nbrY < 0 || nbrX >= maxX || nbrY >= maxY
                    || matrix[nbrX][nbrY] == 'X' //obstacles marked by 'X'
                    || reds.ContainsKey(nbrKey))
                    continue;

                if (greens.ContainsKey(nbrKey))
                {
                    matrixNode curNbr = greens[nbrKey];
                    int from = Math.Abs(nbrX - fromX) + Math.Abs(nbrY - fromY);
                    if (from < curNbr.fr)
                    {
                        curNbr.fr = from;
                        curNbr.sum = curNbr.fr + curNbr.to;
                        curNbr.parent = current.Value;
                    }
                }
                else
                {
                    matrixNode curNbr = new matrixNode { x = nbrX, y = nbrY };
                    curNbr.fr = Math.Abs(nbrX - fromX) + Math.Abs(nbrY - fromY);
                    curNbr.to = Math.Abs(nbrX - toX) + Math.Abs(nbrY - toY);
                    curNbr.sum = curNbr.fr + curNbr.to;
                    curNbr.parent = current.Value;
                    greens.Add(nbrKey, curNbr);
                }
            }
        }
    }
Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
Lee.O.
  • 63
  • 5
  • Little type'o string key = startNode.x.ToString() + startNode.x.ToString(); must be changed to string key = startNode.x.ToString() + startNode.y.ToString(); – V319 Apr 16 '18 at 14:07