コード例 #1
0
ファイル: AStar.cs プロジェクト: xmshaka/slash-framework
        /// <summary>
        ///   Computes the most efficient path in the specified graph between the
        ///   specified pathfinding nodes using the A* algorithm.
        /// </summary>
        /// <param name="graph">Pathfinding graph to look at.</param>
        /// <param name="start">Starting node.</param>
        /// <param name="finish">Finish node.</param>
        /// <returns>
        ///   List of pathfinding nodes representing the shortest path, if there is one,
        ///   and null otherwise.
        /// </returns>
        /// <typeparam name="T">Type of the pathfinding nodes.</typeparam>
        /// <exception cref="ArgumentNullException">
        ///   Passed graph or start or finish node is <c>null</c>.
        /// </exception>
        public static List <T> FindPath <T>(IWeightedGraph <T> graph, T start, T finish) where T : IAStarNode
        {
            if (graph == null)
            {
                throw new ArgumentNullException("graph", "The passed pathfinding graph mustn't be null.");
            }

            if (start == null)
            {
                throw new ArgumentNullException("start", "The passed start node mustn't be null.");
            }

            if (finish == null)
            {
                throw new ArgumentNullException("finish", "The passed finish mustn't be null.");
            }

            // --- Initialization ---
            // Initialize variables to check for the algorithm to terminate.
            bool algorithmComplete = false;
            bool algorithmAborted  = false;

            // Initialize list to choose the next node of the path from.
            IPriorityQueue <T> openList = new FibonacciHeap <T>();

            IPriorityQueueItem <T>[] fibHeapItems = new FibonacciHeapItem <T> [graph.VertexCount];
            HashSet <T> dirtyNodes = new HashSet <T>();

            // Initialize queue to hold the nodes along the path to the finish.
            Queue <T> closedList = new Queue <T>();

            // Declare current node to work on in order to calculate the path.

            // Declare list to hold the neighbors of the current node.

            // Add starting node to open list.
            fibHeapItems[start.Index] = openList.Insert(start, 0);
            dirtyNodes.Add(start);
            start.Discovered = true;

            // --- A* Pathfinding Algorithm ---
            while ((!algorithmComplete) && (!algorithmAborted))
            {
                // Get the node with the lowest F score in the open list.
                T currentNode = openList.DeleteMin().Item;

                // Drop that node from the open list and add it to the closed list.
                closedList.Enqueue(currentNode);
                currentNode.Visited = true;

                // We're done if the target node is added to the closed list.
                if (currentNode.Equals(finish))
                {
                    algorithmComplete = true;
                    break;
                }

                // Otherwise, get all adjacent nodes.
                List <T> neighbors = graph.ListOfAdjacentVertices(currentNode);

                // Add all nodes that aren't already on the open or closed list to the open list.
                foreach (T node in neighbors)
                {
                    // Ignore nodes in the closed list.
                    if (!node.Visited)
                    {
                        if (!node.Discovered)
                        {
                            // The parent node is the previous node on the path to the finish.
                            node.ParentNode = currentNode;

                            // The G score of the node is calculated by adding the G score
                            // of the parent node and the movement cost of the path between
                            // the node and the current node.
                            // In other words: The G score of the node is the total cost of the
                            // path between the starting node and this one.
                            node.G = node.ParentNode.G + graph.GetEdgeWeight(node, (T)node.ParentNode);

                            // The H score of the node is calculated by heuristically
                            // estimating the movement cost from the node to the finish.
                            // In other words: The H score of the node is the total remaining
                            // cost of the path between this node and the finish.
                            node.H = node.EstimateHeuristicMovementCost(finish);

                            // The F score of the node is calculated by adding the G and H scores.
                            // In other words: The F score is an indicator that tells whether this
                            // node should be crossed on the path to the finish, or not.
                            node.F = node.G + node.H;

                            // Add to open list.
                            fibHeapItems[node.Index] = openList.Insert(node, node.F);
                            dirtyNodes.Add(node);
                            node.Discovered = true;
                        }
                        else
                        {
                            // Node is already in open list!
                            // Check if the new path to this node is a better one.
                            if (currentNode.G + graph.GetEdgeWeight(node, currentNode) < node.G)
                            {
                                // G cost of new path is lower!
                                // Change parent node to current node.
                                node.ParentNode = currentNode;

                                // Recalculate F and G costs.
                                node.G = node.ParentNode.G + graph.GetEdgeWeight(node, (T)node.ParentNode);
                                node.F = node.G + node.H;

                                openList.DecreaseKeyTo(fibHeapItems[node.Index], node.F);
                            }
                        }
                    }
                }

                // We've failed to find a path if the open list is empty.
                if (openList.IsEmpty())
                {
                    algorithmAborted = true;
                }
            }

            // Return the path to the finish, if there is one.
            if (algorithmComplete)
            {
                // Generate path through parent pointers using a stack.
                Stack <T> s    = new Stack <T>();
                T         node = finish;
                while (!node.Equals(start))
                {
                    s.Push(node);
                    node = (T)node.ParentNode;
                }

                // Generate path in right order.
                List <T> path = new List <T>();
                while (s.Count > 0)
                {
                    path.Add(s.Pop());
                }

                // Cleanup pathing.
                foreach (T dirtyNode in dirtyNodes)
                {
                    dirtyNode.Reset();
                }

                return(path);
            }
            else
            {
                // Cleanup pathing.
                foreach (T dirtyNode in dirtyNodes)
                {
                    dirtyNode.Reset();
                }

                return(null);
            }
        }