public static List <T> DijkstraSolve <T>(this IReadOnlyGraph <T> graph, T start, T end) where T : IEquatable <T> { // not strictly necessary (DijkstraSolveWithInfo does the same), bu oh well, better safe than sorry :) graph.ThrowOnInvalidInput(start, end); return(graph.DijkstraSolveWithInfo(start, end).Path); }
/// <summary> /// Get a path between two points in the graph using the A* algorithm. /// If no path can be found, an empty list is returned. /// </summary> public static List <T> AStarSolve <T>(this IReadOnlyGraph <T> graph, T start, T end, ICalculator <T> calculator = null) where T : IEquatable <T> { // validate input graph.ThrowOnInvalidInput(start, end); return(graph.AStarSolveWithInfo(start, end, calculator).Path); }
/// <summary> /// Get a path between two points in the graph using the A* algorithm. /// A list of the visited nodes is also returned. /// If no path can be found, the <see cref="PathFindingResult{T}"/> will be empty, but no members will be null. /// <br/><see cref="PathFindingResult{T}"/> will contain the found path, the nodes that were queued to be evaluated /// (in <see cref="PathFindingResult{T}.OpenNodes"/>) and the nodes that were finally evaluated /// (in <see cref="PathFindingResult{T}.ClosedNodes"/>) /// </summary> public static PathFindingResult <T> AStarSolveWithInfo <T>(this IReadOnlyGraph <T> g, T start, T end, ICalculator <T> calculator = null) where T : IEquatable <T> { g.ThrowOnInvalidInput(start, end); if (start.Equals(end)) { return(PathFindingResult <T> .Empty); } // check if another calculator has been provided, if not choose the one from the graph calculator = calculator ?? g.Calculator; // initialize the open and closed list // the closed list contains all nodes that we don't want to evaluate ever again, they are done var closed = new HashSet <AStarNode <T> >(); // the open list contains the nodes that are queued up for evaluation at some point var open = new HashSet <AStarNode <T> > // first we enqueue the provided start node, with an estimated distance of start to end // it should be possible to make this float.maxValue instead { new AStarNode <T>(start, distanceToFinish: g.Calculator.Distance(start, end)) }; // we have nodes to evaluate while (open.Count > 0) { // get most promising node var recordHolder = AStarGetMostPromisingNode(open); if (recordHolder == null) { throw new InvalidOperationException("The most promising node was null. This should never ever happen!"); } // check if the record node is the finish and return the corresponding data if it is if (recordHolder.Data.Equals(end)) { return(new PathFindingResult <T>(BuildPath(recordHolder, closed), new HashSet <T>(open.Select(x => x.Data)), new HashSet <T>(closed.Select(x => x.Data)))); } // if we chose a node, we are kind of done with evaluating it again // (there are scenarios where that might be a good idea) open.Remove(recordHolder); closed.Add(recordHolder); // expand the record holder. AStarExpandNode(recordHolder, end, g.GetPassableConnections(recordHolder.Data), open, closed, calculator); } // if we leave the loop (we have no nodes to evaluate), we have no path return(PathFindingResult <T> .Empty); }
/// <summary> /// Kind of the worst kind of pathfinding you can choose, ever. /// <br/>It will (eventually) return a path between <see cref="start"/> and <see cref="end"/> using a /// recursive algorithm. /// </summary> public static List <T> RecursiveSolve <T>(this IReadOnlyGraph <T> graph, T start, T end) where T : IEquatable <T> { graph.ThrowOnInvalidInput(start, end); Logger.Warn("This can take a while!"); var path = RecursiveSolve(graph, start, end, new List <T>()); path.Reverse(); return(path); }
public static PathFindingResult <T> DijkstraSolveWithInfo <T>(this IReadOnlyGraph <T> g, T start, T end) where T : IEquatable <T> { g.ThrowOnInvalidInput(start, end); if (start.Equals(end)) { return(PathFindingResult <T> .Empty); } var startNode = new DijkstraNode <T>(start, currentPathLength: 0f); // we have visited the start node already and thus don't need to again var visited = new HashSet <DijkstraNode <T> > { startNode }; // we also want to add it to the open set though, to have a node to evaluate the neighbors from var open = new HashSet <DijkstraNode <T> > { startNode }; while (open.Count > 0) { // get the node that has the shortest saved path to the start node // TODO refactor into function (Finding best node) var curr = GetNearestNode(open); // if the best node is actually the finish: hooray lucky day, return it if (curr.Data.Equals(end)) { return(new PathFindingResult <T>(BuildPath(curr, visited), new HashSet <T>(open.Select(x => x.Data)), new HashSet <T>(visited.Select(x => x.Data)))); } // the just evaluated node isn't open for evaluation again, we were to pushy :( open.Remove(curr); // we have in fact just visited it visited.Add(curr); // expand this node DijkstraExpandNode(curr, g.GetPassableConnections(curr.Data), open, visited, g.Calculator); } // we haven't found a path return(PathFindingResult <T> .Empty); }
/// <summary> /// Use the iterative bfs algorithm to find a path between <paramref name="start"/> and <paramref name="end"/>. /// </summary> /// <param name="graph">The graph to solve on</param> /// <param name="start">The start node</param> /// <param name="end">The end node</param> /// <exception cref="Graph{T}.NodeNotFoundException">This will be thrown if either /// start or end are in the graph.</exception> /// <exception cref="InvalidOperationException">This will be thrown when one of /// the nodes is not passable at the time of asking (politely of course)</exception> public static List <T> IterativeBfsSolve <T>(this IReadOnlyGraph <T> graph, T start, T end) where T : IEquatable <T> { graph.ThrowOnInvalidInput(start, end); return(graph.IterativeBfsSolveWithInfo(start, end).Path); }