package sudoku; /** Solver is a class that implements the SudokuSolver interface */ public class Solver implements SudokuSolver { private int[][] board = null; private int tries = 0; /** Constructor */ public Solver() { board = new int[9][9]; } /** {@inheritDoc} */ @Override public void setBoard(int[][] board) throws IllegalArgumentException, NullPointerException { if (board == null) throw new NullPointerException("Board cannot be null"); if (board.length != 9 || board[0].length != 9) throw new IllegalArgumentException("Board must be 9x9"); this.board = board; } /** {@inheritDoc} */ @Override public int[][] getBoard() { return board; } /** Resets the board to all zeros */ @Override public void clear() { board = new int[9][9]; } /* {@inheritDoc} */ @Override public boolean solve() { return solve(0, 0); } /** * Recursive helper method for solve() * * @param row row to solve * @param col column to solve * @return true if solved */ private boolean solve(int row, int col) { if (++tries >= 10000) { return false; } if (row < 0 || row > 9 || col < 0 || col > 9) { return false; } // If row is 9, drop down to the next column, if column is 9, we are done if (row == 9) { row = 0; if (++col == 9) { return true; } } // If we have a "number" in the current cell // recursively call solve() on the next cell // until we find a zero (empty cell) if (board[row][col] != 0) { return solve(row + 1, col); } // Check for legal values in the current cell for (int val = 1; val <= 9; ++val) { if (isLegal(row, col, val)) { board[row][col] = val; // When we find a legal value, recursively call solve() on the next cell if (solve(row + 1, col)) { return true; } } } // Reset the current cell to zero and backtrack board[row][col] = 0; return false; } /** * {@inheritDoc} *
* Default difficulty is 3 */ public void randomizeBoard() { randomizeBoard(3); } /** {@inheritDoc} */ @Override public void randomizeBoard(int difficulty) { int amount_prefilled = (difficulty * 9) + 1; this.clear(); for (int i = 0; i < amount_prefilled; ++i) { int row = (int) (Math.random() * 9); int col = (int) (Math.random() * 9); int val = (int) (Math.random() * 9) + 1; if (isLegal(row, col, val)) { board[row][col] = val; } } // Recursively call randomizeBoard() until we get a solvable board // This is expensive, and there should be a better way to do this if (!isSolvable()) { randomizeBoard(difficulty); } } /** * {@inheritDoc} *
* This is not checked for validity */ @Override public void set(int row, int col, int val) { if (row < 9 && col < 9) { board[row][col] = val; } } /** {@inheritDoc} */ @Override public int get(int row, int col) { if (row < 9 && col < 9) { return board[row][col]; } return 0; } /** {@inheritDoc} */ @Override public boolean isLegal(int row, int col, int num) { // Sanity check if (row < 0 || row >= 9 || col < 0 || col >= 9 || num < 1 || num > 9) { return false; } // Check both the row and column for (int i = 0; i < 9; i++) { if (board[row][i] == num || board[i][col] == num) { return false; // 'num' is already in the row or column } } // Check the 3x3 box int boxRowOffset = (row / 3) * 3; int boxColOffset = (col / 3) * 3; for (int k = 0; k < 3; ++k) { for (int m = 0; m < 3; ++m) { if (num == board[boxRowOffset + k][boxColOffset + m]) { return false; } } } // None of the above failed, so it is legal return true; } /** {@inheritDoc} */ public boolean isSolvable() { // We want to work on a copy int[][] copy = new int[9][9]; for (int row = 0; row < 9; row++) { System.arraycopy(board[row], 0, copy[row], 0, 9); } Solver copyModel = new Solver(); copyModel.setBoard(copy); return copyModel.solve(); } /** * Checks if the board is solved * * @return true if solved */ @Override public boolean isSolved() { return isSolved(0, 0); } /** * Recursive helper method for isSolved() * * @param row * @param col * @return true if solved */ private boolean isSolved(int row, int col) { // If we are at the 9th row and 0th column (the last cell), we are done if (row == 9) { row = 0; if (++col == 9) { return true; } } // If we find a zero, the board is not solved if (board[row][col] == 0) { return false; } for (int i = 0; i < 9; ++i) { // Checks if there is a duplicate in the row if (i != row && board[i][col] == board[row][col]) { return false; } // Checks if there is a duplicate in the column if (i != col && board[row][i] == board[row][col]) { return false; } } // Checks if there is a duplicate in the 3x3 box int boxRowOffset = (row / 3) * 3; int boxColOffset = (col / 3) * 3; for (int k = 0; k < 3; ++k) { for (int m = 0; m < 3; ++m) { int r = boxRowOffset + k; int c = boxColOffset + m; if ((r != row || c != col) && board[r][c] == board[row][col]) { return false; } } } return isSolved(row + 1, col); } /** * Returns a string representation of the board * * @return String representation of the board */ // Not particularly pretty, but it works public String toString() { final String divider = "--------+-------+--------\n"; StringBuilder sb = new StringBuilder(); sb.append(divider); int rowcount = 0; for (int[] row : board) { int colcount = 0; sb.append("| "); for (int val : row) { colcount++; sb.append(val); sb.append(colcount % 3 == 0 ? " | " : " "); } rowcount++; sb.append(rowcount % 3 == 0 ? "\n" : ""); sb.append(rowcount % 3 == 0 ? divider : "\n"); } return sb.toString(); } }