Ejemplo n.º 1
0
        /// <summary>
        /// This constructs a path from the specified start point to
        /// some point in the set of destinations.
        ///
        /// Will only construct paths up to a certain maximum COST;
        /// as of now, going from one square to another costs 1 unit.
        /// So the maximum cost is the maximum path length.
        ///
        /// Returns null if no path was found.
        ///
        /// Running time (if no path is found, the worst case) is O(k+n^2), where
        ///        n=maxCost
        ///        k=destinations.Count
        ///
        /// Assumes no negative cost paths, as in Djikstra's algorithm
        /// </summary>
        /// <param name="startPoints">The set of start points we could be happy with.</param>
        /// <param name="goalPoints">The set of possible destinations we could be happy with.</param>
        /// <param name="maxCost">Maximum cost of any path.</param>
        /// <param name="manager">The WorldManager that we use to do our pathing.</param>
        /// <param name="startTime">The time we start the algorithm.  Use GameTime.TotalGameTime</param>
        /// <returns>The shortest path from here to somewhere in there; returns null iff there is no path of cost less than maxCost.</returns>
        public static Path GetPath <T, S, M>(HashSet <Point> startPoints, HashSet <Point> goalPoints, int maxCost, TileMapManager <T, S, M> manager, TimeSpan startTime)
            where T : InGameObject
            where S : MapCell, Copyable <S>
            where M : TileMap <S>, new()
        {
            //check for trivialities- we can't find a path to nowhere or from nowhere
            if (goalPoints.Count == 0 || startPoints.Count == 0)
            {
                return(null);
            }

            Heap <Path> heap = null;

            if (goalPoints.Count == 1)
            {
                heap = new PathToPointHeap(goalPoints.Single());
            }
            else
            {
                heap = new PathHeap();
            }


            //now set up a list of points we've seen before, so as to not
            //check the same position a billion times
            Dictionary <Point, int> bestDistancesFound = new Dictionary <Point, int>();

            foreach (Point startPoint in startPoints)
            {
                bestDistancesFound[startPoint] = 0;
                heap.Add(new Path(startPoint));

                if (goalPoints.Contains(startPoint))
                {
                    return(new Path(startPoint));
                }
            }

            while (heap.Count > 0)
            {
                //if there's new passability information, start the process over
                if (manager.LastGeneralPassabilityUpdate != null && //if it's null, we're definitely OK
                    startTime < manager.LastGeneralPassabilityUpdate)
                {
                    startTime = manager.LastGeneralPassabilityUpdate;

                    heap.Clear();
                    bestDistancesFound = new Dictionary <Point, int>();

                    foreach (Point startPoint in startPoints)
                    {
                        bestDistancesFound[startPoint] = 0;
                        heap.Add(new Path(startPoint));
                    }
                }

                Path bestPath = heap.Pop();

                //if we didn't cap out our path length yet,
                //check out the adjacent points to form longer paths
                if (bestPath.Cost < maxCost)
                {
                    int newCost = bestPath.Cost + 1;

                    //for each possible extension ...
                    foreach (Point adj in manager.GetAdjacentPoints(bestPath.End.X, bestPath.End.Y))
                    {
                        //if we hit a destination, great, stop
                        if (goalPoints.Contains(adj))
                        {
                            return(new Path(bestPath, adj, newCost));
                        }

                        //don't bother adding this possible extension back on unless
                        //it's either a completely new point or a strict improvement over another path
                        if (bestDistancesFound.Keys.Contains(adj) && bestDistancesFound[adj] <= newCost)
                        {
                            continue;
                        }

                        //otherwise, this is a perfectly serviceable path extension and we should look into it
                        bestDistancesFound[adj] = newCost;

                        heap.Add(new Path(bestPath, adj, newCost));
                    }
                }
            }

            return(null);
        }