Пример #1
0
        /// <summary>
        /// Finds the shortest route in the graph described by the RouteFinderPackage p.
        /// Modifies p so that the next time it is used to find a route, the next-longer
        /// route will be found.
        /// </summary>
        /// <param name="p">The RouteFinderPackage describing the graph on which to find
        ///		the route.</param>
        /// <returns>The shortest route in the graph described by p.</returns>
        public static Route FindRoute(RouteFinderPackage p)
        {
            Vertex startVertex = null;

            // (8) while Q is not an empty set
            while (p.entranceVertices.Count > 0 || p.exitVertices.Count > 0)
            {
                // (9) u := Extract_Min(Q)
                Vertex u = Vertex.NULL_VERTEX;
                bool   isEntranceVertex = true;
                foreach (Vertex v in p.entranceVertices.Values)
                {
                    if (v.ShortestPathCost <= u.ShortestPathCost)
                    {
                        u = v;
                    }
                }
                foreach (Vertex v in p.exitVertices.Values)
                {
                    if (v.ShortestPathCost <= u.ShortestPathCost)
                    {
                        u = v;
                        isEntranceVertex = false;
                    }
                }
                if (isEntranceVertex)
                {
                    p.entranceVertices.Remove(u.Loc.Id);
                }
                else
                {
                    p.exitVertices.Remove(u.Loc.Id);
                }

                // If we're at one of the start locations, we've found a path
                if (p.startVertices.Contains(u))
                {
                    startVertex = u;
                    //p.startVertices.Remove(u);
                    break;
                }

                // (11) for each edge (u,v) outgoing from u
                // Note: This search is being done backwards, so look at incoming edges instead
                foreach (Edge e in u.IncomingEdges)
                {
                    Vertex v = e.From;
                    // (12) if d[v] > d[u] + w(u,v) // Relax (u,v)
                    if (v.ShortestPathCost > u.ShortestPathCost + e.Weight)
                    {
                        // (13) d[v] := d[u] + w(u,v)
                        v.ShortestPathCost = u.ShortestPathCost + e.Weight;
                        // (14) previous[v] := u
                        v.Next = u;
                    }
                }
            }

            // Read the route back
            if (startVertex != null && !double.IsInfinity(startVertex.ShortestPathCost))
            {
                Route route = new Route(startVertex.ShortestPathCost);
                route.Add(startVertex.Loc);
                for (Vertex u = startVertex; u != null; u = u.Next)
                {
                    if (u.Category == VertexCategory.Entrance)
                    {
                        route.Add(u.Loc);
                    }
                }
                return(route);
            }
            else
            {
                return(new Route(0.0));
            }
        }
Пример #2
0
        /// <summary>
        /// Finds the shortest route from any number of start locations to a specific end location
        /// </summary>
        /// <param name="startPoints">The list of the alternate start points</param>
        /// <param name="startLocation">The specific start point specified by the user, or
        ///		NO_LOCATION if the algorithm should only use the locations in startPoints.</param>
        /// <param name="endLocation">The route destination</param>
        /// <param name="p">This will describe the graph generated for route finding.
        ///		Pass it as the parameter to FindRoute(RouteFinderPackage) to find the
        ///		next-longer route.</param>
        /// <returns>The shortest route from endLocation to any of the start points.</returns>
        public static Route FindRoute(LocationDatabase locDb, ICollection <RouteStart> startPoints,
                                      ICollection <PortalDevice> portalDevices, Location startLocation, Location endLocation,
                                      out RouteFinderPackage p)
        {
            // Implementation Notes:
            //   - Based on Dijkstra's Algorithm
            //   - Maintains two "Q" lists, one for portal entrances and one for portal exits.
            //   - "Running" edges exist from portal exits to portal entrances, but only if
            //	   the two points are in the same region (i.e., island).
            //   - "Portal" edges exist from portal entrances to portal exits.
            //   - Finds the route backwards, starting at the end location and working until
            //     it gets to any of the start locations
            //
            // Reference: http://en.wikipedia.org/wiki/Dijkstra's_algorithm
            // 1   function Dijkstra(G, w, s)
            // 2      for each vertex v in V[G]                       // Initializations
            // 3            d[v] := infinity
            // 4            previous[v] := undefined
            // 5      d[s] := 0
            // 6      S := empty set
            // 7      Q := set of all vertices
            // 8      while Q is not an empty set
            // 9            u := Extract_Min(Q)
            // 10           S := S union {u}
            // 11           for each edge (u,v) outgoing from u
            // 12                 if d[v] > d[u] + w(u,v)             // Relax (u,v)
            // 13                       d[v] := d[u] + w(u,v)
            // 14                       previous[v] := u

            LazyLoadRegions();
            p = new RouteFinderPackage();

            // Estimate the number of vertices..
            int numLocations = locDb.PortalLocations.Count + startPoints.Count + portalDevices.Count + 4;

            // The vertices representing portal entrances
            p.entranceVertices = new Dictionary <int, Vertex>(numLocations);
            // The vertices representing portal exits
            p.exitVertices = new Dictionary <int, Vertex>(numLocations);

            // (7) Q := set of all vertices
            // (2-4) Done in Vertex constructors
            foreach (Location portal in locDb.PortalLocations)
            {
                if (portal.UseInRouteFinding)
                {
                    Vertex entrance, exit;
                    p.entranceVertices[portal.Id] = entrance = new Vertex(portal, VertexCategory.Entrance);
                    p.exitVertices[portal.Id]     = exit = new Vertex(portal, VertexCategory.Exit);

                    double portalWeight = mPortalWeight;
                    if (portal.Type == LocationType.UndergroundPortal)
                    {
                        portalWeight *= 2;
                    }

                    Edge edge = new Edge(entrance, exit, portalWeight);

                    //entrance.OutgoingEdges.Add(edge);
                    exit.IncomingEdges.Add(edge);
                }
            }

            // Add portal devices
            foreach (PortalDevice device in portalDevices)
            {
                if (device.Detected && device.Enabled)
                {
                    foreach (Location dest in device.Destinations)
                    {
                        Vertex entrance, exit;
                        p.entranceVertices[dest.Id] = entrance = new Vertex(dest, VertexCategory.Entrance);
                        p.exitVertices[dest.Id]     = exit = new Vertex(dest, VertexCategory.Exit);

                        Edge edge = new Edge(entrance, exit, device.RunDistance);

                        //entrance.OutgoingEdges.Add(edge);
                        exit.IncomingEdges.Add(edge);
                    }
                }
            }

            // Add vertices for start and end if they aren't already in the list.
            p.startVertices = new List <Vertex>(startPoints.Count);
            Dictionary <Vertex, double> startVerticesExtraRun = new Dictionary <Vertex, double>();
            Vertex end;

            // startVerticies are like portal exits - make sure they're in the
            // exit list, and not in the entrance list
            Vertex start;

            foreach (RouteStart startPoint in startPoints)
            {
                if (startPoint.Enabled && startPoint.Coords != Coordinates.NO_COORDINATES)
                {
                    if (!p.exitVertices.TryGetValue(startPoint.Id, out start))
                    {
                        start = new Vertex(startPoint.ToLocation(locDb), VertexCategory.Exit);
                        p.exitVertices[startPoint.Id] = start;
                    }
                    p.entranceVertices.Remove(startPoint.Id);
                    start.IncomingEdges.Clear();

                    if (start.Loc.ExitCoords == Coordinates.NO_COORDINATES)
                    {
                        start.Loc.ExitCoords = start.Loc.Coords;
                    }

                    p.startVertices.Add(start);
                    if (startPoint.RunDistance > 0)
                    {
                        startVerticesExtraRun[start] = startPoint.RunDistance;
                    }
                }
            }
            if (startLocation != null && startLocation != Location.NO_LOCATION)
            {
                if (!p.exitVertices.TryGetValue(startLocation.Id, out start))
                {
                    start = new Vertex(startLocation, VertexCategory.Exit);
                    p.exitVertices[startLocation.Id] = start;
                }
                start.IncomingEdges.Clear();
                p.entranceVertices.Remove(startLocation.Id);

                if (start.Loc.ExitCoords == Coordinates.NO_COORDINATES)
                {
                    // Create a copy so as to not modify the original Location
                    start.Loc            = new Location(start.Loc.Id, start.Loc);
                    start.Loc.ExitCoords = start.Loc.Coords;
                }

                p.startVertices.Add(start);
            }

            // end is like a portal entrance - make sure it's in the
            // entrance list, and not in the exit list
            if (!p.entranceVertices.TryGetValue(endLocation.Id, out end))
            {
                end = new Vertex(endLocation, VertexCategory.Entrance);
                p.entranceVertices[endLocation.Id] = end;
            }
            p.exitVertices.Remove(endLocation.Id);
            //end.OutgoingEdges.Clear();

            // (5) d[s] := 0
            end.ShortestPathCost = 0;

            // Add edges for running from each portal exit to each portal entrance
            // ONLY add edges if the two vertices have the same region ID (i.e., are
            // on the same island).
            foreach (Vertex exit in p.exitVertices.Values)
            {
                foreach (Vertex entrance in p.entranceVertices.Values)
                {
                    if (entrance.Loc != exit.Loc && entrance.RegionId == exit.RegionId)
                    {
                        double distance = exit.Loc.ExitCoords.DistanceTo(entrance.Loc.Coords);
                        double extraRun;
                        if (startVerticesExtraRun.TryGetValue(exit, out extraRun))
                        {
                            distance += extraRun;
                        }

                        if (distance < mMaxRunDistance)
                        {
                            Edge e = new Edge(exit, entrance, distance);
                            //exit.OutgoingEdges.Add(e);
                            entrance.IncomingEdges.Add(e);
                        }
                    }
                }
            }

            return(FindRoute(p));
        }