Compare commits

..

No commits in common. "196f066281f287f6b2247b2ee28f36a562fde316" and "383af5be58c8c3b98aac40fe28b0e9fd58e997be" have entirely different histories.

3 changed files with 24 additions and 113 deletions

View file

@ -2,7 +2,6 @@ package sudoku;
public class Solver implements SudokuSolver {
private int[][] board = null;
private int tries = 0;
public Solver() {
board = new int[9][9];
@ -11,7 +10,6 @@ public class Solver implements SudokuSolver {
/**
* {@inheritDoc}
*/
@Override
public void setBoard(int[][] board) {
this.board = board;
}
@ -19,7 +17,6 @@ public class Solver implements SudokuSolver {
/**
* {@inheritDoc}
*/
@Override
public int[][] getBoard() {
return board;
}
@ -27,7 +24,6 @@ public class Solver implements SudokuSolver {
/**
* Resets the board to all zeros
*/
@Override
public void clear() {
board = new int[9][9];
}
@ -35,7 +31,6 @@ public class Solver implements SudokuSolver {
/**
* {@inheritDoc}
*/
@Override
public boolean solve() {
return solve(0, 0);
}
@ -48,12 +43,6 @@ public class Solver implements SudokuSolver {
* @return true if solved
*/
private boolean solve(int row, int col) {
if (++tries >= 10000) {
if (tries == 10000)
System.out.println(String.format("Likely unsolvable. Tries: %d", tries));
return false;
}
if (row < 0 || row > 9 || col < 0 || col > 9) {
return false;
}
@ -68,7 +57,6 @@ public class Solver implements SudokuSolver {
// 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);
}
@ -84,28 +72,16 @@ public class Solver implements SudokuSolver {
}
}
// Reset the current cell to zero and backtrack
board[row][col] = 0;
return false;
}
/**
* {@inheritDoc}
* <p>
* Default difficulty is 3
* Randomizes the board. This guarantees a solvable board.
*/
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) {
for (int i = 0; i < 9; ++i) {
int row = (int) (Math.random() * 9);
int col = (int) (Math.random() * 9);
int val = (int) (Math.random() * 9) + 1;
@ -113,18 +89,11 @@ public class Solver implements SudokuSolver {
board[row][col] = val;
}
}
// Recursively call randomizeBoard() until we get a solvable board
// This is expensive, and there should be some voodoo magic that computes this in n^2 time
if (!isSolvable()) {
randomizeBoard(difficulty);
}
}
/**
* {@inheritDoc}
*/
@Override
public void set(int row, int col, int val) {
if (row < 9 && col < 9) {
board[row][col] = val;
@ -134,7 +103,6 @@ public class Solver implements SudokuSolver {
/**
* {@inheritDoc}
*/
@Override
public int get(int row, int col) {
if (row < 9 && col < 9) {
return board[row][col];
@ -145,26 +113,32 @@ public class Solver implements SudokuSolver {
/**
* {@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) {
public boolean isLegal(int row, int col, int val) {
if (row < 0 || row >= 9 || col < 0 || col >= 9 || val < 1 || val > 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 if val is already in col
for (int i = 0; i < 9; ++i) {
if (val == board[i][col]) {
return false;
}
}
// Check if val is already in row
for (int j = 0; j < 9; ++j) {
if (val == board[row][j]) {
return false;
}
}
// 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]) {
if (val == board[boxRowOffset + k][boxColOffset + m]) {
return false;
}
}
@ -174,28 +148,11 @@ public class Solver implements SudokuSolver {
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);
}
@ -247,7 +204,7 @@ public class Solver implements SudokuSolver {
}
}
return isSolved(row + 1, col);
return true;
}
/**

View file

@ -7,7 +7,7 @@ public interface SudokuSolver {
* @param board a board to copy values from
* @throws IllegalArgumentException if board is invalid, e.g. not 9x9
*/
void setBoard(int[][] board) throws IllegalArgumentException;
void setBoard(int[][] board);
/**
* Get a copy of the sudoku board
@ -49,32 +49,6 @@ public interface SudokuSolver {
*/
void set(int row, int col, int nbr);
/**
* Randomize the board. Guaranteed to be solvable.
*/
public void randomizeBoard();
/**
* Randomize the board. Guaranteed to be solvable.
*
* @param difficulty 0-9, 0 being easiest
*/
void randomizeBoard(int difficulty);
/**
* Check if the board is solved
*
* @return true if solved
*/
boolean isSolved();
/**
* Checks if the board is solvable
*
* @return true if solvable
*/
boolean isSolvable();
/**
* Clear the board
*/

View file

@ -1,6 +1,6 @@
package sudoku;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.stream.IntStream;
@ -45,7 +45,6 @@ class SolverTest {
}
@Test
@RepeatedTest(100)
void solverTest() {
Solver solver = new Solver();
assertFalse(solver.isSolved());
@ -53,16 +52,12 @@ class SolverTest {
assertTrue(solver.isSolved());
solver.clear();
solver.randomizeBoard();
assertFalse(solver.isSolved());
assertTrue(solver.solve());
}
@Test
@RepeatedTest(100)
void randomizeBoardGuaranteeSolvableTest() {
Solver solver = new Solver();
solver.clear();
solver.randomizeBoard();
assertTrue(solver.solve());
assertFalse(solver.isSolved());
}
@Test
@ -84,26 +79,11 @@ class SolverTest {
}
@Test
@Disabled
void unsolvableTest() {
Solver solver = new Solver();
// Simple example
solver.clear();
solver.set(0, 0, 1);
solver.set(0, 1, 1);
assertFalse(solver.solve());
// More complex example
solver.clear();
solver.set(0, 5, 7);
solver.set(0, 6, 8);
solver.set(0, 7, 2);
solver.set(1, 5, 3);
solver.set(2, 7, 1);
solver.set(3, 1, 4);
solver.set(5, 2, 8);
solver.set(5, 8, 6);
solver.set(7, 1, 8);
assertFalse(solver.solve());
}
}