/** * Write a description of class FifteenPuzzle here. * * @author Lyndon While */ import java.awt.event.*; import java.awt.Color; public class FifteenPuzzleLyndon implements MouseListener { private int[][] grid; // the current positions of the tiles and space, denoted by 0..15 private int xspace; // xspace,yspace are the current coordinates of the space private int yspace; private SimpleCanvas sc; // the canvas for display private final int size = 4; // the number of tiles across and down private final int tilesize = 100; // the size of a tile private final int gridsize = size * tilesize; // the size of the grid private final int offset = tilesize / 10; // Declaring the colours here makes it easier to make pretty combinations private Color usual = Color.blue; private Color legal = Color.green; private Color finished = Color.red; private Color shading = Color.black; private Color writing = Color.white; private Color bgColor = Color.white; private int[][] goal = {{1,5,9,13}, {2,6,10,14}, {3,7,11,15}, {4,8,12,0}}; // these two are public so that they can be used in BlueJ public static int[][] close = {{1,5,9,13}, {2,6,10,14}, {3,7,11,0}, {4,8,12,15}}; public static int[][] example = {{5,11,14,0}, {9,3,13,7}, {2,8,10,12}, {4,1,15,6}}; // this constructor sets up the grid as initialGrid and displays it on the canvas // (plus it initialises the other instance variables) public FifteenPuzzleLyndon (int[][] initialGrid) { // first check if the grid is a proper 2D object and is the right shape if (initialGrid == null) throw new NullPointerException("null grid"); if (initialGrid.length != size) throw new IllegalArgumentException("Grid does not have " + size + " rows"); for (int i = 0; i < size; i++) if (initialGrid[i] == null) throw new NullPointerException("null row"); else if (initialGrid[i].length != size) throw new IllegalArgumentException("Grid is not square"); grid = initialGrid; // now check if it contains the numbers 0..size^2-1 boolean[] found = new boolean[size * size]; for (int i = 0; i < size; i++) for (int j = 0; j < size; j++) if (grid[i][j] < 0) throw new IllegalArgumentException("Negative square found: " + grid[i][j]); else if (grid[i][j] > size * size - 1) throw new IllegalArgumentException("Overflow square found: " + grid[i][j]); else if (found[grid[i][j]]) throw new IllegalArgumentException("Grid has multiple " + grid[i][j] + "s"); else { found[grid[i][j]] = true; if (grid[i][j] == 0) {xspace = i; yspace = j;} } sc = new SimpleCanvas("Fifteen Puzzle", gridsize, gridsize, bgColor); sc.addMouseListener(this); drawGrid(); } // this constructor sets up the grid as goal, // then it makes random moves to set up the puzzle and displays it on the canvas // (plus it initialises the other instance variables) public FifteenPuzzleLyndon () { grid = goal; xspace = 3; yspace = 3; sc = new SimpleCanvas("Fifteen Puzzle", gridsize, gridsize, bgColor); sc.addMouseListener(this); drawGrid(); int k, j; // try lots of random moves (some moves will fail, but no problem...) for (int i = 0; i < 1000; i++) { // xspace-1 <= k <= xspace+1 k = xspace + (int) Math.round(Math.random() * 2 - 1); if (k == xspace) // j must be yspace-1 or yspace+1 j = yspace + (int) (Math.random() * 2) * 2 - 1; else // j must be yspace j = yspace; // if k,j is on the board if (0 <= k && k < size && 0 <= j && j < size && legalClick(k, j)) moveTile(k, j); } } // returns true iff x,y is adjacent to the space private boolean legalClick(int x, int y) { // either x = xspace and y is 1 different to yspace, or vice versa return Math.abs(x - xspace) + Math.abs(y - yspace) == 1; } // returns true iff the puzzle is finished private boolean finished() { for (int i = 0; i < size; i++) for (int j = 0; j < size; j++) if (grid[i][j] != (j * size + i + 1) % (size * size)) return false; return true; } // moves the tile at x,y into the space, if x,y is adjacent, and re-draws the grid; o/w do nothing public void moveTile (int x, int y) { if (!(0 <= x && x < size)) throw new IllegalArgumentException(x + " is an illegal x-coordinate"); if (!(0 <= y && y < size)) throw new IllegalArgumentException(y + " is an illegal y-coordinate"); if (!legalClick(x, y)) throw new IllegalArgumentException(x + "," + y + " not adjacent to space"); grid[xspace][yspace] = grid[x][y]; grid[x][y] = 0; xspace = x; yspace = y; drawGrid(); } // draws the current grid on the canvas private void drawGrid() { Color c; // Re-draw the grid for (int i = 0; i < size; i++) for (int j = 0; j < size; j++) {// Choose the colour if (grid[i][j] == 0) c = bgColor; else if (finished()) c = finished; else if (legalClick(i, j)) c = legal; else c = usual; drawTile (i, j, c); } if (finished()) { sc.setForegroundColour(Color.black); // these magic numbers tweak the words into the right spot sc.drawString ("Well", 3 * tilesize + tilesize / 5, 3 * tilesize + tilesize / 10 * 5); sc.drawString ("done", 3 * tilesize + tilesize / 6, 3 * tilesize + tilesize / 10 * 8); } sc.repaint(); } // draws the tile at x,y in colour c at the appropriate spot on the canvas private void drawTile(int x, int y, Color c) {// Add the shading sc.setForegroundColour(shading); sc.drawRectangle( x * tilesize + offset * 2, y * tilesize + offset * 2, (x + 1) * tilesize, (y + 1) * tilesize); // Colour the tile sc.setForegroundColour(c); sc.drawRectangle( x * tilesize + offset, y * tilesize + offset, (x + 1) * tilesize - offset, (y + 1) * tilesize - offset); // Add the number sc.setForegroundColour(writing); sc.drawString(String.valueOf(grid[x][y]), // these magic numbers tweak the position of the number x * tilesize + tilesize / 2 - (grid[x][y] / 10 * 4 + 2), y * tilesize + tilesize / 2 + tilesize / 10); } public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e){moveTile(e.getX() / tilesize, e.getY() / tilesize);} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} }