Пример #1
0
        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);
        }
Пример #2
0
        /// <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);
        }
Пример #3
0
        /// <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);
        }
Пример #5
0
        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);
        }