/******************************************************************************
* Compilation: javac SolverVisualizer.java
* Execution: java SolverVisualizer filename
* Dependencies: Solver.java Board.java
*
* This program takes the name of a file as a command-line argument.
* From that file, it
*
* - Uses your Solver.java and Board.java programs to solve the
* sliding block puzzle defined by the input file.
* - Renders a graphical animation of your program's output.
* - Uses the manhattan() method in Board to display the
* Manhattan distance at each stage of the solution.
*
* by Martin Round. September 2014 Coursera Algorithms, Part I course
*
* % java SolverVisualizer puzzle07.txt
*
******************************************************************************/
import java.awt.Color;
import java.awt.Font;
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdOut;
public class SolverVisualizer {
// time in milliseconds to animate each move of the solution
private static final int ANIMATE_TIME = 800;
// time in milliseconds to pause after each move
private static final int PAUSE_TIME = 300;
// time in milliseconds for each frame of animation. 20 is sensible
private static final int FRAME_TIME = 20;
// background color of tile
private static final Color TILE_BACKGROUND_COLOR = Color.WHITE;
// foreground color of tile
private static final Color TILE_FOREGROUND_COLOR = Color.BLACK;
// background color of board
private static final Color BOARD_COLOR = Color.LIGHT_GRAY;
// text color
private static final Color TEXT_COLOR = Color.BLACK;
private static int n; // dimension of grid
private static int[][] tileAt; // number of tile at [row][column]
private static int movingTile; // which tile is moving, or 0 if none
private static int totalMoves; // number of moves to solve puzzle
private static int currentMoves; // number of moves currently displayed
private static int manhattan; // Manhattan distance currently displayed
private static String title;
// Draw one frame of animation.
// If the start position is being displayed (movingTile == 0) or during
// the PAUSE_TIME following each animated move, the display is unchanged.
// moveTime is the number of milliseconds since the current move began.
private static void refresh(int moveTime) {
// find where the empty space (0 tile) is located
int emptyRow = 0;
int emptyCol = 0;
while (tileAt[emptyRow][emptyCol] != 0) {
if (++emptyCol == n) {
emptyCol = 0;
++emptyRow;
}
}
StdDraw.clear();
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.setXscale(-0.1*n, 1.1*n);
StdDraw.setYscale(-0.1*n, 1.1*n);
StdDraw.setPenRadius(0.03 / n);
int fontSize = 200 / n;
StdDraw.setFont(new Font("SansSerif", Font.PLAIN, fontSize));
StdDraw.setPenColor(BOARD_COLOR);
StdDraw.filledSquare(n/2.0, n/2.0, n/1.96);
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
double x = col + 0.5;
double y = n - row - 0.5;
if (tileAt[row][col] == movingTile) {
double moveFraction = (double) moveTime / ANIMATE_TIME;
if (moveFraction > 1.0) moveFraction = 1.0;
x += (1.0 - moveFraction) * (emptyCol - col);
y -= (1.0 - moveFraction) * (emptyRow - row);
}
if (tileAt[row][col] != 0) {
StdDraw.setPenColor(TILE_BACKGROUND_COLOR);
StdDraw.filledSquare(x, y, 0.475);
StdDraw.setPenColor(TILE_FOREGROUND_COLOR);
StdDraw.square(x, y, 0.475);
StdDraw.text(x, y - 0.05, "" + tileAt[row][col]);
}
}
}
// write status text
StdDraw.setFont(new Font("SansSerif", Font.PLAIN, 12));
StdDraw.setPenColor(TEXT_COLOR);
StdDraw.text(0.100 * n, -0.06 * n, currentMoves + "/" + totalMoves + " moves");
StdDraw.text(0.775 * n, -0.06 * n, "Manhattan distance: " + manhattan);
StdDraw.text(0.500 * n, 1.06 * n, title);
}
private static void animateMove() {
int milliseconds = 0;
while (milliseconds < ANIMATE_TIME + PAUSE_TIME) {
refresh(milliseconds);
StdDraw.show();
StdDraw.pause(FRAME_TIME);
milliseconds += FRAME_TIME;
}
}
public static void main(String[] args) {
StdDraw.enableDoubleBuffering();
// for each command-line argument
for (String filename : args) {
// read in the board specified in the filename
In in = new In(filename);
n = in.readInt();
tileAt = new int[n][n];
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
tileAt[row][col] = in.readInt();
}
}
title = filename;
movingTile = 0; // show initial state for first 'move time'
// solve the slider puzzle
Board initial = new Board(tileAt);
manhattan = initial.manhattan();
long start = System.currentTimeMillis();
if (!initial.isSolvable()) {
long now = System.currentTimeMillis();
title += " (no solution possible)";
movingTile = 0;
currentMoves = 0;
animateMove(); // display start position only
}
else {
Solver solver = new Solver(initial);
totalMoves = solver.moves();
currentMoves = 0;
for (Board board : solver.solution()) {
manhattan = board.manhattan();
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
int tile = board.tileAt(row, col);
// if this position was previously empty
if (tileAt[row][col] == 0)
movingTile = tile; // animate the tile into it
tileAt[row][col] = tile;
}
}
animateMove(); // show move (or static initial state 1st time)
currentMoves++;
}
// show final position for one extra 'move time' before (possibly)
// moving on to display next puzzle solution.
StdDraw.show();
StdDraw.pause(ANIMATE_TIME + PAUSE_TIME);
}
StdOut.println(title);
}
}
}