/// <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)); } }
/// <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)); }