Recursion limit for bailing on unsolvable, more extensive testing and additions to interface
This commit is contained in:
		
							parent
							
								
									6366a5e0f2
								
							
						
					
					
						commit
						196f066281
					
				
					 3 changed files with 93 additions and 25 deletions
				
			
		|  | @ -2,6 +2,7 @@ package sudoku; | |||
| 
 | ||||
| public class Solver implements SudokuSolver { | ||||
|     private int[][] board = null; | ||||
|     private int tries = 0; | ||||
| 
 | ||||
|     public Solver() { | ||||
|         board = new int[9][9]; | ||||
|  | @ -47,6 +48,12 @@ 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; | ||||
|         } | ||||
|  | @ -61,6 +68,7 @@ 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); | ||||
|         } | ||||
|  | @ -76,17 +84,28 @@ public class Solver implements SudokuSolver { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Reset the current cell to zero and backtrack | ||||
|         board[row][col] = 0; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Randomizes the board. This guarantees a solvable board. | ||||
|      * {@inheritDoc} | ||||
|      * <p> | ||||
|      * Default difficulty is 3 | ||||
|      */ | ||||
|     public void randomizeBoard() { | ||||
|         randomizeBoard(3); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * {@inheritDoc} | ||||
|      */ | ||||
|     @Override | ||||
|     public void randomizeBoard() { | ||||
|     public void randomizeBoard(int difficulty) { | ||||
|         int amount_prefilled = (difficulty * 9) + 1; | ||||
|         this.clear(); | ||||
|         for (int i = 0; i < 9; ++i) { | ||||
|         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; | ||||
|  | @ -94,6 +113,12 @@ 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); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -121,32 +146,25 @@ public class Solver implements SudokuSolver { | |||
|      * {@inheritDoc} | ||||
|      */ | ||||
|     @Override | ||||
|     public boolean isLegal(int row, int col, int val) { | ||||
|         if (row < 0 || row >= 9 || col < 0 || col >= 9 || val < 1 || val > 9) { | ||||
|     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 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 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 (val == board[boxRowOffset + k][boxColOffset + m]) { | ||||
|                 if (num == board[boxRowOffset + k][boxColOffset + m]) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|  | @ -156,6 +174,22 @@ 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 | ||||
|      *  | ||||
|  | @ -213,7 +247,7 @@ public class Solver implements SudokuSolver { | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|         return isSolved(row + 1, col); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  |  | |||
|  | @ -52,7 +52,14 @@ public interface SudokuSolver { | |||
|     /** | ||||
|      * Randomize the board. Guaranteed to be solvable. | ||||
|      */ | ||||
|     void randomizeBoard(); | ||||
|     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 | ||||
|  | @ -61,6 +68,13 @@ public interface SudokuSolver { | |||
|      */ | ||||
|     boolean isSolved(); | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if the board is solvable | ||||
|      *  | ||||
|      * @return true if solvable | ||||
|      */ | ||||
|     boolean isSolvable(); | ||||
| 
 | ||||
|     /** | ||||
|      * Clear the board | ||||
|      */ | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| package sudoku; | ||||
| 
 | ||||
| import org.junit.jupiter.api.Disabled; | ||||
| import org.junit.jupiter.api.RepeatedTest; | ||||
| import org.junit.jupiter.api.Test; | ||||
| import static org.junit.jupiter.api.Assertions.*; | ||||
| import java.util.stream.IntStream; | ||||
|  | @ -45,6 +45,7 @@ class SolverTest { | |||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     @RepeatedTest(100) | ||||
|     void solverTest() { | ||||
|         Solver solver = new Solver(); | ||||
|         assertFalse(solver.isSolved()); | ||||
|  | @ -52,12 +53,16 @@ class SolverTest { | |||
|         assertTrue(solver.isSolved()); | ||||
| 
 | ||||
|         solver.clear(); | ||||
|         solver.randomizeBoard(); | ||||
|         assertFalse(solver.isSolved()); | ||||
|         assertTrue(solver.solve()); | ||||
|         solver.clear(); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     @RepeatedTest(100) | ||||
|     void randomizeBoardGuaranteeSolvableTest() { | ||||
|         Solver solver = new Solver(); | ||||
|         solver.randomizeBoard(); | ||||
|         assertFalse(solver.isSolved()); | ||||
|         assertTrue(solver.solve()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|  | @ -79,11 +84,26 @@ 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()); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Imbus
						Imbus