diff --git a/src/main/java/com/thealgorithms/backtracking/SudokuSolver.java b/src/main/java/com/thealgorithms/backtracking/SudokuSolver.java new file mode 100644 index 000000000000..b82abf0ba009 --- /dev/null +++ b/src/main/java/com/thealgorithms/backtracking/SudokuSolver.java @@ -0,0 +1,106 @@ +package com.thealgorithms.backtracking; + +/** + * Solves a Sudoku of any level and prints solved Sudoku. + * This class is a utility and should not be instantiated. + * @author Indraneela Doradla (git-Indraneela Doradla) + */ +public final class SudokuSolver { + + /** + * Private constructor to prevent instantiation. + */ + private SudokuSolver() { + // Utility class + } + + /** + * Solves the Sudoku using backtracking. + * @param board the Sudoku grid + * @return boolean indicating if the Sudoku can be solved + */ + public static boolean solveSudoku(int[][] board) { + int r = -1; + int c = -1; + boolean isEmpty = true; + + // Find the first empty position + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == 0) { + r = i; + c = j; + isEmpty = false; + break; + } + } + if (!isEmpty) { + break; + } + } + + // If no empty position is found, the Sudoku is solved + if (isEmpty) { + return true; + } + + // Try filling the empty position with numbers from 1 to 9 + for (int num = 1; num <= 9; num++) { + if (isSafe(board, r, c, num)) { + board[r][c] = num; + if (solveSudoku(board)) { + return true; // Successfully solved + } + board[r][c] = 0; // Backtrack + } + } + + return false; // No solution found, backtrack + } + + /** + * Checks if placing a number at the given position is valid. + * @param board the Sudoku grid + * @param r row index + * @param c column index + * @param val the value to place + * @return boolean indicating if it's safe to place the value + */ + public static boolean isSafe(int[][] board, int r, int c, int val) { + // Check the row and column + for (int i = 0; i < board.length; i++) { + if (board[i][c] == val || board[r][i] == val) { + return false; + } + } + + // Check the 3x3 subgrid + int sqrt = (int) Math.sqrt(board.length); + int boxRowStart = r - r % sqrt; + int boxColStart = c - c % sqrt; + + for (int i = boxRowStart; i < boxRowStart + sqrt; i++) { + for (int j = boxColStart; j < boxColStart + sqrt; j++) { + if (board[i][j] == val) { + return false; + } + } + } + + return true; + } + + /** + * Prints the Sudoku grid. + * @param board the Sudoku grid + */ + public static void printSudoku(int[][] board) { + for (int[] row : board) { + for (int num : row) { + System.out.print(num + " "); + } + System.out.println(); + } + System.out.println(); + } +} diff --git a/src/test/java/com/thealgorithms/others/SudokuTest.java b/src/test/java/com/thealgorithms/others/SudokuTest.java new file mode 100644 index 000000000000..7083bc1eb31b --- /dev/null +++ b/src/test/java/com/thealgorithms/others/SudokuTest.java @@ -0,0 +1,77 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; + +public class SudokuTest { + + @Test + public void testSolvableSudoku() { + int[][] board = new int[][] { + {3, 0, 6, 5, 0, 8, 4, 0, 0}, + {5, 2, 0, 0, 0, 0, 0, 0, 0}, + {0, 8, 7, 0, 0, 0, 0, 3, 1}, + {0, 0, 3, 0, 1, 0, 0, 8, 0}, + {9, 0, 0, 8, 6, 3, 0, 0, 5}, + {0, 5, 0, 0, 9, 0, 6, 0, 0}, + {1, 3, 0, 0, 0, 0, 2, 5, 0}, + {0, 0, 0, 0, 0, 0, 0, 7, 4}, + {0, 0, 5, 2, 0, 6, 3, 0, 0} + }; + + assertTrue(Sudoku.solveSudoku(board, board.length), "The puzzle should be solvable."); + } + + @Test + public void testUnsolvableSudoku() { + int[][] board = new int[][] { + {3, 0, 6, 5, 0, 8, 4, 0, 0}, + {5, 2, 1, 0, 0, 0, 0, 0, 0}, + {9, 8, 7, 0, 0, 0, 0, 3, 1}, + {0, 0, 3, 0, 1, 0, 0, 8, 0}, + {9, 0, 0, 8, 6, 3, 0, 0, 5}, + {0, 5, 0, 0, 9, 0, 6, 0, 0}, + {1, 3, 0, 0, 0, 0, 2, 5, 0}, + {0, 0, 0, 0, 0, 0, 0, 7, 4}, + {0, 0, 5, 2, 0, 6, 3, 1, 0} + }; + + assertFalse(Sudoku.solveSudoku(board, board.length), "The puzzle should be unsolvable."); + } + + @Test + public void testEmptySudoku() { + int[][] board = new int[][] { + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0} + }; + + assertTrue(Sudoku.solveSudoku(board, board.length), "An empty puzzle should be solvable."); + } + + @Test + public void testAlreadySolvedSudoku() { + int[][] board = new int[][] { + {5, 3, 4, 6, 7, 8, 9, 1, 2}, + {6, 7, 2, 1, 9, 5, 3, 4, 8}, + {1, 9, 8, 3, 4, 2, 5, 6, 7}, + {8, 5, 9, 7, 6, 1, 4, 2, 3}, + {4, 2, 6, 8, 5, 3, 7, 9, 1}, + {7, 1, 3, 9, 2, 4, 8, 5, 6}, + {9, 6, 1, 5, 3, 7, 2, 8, 4}, + {2, 8, 7, 4, 1, 9, 6, 3, 5}, + {3, 4, 5, 2, 8, 6, 1, 7, 9} + }; + + assertTrue(Sudoku.solveSudoku(board, board.length), "An already solved puzzle should return true."); + } +}