import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import org.jgrapht.DirectedGraph; import org.jgrapht.graph.SimpleDirectedWeightedGraph; /** * This class represents the implementation of the A* algorithm */ public abstract class PuzzleAStar { private DirectedGraph graph; // vertex: PuzzleMove // edge: Integer (use an int value as a unique ID) private PuzzleMove startState; // the start state of the puzzle private PuzzleMove destinationState; // the destination state of the puzzle private PuzzleMove currentState; // the current explored puzzle state private List movesToProcess; // the number of moves not taken yet private long startPuzzleTime; // Timing variables private long endPuzzleTime; /** * Initializes PuzzleAStar */ void init() { //Create ArrayList for moves movesToProcess = new ArrayList(); //Create graph graph = new SimpleDirectedWeightedGraph(Integer.class); //Set currentState to startState this.currentState = startState; //Add StartState to graph graph.addVertex(startState); } /** * This method implements the A* algorithm for the puzzle */ public void findDestination() { //check input data if(currentState == null){ return; } //save the start time startPuzzleTime = System.currentTimeMillis(); //integer identification for edges int edgecount = 0; //Add all possible Moves of startState to movesToProcess movesToProcess.addAll(currentState.successorMoves()); //Do as long destination is not reached while(!currentState.equals(destinationState)) { PuzzleMove nextstep = null; for(int i=0; i< movesToProcess.size(); i++) { //calculate distance of Move - need to be done here cuz PuzzleMove has no //access to destinationState movesToProcess.get(i).calculateDistance(this.destinationState); //identify best move in movesToProcess if(nextstep == null) { //no move found till now nextstep = movesToProcess.get(i); } else { //is move better then last identified? if(nextstep.getCost() > movesToProcess.get(i).getCost()) { nextstep = movesToProcess.get(i); } } } //movesToProcess is empty. There is no connection from start to destination if(nextstep == null) { //no way found break; } //try to add identified step. If not possible it is already in the graph if(graph.addVertex(nextstep)) { //Add an edge from identified step's predecessor to identified step graph.addEdge(nextstep.getPredecessorMove(), nextstep,edgecount++); //now continue with identified step currentState = nextstep; //add all successorMoves to movesToProcess movesToProcess.addAll(currentState.successorMoves()); } //remove the identified step from movesToProcess movesToProcess.remove(nextstep); } //save end time endPuzzleTime = System.currentTimeMillis(); } /** * This method prints a summary of the solution * * @param state The final puzzle state (Destination) */ public void printSolutionSummary(PuzzleMove state) { System.out.println("Time needed to find Destination (ms): " + (this.endPuzzleTime-this.startPuzzleTime)); System.out.println("Current cost at final state (Destination): " + state.getCost()); System.out.println("States processed so far: " + numberOfProcessedStates()); System.out.println("States identified for processing (not being processed): " + numberOfStatesToProcess()); } /** * This method prints the path to the solution * * Make sure you called findDestination() first. * This method only works if the A* Algorithm was * calculated before. * * Uses currentState & destinationState * * @param graph */ public void printPathToSolution(DirectedGraph graph) { System.out.println("\nThe steps/path needed to reach the destination:\n"); //Check if A* was successful if(!currentState.equals(destinationState)){ System.out.println("No Path to Solution found"); return; } //Add String representation of destinationState String result = currentState.toString(); PuzzleMove curMove = destinationState; //Move from destination to start while(!curMove.equals(startState)) { //get incoming edges Set s = graph.incomingEdgesOf(curMove); //No or more then one edge found - something is wrong if(s.size() != 1) { System.out.println("An Error occured: Graph destination has no or to many connections to start"); return; } //only look at first edge Iterator it = s.iterator(); curMove = graph.getEdgeSource(it.next()); //Add String representation before last steps result = curMove.toString() + result; } //print the result System.out.println(result); } /** * The number of states identified for processing but not processed yet * @return the number of moves not taken yet */ int numberOfStatesToProcess() { return this.movesToProcess.size(); } /** * The number of states/puzzle moves processed so far * @return the number of edges */ int numberOfProcessedStates() { return this.graph.vertexSet().size(); } /** * @return the graph */ public DirectedGraph getGraph() { return this.graph; } /** * @return the currentState */ public PuzzleMove getCurrentState() { return this.currentState; } /** * @param destinationState the destinationState to set */ public void setDestinationState(PuzzleMove destinationState) { this.destinationState = destinationState; } /** * @param startState the startState to set */ public void setStartState(PuzzleMove startState) { this.startState = startState; } /** * @return the startState */ public PuzzleMove getStartState() { return this.startState; } /** * @return the destinationState */ public PuzzleMove getDestinationState() { return this.destinationState; } /** * @return the movesToProcess */ public List getMovesToProcess() { return this.movesToProcess; } }