public class Puzzle
{private int n;
private int missingCellRow;
private int missingCellCol;
private int[][] cells;
private int moves;
private long startTimeMillis;public Puzzle(int n)
{this.n = n; this.missingCellRow = n-1; this.missingCellCol = n-1; cells = new int[n][n]; setCells(); scramble();}
public int getSize()
{return n;
}private void setCells()
{for (int i=0; i<n*n; i++) { setCell(i, i+1); } setCell(missingCellRow*n+missingCellCol, -1);}
public int getCell(int row, int col) {
return cells[row][col];
}public int getCell(int index)
{return getCell(index/n, index%n); }
public void setCell(int row, int col, int value) {
cells[row][col] = value;
}public void setCell(int index, int value) {
setCell(index/n, index%n, value); }
public int getMissingCellRow()
{return missingCellRow;
}public int getMissingCellCol()
{return missingCellCol;
}public boolean isCellMovable(int row, int col) {
// corner cells if (missingCellRow == 0 && missingCellCol == 0) { return ((row==0 && col==1) || (row==1 && col==0)); } else if (missingCellRow == 0 && missingCellCol == n-1) { return ((row==0 && col==n-2) || (row==1 && col==n-1)); } else if (missingCellRow == n-1 && missingCellCol == 0) { return ((row==n-2 && col==0) || (row==n-1 && col==1)); } else if (missingCellRow == n-1 && missingCellCol == n-1) { return ((row==n-2 && col==n-1) || (row==n-1 && col==n-2)); } // side cells not at corners else if (missingCellRow == 0) { return ((row==0 && col==missingCellCol-1) || (row==0 && col==missingCellCol+1) || (row==1 && col==missingCellCol)); } else if (missingCellRow == n-1) { return ((row==n-1 && col==missingCellCol-1) || (row==n-1 && col==missingCellCol+1) || (row==n-2 && col==missingCellCol)); } else if (missingCellCol == 0) { return ((col==0 && row==missingCellRow-1) || (col==0 && row==missingCellRow+1) || (col==1 && row==missingCellRow)); } else if (missingCellCol == n-1) { return ((col==n-1 && row==missingCellRow-1) || (col==n-1 && row==missingCellRow+1) || (col==n-2 && row==missingCellRow)); } // rest of cells else { return ((row==missingCellRow-1 && col==missingCellCol) || (row==missingCellRow && col==missingCellCol-1) || (row==missingCellRow && col==missingCellCol+1) || (row==missingCellRow+1 && col==missingCellCol)); }}
public boolean isCellMovable(int index) {
return isCellMovable(index/n, index%n); }
public void move(int row, int col)
{setCell(missingCellRow, missingCellCol, getCell(row, col)); missingCellRow = row; missingCellCol = col; setCell(missingCellRow*n+missingCellCol, -1); moves++;}
public void move(int index)
{move(index/n, index%n);
}public void scramble()
{for (int i=0; i<n*n*n; i++) { int index; do { index = (int)(Math.random()*n*n); } while (!isCellMovable(index)); move(index); } moves = 0;}
public boolean isDone()
{for (int index=0; index<n*n-1; index++) { if (getCell(index) != index+1) return false; } return true;}
public int getMoves()
{return moves;
}public void startTimer()
{startTimeMillis = System.currentTimeMillis(); }
public String getElapseTime()
{int seconds = (int) ((System.currentTimeMillis() - startTimeMillis) / 1000); int minute = seconds / 60; int second = seconds % 60; return minute+"' "+second+"''";}
}