/* ***************************************************************************** * Compilation: javac-algs4 SolverVisualizer.java * Execution: java-algs4 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-algs4 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(); if (!initial.isSolvable()) { 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); } } }