import java.io.IOException;

/***********************************************************************************
 * Task / Aufgabe
 ***********************************************************************************
 * *  Implement the A* algorithm to solve the puzzle problem known from
 *    the exercises. Use the following heuristic to calculate the distance:
 * 
 *    - The distance from start is the number of steps needed to reach the current
 *      state, e.g., the direct neighbour state has distance 1 and so on.
 *    - The distance to destination is the number of blocks on wrong positions, e.g.,
 *       1 2 3		1 2 3
 *       4 5 6		4 5 6
 *       8 7 #		7 8 #
 *      (start)   (destination)
 *      The distance from start to destination would be 2. (Note: start cannot be solved.)
 *      (7 and 8 are on wrong positions)
 *	Hints: 	When testing, be aware that some puzzle states cannot be solved.
 *			Try not to consider predecessor states, since they have already 
 *			been processed. 
 *      
 * *  Implementieren Sie den A* Algorithmus, um das in den Übungen behandelte Puzzle 
 *    Problem zu lösen.
 *    Verwenden Sie folgende Heuristik für die Berechnung der Entfernungskosten:
 * 
 *    - Die Entfernung vom Startknoten ist die Anzahl der benötigten Schritte
 *      bis zum aktuellen Zustand, z.B. der unmittelbare Nachbarzustand hat
 *      die Entfernung 1, usw.
 *    - Die Entfernung zum Zielknoten ist die Anzahl der Puzzleteile, 
 *      die an den falschen Positionen sind, z.B.
 *       1 2 3		1 2 3
 *       4 5 6		4 5 6
 *       8 7 #		7 8 #
 *      (Start)    (Ziel)
 *      The Entfernung zwischen Start und Ziel wäre 2.(Achtung: Start ist unlösbar.)
 *      (7 und 8 sind an falschen Positionen)
 *	Hinweise: 	Achten Sie beim Testen, dass bestimmte Zustände möglicherweise
 *				nicht lösbar sind. Versuchen Sie die Vorgängerzustände nicht zu
 *				betrachten, da sie ja schon bearbeitet wurden. 
 ***********************************************************************************/

/***********************************************************************************
 * Questions / Fragen
 ***********************************************************************************
 * 
 * ----------------------------------------------------------------
 * a) Choose and implement another two heuristics to calculate the distance, 
 *    e.g., one could use the heuristic introduced in the exercises or similar 
 *    versions of it.
 * 	  Use TPTP to profile Puzzle.main() with all three heuristics. 
 * 	  i)  Which heuristic did you choose? Why? Discuss them.
 *    	  Which heuristic was the best? 
 *    ii) What was the largest puzzle (NxN) used?
 *    
 * Enter at least 5 different, meaningful runs in the table below. 
 * 
 * a) Wählen und implementieren Sie zwei zusätzliche Heuristiken für die 
 * 	  Berechnung der Enfernungskosten. Sie können z.B. die in der Übung
 *    vorgestellte Heuristik verwenden, oder ähnliches.
 *    Verwenden Sie TPTP um Puzzle.main() zu profilen.
 *    i)  Welche Heuristiken haben Sie gewählt? Wieso? Diskutieren Sie sie.
 *        Welche Heuristik war die beste? 
 *    ii) Welche Größe(NxN) hatten die verwendeten Puzzles?
 *    
 * Tragen Sie mindestens 5 verschiedene, aussagekräftige Resultate in der Tabelle ein.
 * ----------------------------------------------------------------
 * 
 *TODO
 * Heuristik 1: Anzahl der Elemente an der falschen Position
 * 				O(n^2); n = puzzlesize
 * 				
 * Heuristik 2: Entfernung der einzelnen Elemente von ihrer Position zur
 * 				Zielposition. Berechnet über ganze Felder:
 * 				x-distance + y-distance.
 * 				Diese Werte werden für alle Elemente in einem PuzzleMove
 * 				aufaddiert.
 * 				O(n^4); n = puzzlesize
 * 
 * Heuristik 3: Entfernun der einzelnen Elemente von ihrer  Position zur
 * 				Zielposition. Berechnet über Pythagoras:
 * 				sqrt(x-distance^2 + y-distance^2)
 * 				Diese Werte werden für alle Elemente in einem PuzzleMove
 * 				aufaddiert.
 * 				O(n^4); n = puzzlesize
 * 
 * TODO
 * Heuristik 2 am genausten, am effizientesten
 * Heuristik 1 am ungenausten
 * Heuristik 3 am zeitintensievsten
 * 
 * 
 * 				|    heuristic 1   	|    heuristic 2   	|   heuristic 3
 * -------------+-------+-----------+-------+-----------+-------+--------
 * NxN puzzle	|	ms	|  steps	|	ms	|  steps	|	ms	|  steps
 * -------------+-------+-----------+-------+-----------+-------+--------	
 * 3x3 test_00	| 15303	 8717		| 2246	 1474		| 26096	 3972	
 * 3x3 test_02	| 15383	 8717		| 2165	 1474		| 26022	 3972	
 * 3x3 test_03	| 2		 5			| 3		 5			| 2		 5	
 * 4x4 test_04	| 28	 143		| 49	 102		| 201	 134	
 * 4x4 test_05	|					|					|	
 *  
 *   ms    - time needed to find the solution
 *   steps - steps needed to reach the destination
 ***********************************************************************************/

/**
 * The class from where the puzzle is loaded and prepared for processing
 */

public class Puzzle extends PuzzleAStar
{
	/**
	 * Initialise the puzzle states (Start and Destination)
	 * 
	 * @param start
	 * @param destination
	 */
	public Puzzle (PuzzleMove start, PuzzleMove destination)
	{
		setStartState(start);
		setDestinationState(destination);
	}
	
	/**
	 * The main method
	 * 
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException
	{	
		// Read the PuzzleInput
		PuzzleInputOutput pio = new PuzzleInputOutput("test_05.txt");
		
		//Create and initialize the Puzzle
		Puzzle puzzle = new Puzzle(	new PuzzleMove(pio.getPuzzleStart()			,PuzzleMove.HEURISTIC_A),
									new PuzzleMove(pio.getPuzzleDestination()	,PuzzleMove.HEURISTIC_A));
		puzzle.init();
		
		//Calculate A*
		puzzle.findDestination();
		
		//Print Summary
		System.out.println("Summary:");
		System.out.println("\nHeuristic used: "+puzzle.getDestinationState().getHeuristic());
		System.out.println("\nStartState: ");
		PuzzleInputOutput.printPuzzle(puzzle.getStartState().getState());
		System.out.println("FinalState: ");
		PuzzleInputOutput.printPuzzle(puzzle.getDestinationState().getState());
		
		puzzle.printSolutionSummary(puzzle.getCurrentState());
		
		//puzzle.printPathToSolution(puzzle.getGraph());
	}
}
