001/*
002MIT License
003
004Copyright (c) 2020 earlygrey
005
006Permission is hereby granted, free of charge, to any person obtaining a copy
007of this software and associated documentation files (the "Software"), to deal
008in the Software without restriction, including without limitation the rights
009to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010copies of the Software, and to permit persons to whom the Software is
011furnished to do so, subject to the following conditions:
012
013The above copyright notice and this permission notice shall be included in all
014copies or substantial portions of the Software.
015
016THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
022SOFTWARE.
023 */
024package squidpony.squidai.graph;
025
026import java.util.ArrayList;
027
028/**
029 * Most of the algorithms that operate on a {@link Graph} are defined here, with some specific cases in subclasses.
030 * The most-frequently-used algorithm here is probably {@link #findShortestPath(Object, Object, ArrayList, Heuristic)},
031 * or some overload of it; this is a standard A-Star algorithm backed by a priority queue that is very fast. Somewhat
032 * surprisingly, it doesn't need indices like gdx-ai's IndexedAStarPathFinder needs, and this makes the code easier to
033 * use. There's a good amount of other useful algorithms here, like {@link #detectCycle()} and various non-A-Star search
034 * methods, like {@link #breadthFirstSearch(Object, int, int)} and {@link #depthFirstSearch(Object, int, int)}.
035 * <br>
036 * This class is not meant to be extended from outside of this package, so it uses a package-private implementation.
037 * @param <V> the vertex type; often {@link squidpony.squidmath.Coord}
038 * @author earlygrey
039 */
040public class Algorithms<V> {
041
042    final Graph<V> graph;
043    final AlgorithmImplementations<V> implementations;
044
045    Algorithms(Graph<V> graph) {
046        this.graph = graph;
047        implementations = new AlgorithmImplementations<>(graph);
048    }
049
050    //--------------------
051    //  Shortest Path
052    //--------------------
053
054    /**
055     * Find the shortest path between the start and target vertices, using Dijkstra's algorithm implemented with a priority queue.
056     * @param start the starting vertex
057     * @param target the target vertex
058     * @return a list of vertices from start to target containing the ordered vertices of a shortest path, including both the start and target vertices
059     */
060    public ArrayList<V> findShortestPath(V start, V target) {
061        return findShortestPath(start, target, null);
062    }
063
064    /**
065     * Find the shortest path between the start and target vertices, using the A* search algorithm with the provided heuristic, and implemented with a priority queue.
066     * @param start the starting vertex
067     * @param target the target vertex
068     * @return a list of vertices from start to target containing the ordered vertices of a shortest path, including both the start and target vertices
069     */
070    public ArrayList<V> findShortestPath(V start, V target, Heuristic<V> heuristic) {
071        ArrayList<V> list = new ArrayList<>();
072        findShortestPath(start, target, list, heuristic);
073        return list;
074    }
075
076    /**
077     * Find the shortest path between the start and target vertices, using the A* search algorithm with the provided
078     * heuristic, and implemented with a priority queue. Fills path with a list of vertices from start to target
079     * containing the ordered vertices of a shortest path, including both the start and target vertices.
080     * @param start the starting vertex
081     * @param target the target vertex
082     * @param path the list of vertices to which the path vertices should be added
083     * @return true if a path was found, or false if no path could be found
084     */
085    public boolean findShortestPath(V start, V target, ArrayList<V> path, Heuristic<V> heuristic) {
086        path.clear();
087        Node<V> startNode = graph.getNode(start);
088        Node<V> targetNode = graph.getNode(target);
089        if (startNode==null || targetNode==null) throw new IllegalArgumentException("At least one vertex is not in the graph");
090        implementations.findShortestPath(startNode, targetNode, path, heuristic);
091        return !path.isEmpty();
092    }
093
094    /**
095     * Find the length of the shortest path between the start and target vertices, using Dijkstra's algorithm implemented with a priority queue.
096     * @param start the starting vertex
097     * @param target the target vertex
098     * @return the sum of the weights in a shortest path from the starting vertex to the target vertex
099     */
100    public double findMinimumDistance(V start, V target) {
101        return implementations.findMinimumDistance(graph.getNode(start), graph.getNode(target));
102    }
103
104    //--------------------
105    // Graph Searching
106    //--------------------
107
108    /**
109     * Perform a breadth first search starting from the specified vertex.
110     * @param v the vertex at which to start the search
111     * @param maxVertices the maximum number of vertices to process before terminating the search
112     * @param maxDepth the maximum edge distance (the number of edges in a shortest path between vertices) a vertex should have to be
113     *                 considered for processing. If a vertex has a distance larger than the maxDepth, it will not be added to the
114     *                 returned graph
115     * @return a Graph object containing all the processed vertices, and the edges from which each vertex was encountered.
116     * The vertices and edges in the returned graph will be in the order they were encountered in the search, and this will be
117     * reflected in the iteration order of the collections returned by {@link Graph#getVertices()} and {@link Graph#getEdges()}.
118     */
119    public Graph<V> breadthFirstSearch(V v, int maxVertices, int maxDepth) {
120        Node<V> node = graph.getNode(v);
121        if (node==null) throw new IllegalArgumentException("At least one vertex is not in the graph");
122        Graph<V> tree = graph.createNew();
123        implementations.breadthFirstSearch(node, tree, maxVertices, maxDepth);
124        return tree;
125    }
126
127    /**
128     * Perform a breadth first search starting from the specified vertex.
129     * @param v the vertex at which to start the search
130     * @return a Graph object containing all the processed vertices (all the vertices in this graph), and the edges from which each vertex was encountered.
131     * The vertices and edges in the returned graph will be in the order they were encountered in the search, and this will be
132     * reflected in the iteration order of the collections returned by {@link Graph#getVertices()} and {@link Graph#getEdges()}.
133     */
134    public Graph<V> breadthFirstSearch(V v) {
135        return breadthFirstSearch(v, graph.size(), graph.size());
136    }
137
138    /**
139     * Perform a depth first search starting from the specified vertex.
140     * @param v the vertex at which to start the search
141     * @param maxVertices the maximum number of vertices to process before terminating the search
142     * @param maxDepth the maximum edge distance (the number of edges in a shortest path between vertices) a vertex should have to be
143     *                 considered for processing. If a vertex has a distance larger than the maxDepth, it will not be added to the
144     *                 returned graph
145     * @return a Graph object containing all the processed vertices, and the edges from which each vertex was encountered.
146     * The vertices and edges in the returned graph will be in the order they were encountered in the search, and this will be
147     * reflected in the iteration order of the collections returned by {@link Graph#getVertices()} and {@link Graph#getEdges()}.
148     */
149    public Graph<V> depthFirstSearch(V v, int maxVertices, int maxDepth) {
150        Node<V> node = graph.getNode(v);
151        if (node==null) throw new IllegalArgumentException("At least one vertex is not in the graph");
152        Graph<V> tree = graph.createNew();
153        implementations.depthFirstSearch(node, tree, maxVertices, maxDepth);
154        return tree;
155    }
156
157    /**
158     * Perform a depth first search starting from the specified vertex.
159     * @param v the vertex at which to start the search
160     * @return a Graph object containing all the processed vertices (all the vertices in this graph), and the edges from which each vertex was encountered.
161     * The vertices and edges in the returned graph will be in the order they were encountered in the search, and this will be
162     * reflected in the iteration order of the collections returned by {@link Graph#getVertices()} and {@link Graph#getEdges()}.
163     */
164    public Graph<V> depthFirstSearch(V v) {
165        return depthFirstSearch(v, graph.size(), graph.size());
166    }
167
168
169    //--------------------
170    //  Structures
171    //--------------------
172
173    /**
174     * Checks whether there are any cycles in the graph using depth first searches.
175     * @return true if the graph contains a cycle, false otherwise
176     */
177    public boolean detectCycle() {
178        return implementations.containsCycle(graph);
179    }
180
181    //--------------------
182    //  Structures
183    //--------------------
184
185    /**
186     * Gets the identifier for the last run of an algorithm; this should mostly be usable internally, but may be useful
187     * when different runs have affected a graph and a {@link Node#lastRunID} may need to be checked against this.
188     * @return an identifier for which algorithm ran last, as a probably-unique int
189     */
190    public int lastRunID() {
191        return implementations.getRunID();
192    }
193
194}