简体   繁体   中英

Fixing A* implementation in C#

So I've implemented an A* algorithm in Unity C# to do some tests in order to build a 2D based game. I know there are several components out there that help you with this but I want to try myself just for the challenge.

I have basically read how the A* should behave and translated the behaviour into code. It almost works. But there are some ocassions where the adjacent tiles have exactly the same score (manhattan distance + distance from origin) and you end up with a path that leads to the destination but is not the shortest. As you can see in the image, those two adjacent tiles have the same score, but I pick up a random one at that point...(In the image below, the starting point is the cat and the red cross is the destination point. The green semi-transparent files is the calculated path)

在此处输入图片说明

I was thinking that since there are not too many tiles, I could calculate the 4 different paths from the 4 initial adjacent tiles, store the valid ones in a array, and then basically just use the shortest, but maybe that will be too much overhead and there is another solution?

To calculate the distance I'm using a basic calculation:

private int CalculateManhattanDistance(int x1, int x2, int y1, int y2)
{
        return Mathf.Abs(x1 - x2) + Mathf.Abs(y1 - y2);
}

Maybe this help you to understand what both @Zdeněk Jelínek and @Peter Wishart have pointed out. The openSet (also named frontier), usually is a PriorityQueue . The nodes of the queue are sorted according to their priorities. The priority of a node is calculated as the sum of the cost so far (number of steps in your case) and the heuristic distance (Manhattan in your case). Therefore, as soon A* reaches the node with priority 11, it will stop to explore that path and will continue with the others (blue circle)

I recommend checking the pseudocode for A* eg on Wikipedia against your code.

Based on that pseudocode you would implement the algorithm data like this, I suspect you have simplified some of these:

        var closedSet = new HashSet<GraphNode>();
        var openSet = new List<GraphNode>{startNode};
        var cameFrom = new Dictionary<GraphNode, GraphNode>();
        var gScore = new Dictionary<GraphNode, double>();
        var fScore = new Dictionary<GraphNode, double>();

When the algorithm makes a bad first choice for the heuristic, as in this test case, it will initially evaluate a move in the wrong direction. But this isn't a problem as it should:

  • Select an open node with the lowest fScore
  • Evaluate all reachable neighbours (eg including the node to the left of the start node in your example's 1st iteration)
  • Update gScore with actual distance (via cameFrom)
  • Update fScore with actual distance plus estimated (eg Manhattan) distance to target
  • Move evaluated nodes from openSet to closedSet

This means that nodes along the "wrong" path will compute increasing actual + expected distances, to the extent that the algorithm starts choosing other open nodes eg the "right" node from the 1st iteration.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM