import java.util.Random; /* An instance of this class represents a configuration of a Slider Puzzle. ** Such a puzzle is "played" on a rectangular grid each of whose squares, ** except for one, is occupied by a "tile". Common versions of this class ** of puzzles are the 15-Puzzle and 8-Puzzle, which are played on grids ** with dimensions 4-by-4 and 3-by-3, respectively. ** ** Below and left is one possible configuration of the 8-Puzzle. Notice that ** the eight tiles are numbered 1 through 8. The square labeled with a dash ** is the "unoccupied" square, as there is no tile there. A player makes a ** "move" by shifting, or sliding, a tile into what had been the unoccupied ** square. A tile can slide only if it occupies a square that is adjacent to ** the unoccupied square, horizontally or vertically. In the configuration ** shown, only tiles 1, 4, and 5 can slide. ** ** To "solve" a puzzle, which begins with the tiles randomly arranged among ** the squares, the player makes a sequence of moves that results in the ** "goal" configuration, shown below on the right. ** ** random standard goal ** configuration configuration ** ** +---+---+---+ +---+---+---+ ** 0 | 4 | - | 1 | | 1 | 2 | 3 | ** +---+---+---+ +---+---+---+ ** 1 | 8 | 5 | 2 | | 4 | 5 | 6 | ** +---+---+---+ +---+---+---+ ** 2 | 3 | 7 | 6 | | 7 | 8 | - | ** +---+---+---+ +---+---+---+ ** 0 1 2 0 1 2 ** ** Authors: R. McCloskey and < STUDENT's NAME > ** Date: April 2022 ** Collaborated with: ... ** Known Defects: ... */ public class SliderPuzzleConfig { // class constants // --------------- public static final char UP = 'U'; // Codes for the four possible public static final char DOWN = 'D'; // directions in which the public static final char LEFT = 'L'; // unoccupied square can "move" public static final char RIGHT = 'R'; public static final String directions = "" + UP + DOWN + LEFT + RIGHT; // instance variable // ----------------- private int[][] grid; // grid[i][j] identifies the tile in row i, column j // constructors // ------------ /* Initializes this new Slider Puzzle Configuration to have the specified ** dimensions and to match the standard goal configuration. */ public SliderPuzzleConfig(int numRows, int numCols) { grid = standardGoalGrid(numRows, numCols); } /* Initializes this new Slider Puzzle Configuration to be identical to ** the given one. */ public SliderPuzzleConfig(SliderPuzzleConfig spc) { this.grid = spc.copyOfGrid(); } /* Initializes this new Slider Puzzle Configuration to have the specified ** dimensions and to be the result of applying the given number of moves, ** pseudorandomly generated using the given Random object, starting with ** the standard goal configuration. */ public SliderPuzzleConfig(int numRows, int numCols, Random rand, int numMoves) { this(numRows, numCols); applyRandomMoves(rand, numMoves); } /* Initializes this new Slider Puzzle Configuration to be that obtained ** by applying the given number of moves, pseudorandomly generated using ** the given Random object, starting with the given configuration. */ public SliderPuzzleConfig(SliderPuzzleConfig spc, Random rand, int numMoves) { this(spc); applyRandomMoves(rand, numMoves); } // observers // --------- /* Returns the number of rows in this Slider Puzzle Configuration. */ public int numRows() { return grid.length; } /* Returns the number of columns in this Slider Puzzle Configuration. */ public int numColumns() { return grid[0].length; } /* Reports whether or not, with respect to this Slider Puzzle Configuration, ** the blank square can move in the specified direction. */ public boolean canMove(char direction) { if (direction == UP) { return canMoveUp(); } else if (direction == DOWN) { return canMoveDown(); } else if (direction == LEFT) { return canMoveLeft(); } else if (direction == RIGHT) { return canMoveRight(); } else { return false; } // invalid direction } /* Reports whether or not the blank square can move up. */ public boolean canMoveUp() { return false; } // STUB /* Reports whether or not the blank square can move down. */ public boolean canMoveDown() { return false; } // STUB /* Reports whether or not the blank square can move left. */ public boolean canMoveLeft() { return false; } // STUB /* Reports whether or not the blank square can move right. */ public boolean canMoveRight() { return false; } // STUB /* Reports whether or not this Slider Puzzle Configuration is identical ** to the given one. */ public boolean equals(SliderPuzzleConfig other) { boolean equalSoFar = true; int numRows = numRows(); int numCols = numColumns(); if (numRows != other.numRows() || numCols != other.numColumns()) { equalSoFar = false; } else { // Dimensions of this.grid and other.grid are same; now determine // whether their contents are the same int r = 0, c = 0; while (equalSoFar && r != numRows) { if (c == numCols) // advance to beginning of next row { r = r+1; c = 0; } else if (this.grid[r][c] != other.grid[r][c]) { equalSoFar = false; } else { c = c+1; } // advance to next column in same row } } return equalSoFar; } /* Displays this Slider Puzzle Configuration on standard output. */ public void display() { int numRows = numRows(); int numCols = numColumns(); for (int i=0; i != numRows; i++) { for (int j=0; j != numCols; j++) { int tileNum = grid[i][j]; if (tileNum == 0) { System.out.printf(" - "); } // empty square else { System.out.printf("%2d ", grid[i][j]); } } System.out.println(); } } // mutators // -------- /* Modifies this Slider Puzzle Configuration by moving the blank square ** in the indicated direction. ** pre: canMove(direction) */ public void move(char direction) { if (direction == UP) { up(); } else if (direction == DOWN) { down(); } else if (direction == LEFT) { left(); } else if (direction == RIGHT) { right(); } else { } // invalid direction code; do nothing } /* Modifies this Slider Puzzle Configuration by moving the blank square up. ** pre: canMoveUp() */ public void up() { // STUB } /* Modifies this Slider Puzzle Configuration by moving the blank square down. ** pre: canMoveDown() */ public void down() { // STUB } /* Modifies this Slider Puzzle Configuration by moving the blank square ** to the left. ** pre: canMoveLeft() */ public void left() { // STUB } /* Modifies this Slider Puzzle Configuration by moving the blank square ** to the right. ** pre: canMoveRight() */ public void right() { // STUB } /* Applies the specified number of operations, generated by the ** given Random object. */ public void applyRandomMoves(Random rand, int numMoves) { int counter = 0; while (counter != numMoves) { char direction = randomDirection(rand); if (canMove(direction)) { move(direction); counter++; } } } // private methods // --------------- /* Returns one of UP, DOWN, LEFT, or RIGHT, pseudorandomly generated ** by the given Random object. */ private char randomDirection(Random rand) { int k = rand.nextInt(directions.length()); return directions.charAt(k); } /* Returns a new copy of this puzzle's grid. */ private int[][] copyOfGrid() { int numRows = numRows(); int numCols = numColumns(); int[][] result = new int[numRows][numCols]; for (int i = 0; i != numRows; i++) { for (int j = 0; j != numCols; j++) { result[i][j] = grid[i][j]; } } return result; } /* Returns a new grid of the specified dimensions and having ** contents that correspond to the standard solution. */ private int[][] standardGoalGrid(int numRows, int numCols) { int[][] goalGrid = new int[numRows][numCols]; int count = 1; for (int i = 0; i != numRows; i++) { for (int j = 0; j != numCols; j++) { goalGrid[i][j] = count; count = count + 1; } } goalGrid[numRows-1][numCols-1] = 0; // empty square indicated by zero return goalGrid; } }