278 lines
No EOL
7.4 KiB
Java
278 lines
No EOL
7.4 KiB
Java
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() {
|
|
for (int[] row : board) {
|
|
for (int i = 0; i < row.length; ++i) {
|
|
row[i] = 0;
|
|
}
|
|
}
|
|
// 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}
|
|
* <p>
|
|
* 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}
|
|
* <p>
|
|
* This is <b>not</b> 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;
|
|
}
|
|
|
|
// Ihe the number is already present in the cell
|
|
if (board[row][col] == num) {
|
|
return true;
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
} |