Solver working, basic test cases

This commit is contained in:
Imbus 2023-12-05 15:34:29 +01:00
parent 999d97f565
commit 75da119da9
4 changed files with 270 additions and 6 deletions

View file

@ -28,7 +28,7 @@ dependencies {
// Apply a specific Java toolchain to ease working on different environments. // Apply a specific Java toolchain to ease working on different environments.
java { java {
toolchain { toolchain {
languageVersion.set(JavaLanguageVersion.of(11)) languageVersion.set(JavaLanguageVersion.of(21))
} }
} }

View file

@ -1,5 +1,7 @@
package sudoku; package sudoku;
import java.util.stream.IntStream;
public class Solver implements SudokuSolver { public class Solver implements SudokuSolver {
private int[][] board; private int[][] board;
@ -16,17 +18,200 @@ public class Solver implements SudokuSolver {
} }
public boolean solve() { public boolean solve() {
return solve(0, 0);
} }
public Boolean isSolved() { /**
* Resets the board to all zeros
*/
public void reset() {
board = new int[9][9];
} }
/**
* 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) { private boolean solve(int row, int col) {
if (row < 0 || row > 9 || col < 0 || col > 9) {
return false;
}
if (row == 9) {
row = 0;
if (++col == 9) {
return true;
}
}
if (board[row][col] != 0) {
return solve(row + 1, col);
}
for (int val = 1; val <= 9; ++val) {
if (legal(row, col, val)) {
board[row][col] = val;
if (solve(row + 1, col)) {
return true;
}
}
}
board[row][col] = 0;
return false;
} }
public boolean legal(int row, int col, int nbr) { /**
* Randomizes the board. This guarantees a solvable board.
*/
public void randomizeBoard() {
this.reset();
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;
if (legal(row, col, val)) {
board[row][col] = val;
}
}
} }
/**
* Sets the value of the board at the given position
*
* @param row row to set
* @param col column to set
* @param val value to set
*/
public void setPos(int row, int col, int val) {
if (row < 9 && col < 9) {
board[row][col] = val;
}
}
/**
* Checks if val is legal in the given row, column, and 3x3 box
*
* @param row row to check
* @param col column to check
* @param val value to check
* @return true if val is legal
*/
public boolean legal(int row, int col, int val) {
if (row < 0 || row >= 9 || col < 0 || col >= 9 || val < 1 || val > 9) {
return false;
}
// 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 (val == board[boxRowOffset + k][boxColOffset + m]) {
return false;
}
}
}
// None of the above failed, so it is legal
return true;
}
public boolean isSolved() {
return isSolved(0, 0);
}
/**
* Recursive helper method for isSolved()
*
* @param row
* @param col
* @return
*/
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 true;
}
/**
* Returns a string representation of the board
*
* @return String representation of the board
*/
// Not particularly pretty, but it works
public String toString() { 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();
} }
} }

View file

@ -2,6 +2,11 @@ package sudoku;
public class SolverMain { public class SolverMain {
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("Hello world!"); Solver s = new Solver();
System.out.println(s.toString());
s.randomizeBoard();
System.out.println(s.toString());
s.solve();
System.out.println(s.toString());
} }
} }

View file

@ -1,12 +1,86 @@
package sudoku; package sudoku;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import java.util.stream.IntStream;
class SolverTest { class SolverTest {
@Test void boardTest() { @Test
void constructorTest() {
Solver solver = new Solver(); Solver solver = new Solver();
solver.solve(); assertNotNull(solver);
}
@Test
void setBoardTest() {
Solver solver = new Solver();
int[][] board = new int[9][9];
solver.setBoard(board);
assertEquals(board, solver.getBoard());
}
@Test
void randomizeBoardTest() {
int[][] board = new int[9][9];
Solver solver = new Solver();
solver.randomizeBoard();
assertNotEquals(board, solver.getBoard());
}
@Test
void legalTest() {
Solver solver = new Solver();
assertTrue(solver.legal(0, 0, 1));
solver.setPos(0, 0, 1);
IntStream.range(0, 9).forEach(i -> {
assertFalse(solver.legal(0, i, 1));
assertFalse(solver.legal(i, 0, 1));
});
assertTrue(solver.legal(5, 5, 1));
assertTrue(solver.legal(8, 8, 9));
assertTrue(solver.legal(8, 8, 1));
}
@Test
void solverTest() {
Solver solver = new Solver();
assertFalse(solver.isSolved());
assertTrue(solver.solve());
assertTrue(solver.isSolved()); assertTrue(solver.isSolved());
solver.reset();
solver.randomizeBoard();
assertFalse(solver.isSolved());
assertTrue(solver.solve());
}
@Test
void resetTest() {
Solver solver = new Solver();
assertTrue(solver.solve());
solver.randomizeBoard();
solver.reset();
assertFalse(solver.isSolved());
}
@Test
void legalWithInvalidInputsTest() {
Solver solver = new Solver();
assertFalse(solver.legal(-1, 0, 1));
assertFalse(solver.legal(0, -1, 1));
assertFalse(solver.legal(0, 0, -1));
assertFalse(solver.legal(0, 0, 10));
}
@Test
@Disabled
void unsolvableTest() {
Solver solver = new Solver();
solver.setPos(0, 0, 1);
solver.setPos(0, 1, 1);
assertFalse(solver.solve());
} }
} }