Compare commits
No commits in common. "67ae4983d46440c67ec0ac18e2f7a1cf4569b34a" and "999d97f565613e04b72ca246bd83b1d4658c300f" have entirely different histories.
67ae4983d4
...
999d97f565
6 changed files with 7 additions and 276 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,5 +3,3 @@
|
||||||
|
|
||||||
# Ignore Gradle build output directory
|
# Ignore Gradle build output directory
|
||||||
build
|
build
|
||||||
|
|
||||||
.vscode
|
|
||||||
|
|
3
Justfile
3
Justfile
|
@ -6,6 +6,3 @@ test:
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
fd -td -I build -x rm -r
|
fd -td -I build -x rm -r
|
||||||
|
|
||||||
watch:
|
|
||||||
watchexec -c -w app/src "just test && just run"
|
|
|
@ -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(21))
|
languageVersion.set(JavaLanguageVersion.of(11))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
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;
|
||||||
|
|
||||||
|
@ -18,200 +16,17 @@ 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) {
|
public boolean legal(int row, int col, int nbr) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,11 +2,6 @@ package sudoku;
|
||||||
|
|
||||||
public class SolverMain {
|
public class SolverMain {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
Solver s = new Solver();
|
System.out.println("Hello world!");
|
||||||
System.out.println(s.toString());
|
|
||||||
s.randomizeBoard();
|
|
||||||
System.out.println(s.toString());
|
|
||||||
s.solve();
|
|
||||||
System.out.println(s.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,86 +1,12 @@
|
||||||
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
|
@Test void boardTest() {
|
||||||
void constructorTest() {
|
|
||||||
Solver solver = new Solver();
|
Solver solver = new Solver();
|
||||||
assertNotNull(solver);
|
solver.solve();
|
||||||
}
|
|
||||||
|
|
||||||
@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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue