244 lines
6.1 KiB
Java
244 lines
6.1 KiB
Java
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<PuzzleMove, Integer> 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<PuzzleMove> 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<PuzzleMove>();
|
|
|
|
//Create graph
|
|
graph = new SimpleDirectedWeightedGraph<PuzzleMove, Integer>(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<PuzzleMove, Integer> 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<Integer> 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<Integer> 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<PuzzleMove, Integer> 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<PuzzleMove> getMovesToProcess() {
|
|
return this.movesToProcess;
|
|
}
|
|
}
|