Skip to content

Add new algorithm Sudoku Solver in Backtracking #5524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* [Permutation](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/Permutation.java)
* [PowerSum](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/PowerSum.java)
* [SubsequenceFinder](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/SubsequenceFinder.java)
* [SudokuSolver](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/SudokuSolver.java)
* [WordSearch](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/backtracking/WordSearch.java)
* bitmanipulation
* [BitSwap](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/bitmanipulation/BitSwap.java)
Expand Down Expand Up @@ -593,6 +594,7 @@
* [PermutationTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/PermutationTest.java)
* [PowerSumTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/PowerSumTest.java)
* [SubsequenceFinderTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/SubsequenceFinderTest.java)
* [SudokuSolverTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/SudokuSolverTest.java)
* [WordSearchTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/backtracking/WordSearchTest.java)
* bitmanipulation
* [BitSwapTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/bitmanipulation/BitSwapTest.java)
Expand Down
175 changes: 175 additions & 0 deletions src/main/java/com/thealgorithms/backtracking/SudokuSolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package com.thealgorithms.backtracking;

/**
* Java program for Sudoku Solver using Backtracking Algorithm.
* Solves a given Sudoku puzzle by filling the empty cells (denoted by '0') with
* valid digits (1-9).
*/
public final class SudokuSolver {
private SudokuSolver() {
}

/**
* Check if placing a number in a specific cell is valid.
* Validity is checked based on the row, column, and 3x3 subgrid.
*
* @param board The 9x9 Sudoku board
* @param row The row index
* @param col The column index
* @param num The number to be placed in the cell
* @return true if placing the number is valid, false otherwise
*/
public static boolean isValidMove(final int[][] board, final int row, final int col, final int num) {
// Check the row
for (int x = 0; x < 9; x++) {
if (board[row][x] == num) {
return false;
}
}

// Check the column
for (int x = 0; x < 9; x++) {
if (board[x][col] == num) {
return false;
}
}

// Check the 3x3 sub-grid
int startRow = row / 3 * 3;
int startCol = col / 3 * 3;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[startRow + i][startCol + j] == num) {
return false;
}
}
}

return true;
}

/**
* Solve the Sudoku puzzle using backtracking.
*
* @param board The 9x9 Sudoku board
* @return true if the puzzle is solved, false if no solution exists
*/
public static boolean solveSudokuHelper(final int[][] board) {
// Traverse the board
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
// Empty cell found
if (board[row][col] == 0) {
// Try placing numbers 1-9 in the empty cell
for (int num = 1; num <= 9; num++) {
if (isValidMove(board, row, col, num)) {
board[row][col] = num;

// Recursively try to solve the board with the new number
if (solveSudoku(board)) {
return true; // Solution found
}
board[row][col] = 0; // Backtrack
}
}
return false; // No valid number can be placed in this cell
}
}
}

return true; // Sudoku is solved if no empty cells are left
}

/**
* Check if the given Sudoku board is valid.
* A valid board has no duplicate numbers in rows, columns, or 3x3 subgrids.
*
* @param board The 9x9 Sudoku board
* @return true if the board is valid, false otherwise
*/
public static boolean isValidSudoku(final int[][] board) {
// Check each row
for (int i = 0; i < 9; i++) {
boolean[] row = new boolean[10];
for (int j = 0; j < 9; j++) {
if (board[i][j] != 0) {
if (row[board[i][j]]) {
return false;
}
row[board[i][j]] = true;
}
}
}

// Check each column
for (int i = 0; i < 9; i++) {
boolean[] col = new boolean[10];
for (int j = 0; j < 9; j++) {
if (board[j][i] != 0) {
if (col[board[j][i]]) {
return false;
}
col[board[j][i]] = true;
}
}
}

// Check each 3x3 subgrid
for (int i = 0; i < 9; i += 3) {
for (int j = 0; j < 9; j += 3) {
boolean[] grid = new boolean[10];
for (int k = 0; k < 3; k++) {
for (int l = 0; l < 3; l++) {
if (board[i + k][j + l] != 0) {
if (grid[board[i + k][j + l]]) {
return false;
}
grid[board[i + k][j + l]] = true;
}
}
}
}
}

return true;
}

public static boolean solveSudoku(final int[][] board) {
// Check if the board is valid
if (!isValidSudoku(board)) {
return false;
}

return solveSudokuHelper(board);
}

/**
* Print the current state of the Sudoku board.
*
* @param board The 9x9 Sudoku board
*/
public static void printBoard(final int[][] board) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}

public static void main(String[] args) {
// Example 9x9 Sudoku board with some cells pre-filled and some cells empty (0)
int[][] board = {{5, 3, 0, 0, 7, 0, 0, 0, 0}, {6, 0, 0, 1, 9, 5, 0, 0, 0}, {0, 9, 8, 0, 0, 0, 0, 6, 0}, {8, 0, 0, 0, 6, 0, 0, 0, 3}, {4, 0, 0, 8, 0, 3, 0, 0, 1}, {7, 0, 0, 0, 2, 0, 0, 0, 6}, {0, 6, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 4, 1, 9, 0, 0, 5}, {0, 0, 0, 0, 8, 0, 0, 7, 9}};

System.out.println("Original Sudoku Board:");
printBoard(board);

// Solve the Sudoku puzzle
if (solveSudoku(board)) {
System.out.println("\nSolved Sudoku Board:");
printBoard(board);
} else {
System.out.println("\nNo solution exists for the given Sudoku puzzle.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.thealgorithms.backtracking;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

/**
* Test class for SudokuSolver using JUnit 5.
* This class verifies the correctness of the Sudoku solver algorithm.
*/
class SudokuSolverTest {

/**
* Test case for solving a solvable Sudoku puzzle.
*/
@Test
void testForValidSudoku() {
int[][] board = {{5, 3, 0, 0, 7, 0, 0, 0, 0}, {6, 0, 0, 1, 9, 5, 0, 0, 0}, {0, 9, 8, 0, 0, 0, 0, 6, 0}, {8, 0, 0, 0, 6, 0, 0, 0, 3}, {4, 0, 0, 8, 0, 3, 0, 0, 1}, {7, 0, 0, 0, 2, 0, 0, 0, 6}, {0, 6, 0, 0, 0, 0, 2, 8, 0}, {0, 0, 0, 4, 1, 9, 0, 0, 5}, {0, 0, 0, 0, 8, 0, 0, 7, 9}};

// Solve the Sudoku puzzle
boolean solved = SudokuSolver.solveSudoku(board);

// Check that the puzzle is solvable
assertTrue(solved, "The Sudoku puzzle should be solvable.");

// Expected output for a solved puzzle
int[][] expected = {{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}};

// Verify the solved board matches the expected result
assertArrayEquals(expected, board);
}

/**
* Test case for an already solved Sudoku puzzle.
*/
@Test
void testForAlreadySolvedSudoku() {
int[][] board = {{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}};

// Check that the board is already solved
boolean solved = SudokuSolver.solveSudoku(board);

// Check that no changes are made to the already solved board
assertTrue(solved, "The Sudoku board should be already solved.");
}

/**
* Test case for solving an empty Sudoku puzzle (no empty cells).
*/
@Test
void testForEmptySudoku() {
int[][] board = {{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}};

// Solve the "already filled" puzzle
boolean solved = SudokuSolver.solveSudoku(board);

// Check that the board is already solved
assertTrue(solved, "The Sudoku board should be solved (no empty cells).");
}
}