1
-
2
1
package com .thealgorithms .puzzlesandgames ;
3
2
3
+ import java .util .Iterator ;
4
+ import java .util .NoSuchElementException ;
5
+
4
6
/**
5
- * A class that provides methods to solve Sudoku puzzles of any n x n size
6
- * using a backtracking approach, where n must be a perfect square.
7
- * The algorithm checks for safe number placements in rows, columns,
8
- * and subgrids (which are sqrt(n) x sqrt(n) in size) and recursively solves the puzzle.
9
- * Though commonly used for 9x9 grids, it is adaptable to other valid Sudoku dimensions.
7
+ * Represents a Sudoku board with validation and iteration support.
8
+ * The board is always a square grid of size n x n,
9
+ * where n must be a perfect square (e.g., 4, 9, 16).
10
10
*/
11
- final class Sudoku {
11
+ class SudokuBoard implements Iterable <SudokuBoard .Cell > {
12
+
13
+ private final int size ;
14
+ private final int boxSize ;
15
+ private final int [][] board ;
16
+
17
+ /**
18
+ * Constructs a SudokuBoard of the given size.
19
+ *
20
+ * @param size the dimension of the Sudoku board (must be a perfect square)
21
+ * @throws IllegalArgumentException if size is not a positive perfect square
22
+ */
23
+ public SudokuBoard (int size ) {
24
+ if (size <= 0 || Math .sqrt (size ) % 1 != 0 ) {
25
+ throw new IllegalArgumentException ("Size must be a perfect square (e.g., 4, 9, 16)" );
26
+ }
27
+ this .size = size ;
28
+ this .boxSize = (int ) Math .sqrt (size );
29
+ this .board = new int [size ][size ];
30
+ }
12
31
13
- private Sudoku () {
32
+ /**
33
+ * Returns the size of the board.
34
+ *
35
+ * @return the board size
36
+ */
37
+ public int getSize () {
38
+ return size ;
14
39
}
15
40
16
41
/**
17
- * Checks if placing a number in a specific position on the Sudoku board is safe.
18
- * The number is considered safe if it does not violate any of the Sudoku rules:
19
- * - It should not be present in the same row.
20
- * - It should not be present in the same column.
21
- * - It should not be present in the corresponding 3x3 subgrid.
22
- * - It should not be present in the corresponding subgrid, which is sqrt(n) x sqrt(n) in size (e.g., for a 9x9 grid, the subgrid will be 3x3).
42
+ * Returns the box (subgrid) size.
23
43
*
24
- * @param board The current state of the Sudoku board.
25
- * @param row The row index where the number is to be placed.
26
- * @param col The column index where the number is to be placed.
27
- * @param num The number to be placed on the board.
28
- * @return True if the placement is safe, otherwise false.
44
+ * @return the size of a subgrid
29
45
*/
30
- public static boolean isSafe (int [][] board , int row , int col , int num ) {
31
- // Check the row for duplicates
32
- for (int d = 0 ; d < board .length ; d ++) {
33
- if (board [row ][d ] == num ) {
46
+ public int getBoxSize () {
47
+ return boxSize ;
48
+ }
49
+
50
+ /**
51
+ * Gets the value at the given cell.
52
+ *
53
+ * @param row the row index
54
+ * @param col the column index
55
+ * @return the value at the specified cell
56
+ * @throws IndexOutOfBoundsException if indices are invalid
57
+ */
58
+ public int get (int row , int col ) {
59
+ validateCell (row , col );
60
+ return board [row ][col ];
61
+ }
62
+
63
+ /**
64
+ * Sets the value at the given cell.
65
+ *
66
+ * @param row the row index
67
+ * @param col the column index
68
+ * @param value the value to set (0 means empty)
69
+ * @throws IndexOutOfBoundsException if indices are invalid
70
+ * @throws IllegalArgumentException if value is out of range
71
+ */
72
+ public void set (int row , int col , int value ) {
73
+ validateCell (row , col );
74
+ if (value < 0 || value > size ) {
75
+ throw new IllegalArgumentException ("Value must be between 0 and " + size );
76
+ }
77
+ board [row ][col ] = value ;
78
+ }
79
+
80
+ /**
81
+ * Checks whether placing a value at the given cell is valid
82
+ * according to Sudoku rules.
83
+ *
84
+ * @param row the row index
85
+ * @param col the column index
86
+ * @param value the value to check
87
+ * @return true if placement is valid, false otherwise
88
+ */
89
+ public boolean isValid (int row , int col , int value ) {
90
+ validateCell (row , col );
91
+ if (value <= 0 || value > size ) {
92
+ return false ;
93
+ }
94
+
95
+ // check row
96
+ for (int c = 0 ; c < size ; c ++) {
97
+ if (board [row ][c ] == value ) {
34
98
return false ;
35
99
}
36
100
}
37
101
38
- // Check the column for duplicates
39
- for (int r = 0 ; r < board . length ; r ++) {
40
- if (board [r ][col ] == num ) {
102
+ // check column
103
+ for (int r = 0 ; r < size ; r ++) {
104
+ if (board [r ][col ] == value ) {
41
105
return false ;
42
106
}
43
107
}
44
108
45
- // Check the corresponding 3x3 subgrid for duplicates
46
- int sqrt = (int ) Math .sqrt (board .length );
47
- int boxRowStart = row - row % sqrt ;
48
- int boxColStart = col - col % sqrt ;
109
+ // check box
110
+ int boxRowStart = (row / boxSize ) * boxSize ;
111
+ int boxColStart = (col / boxSize ) * boxSize ;
49
112
50
- for (int r = boxRowStart ; r < boxRowStart + sqrt ; r ++) {
51
- for (int d = boxColStart ; d < boxColStart + sqrt ; d ++) {
52
- if (board [r ][d ] == num ) {
113
+ for (int r = 0 ; r < boxSize ; r ++) {
114
+ for (int c = 0 ; c < boxSize ; c ++) {
115
+ if (board [boxRowStart + r ][boxColStart + c ] == value ) {
53
116
return false ;
54
117
}
55
118
}
@@ -59,112 +122,102 @@ public static boolean isSafe(int[][] board, int row, int col, int num) {
59
122
}
60
123
61
124
/**
62
- * Solves the Sudoku puzzle using backtracking.
63
- * The algorithm finds an empty cell and tries placing numbers
64
- * from 1 to n, where n is the size of the board
65
- * (for example, from 1 to 9 in a standard 9x9 Sudoku).
66
- * The algorithm finds an empty cell and tries placing numbers from 1 to 9.
67
- * The standard version of Sudoku uses numbers from 1 to 9, so the algorithm can be
68
- * easily modified for other variations of the game.
69
- * If a number placement is valid (checked via `isSafe`), the number is
70
- * placed and the function recursively attempts to solve the rest of the puzzle.
71
- * If no solution is possible, the number is removed (backtracked),
72
- * and the process is repeated.
125
+ * Ensures that the given cell indices are valid.
73
126
*
74
- * @param board The current state of the Sudoku board.
75
- * @param n The size of the Sudoku board (typically 9 for a standard puzzle).
76
- * @return True if the Sudoku puzzle is solvable, false otherwise.
127
+ * @param row the row index
128
+ * @param col the column index
129
+ * @throws IndexOutOfBoundsException if indices are outside the board
77
130
*/
78
- public static boolean solveSudoku (int [][] board , int n ) {
79
- int row = -1 ;
80
- int col = -1 ;
81
- boolean isEmpty = true ;
82
-
83
- // Find the next empty cell
84
- for (int i = 0 ; i < n ; i ++) {
85
- for (int j = 0 ; j < n ; j ++) {
86
- if (board [i ][j ] == 0 ) {
87
- row = i ;
88
- col = j ;
89
- isEmpty = false ;
90
- break ;
91
- }
92
- }
93
- if (!isEmpty ) {
94
- break ;
95
- }
131
+ private void validateCell (int row , int col ) {
132
+ if (row < 0 || row >= size || col < 0 || col >= size ) {
133
+ throw new IndexOutOfBoundsException ("Cell position out of bounds" );
96
134
}
135
+ }
97
136
98
- // No empty space left
99
- if (isEmpty ) {
100
- return true ;
137
+ /**
138
+ * Represents a single cell on the Sudoku board.
139
+ */
140
+ public class Cell {
141
+ private final int row ;
142
+ private final int col ;
143
+
144
+ /**
145
+ * Constructs a Cell with the given row and column.
146
+ *
147
+ * @param row the row index
148
+ * @param col the column index
149
+ */
150
+ public Cell (int row , int col ) {
151
+ this .row = row ;
152
+ this .col = col ;
101
153
}
102
154
103
- // Try placing numbers 1 to n in the empty cell (n should be a perfect square)
104
- // Eg: n=9 for a standard 9x9 Sudoku puzzle, n=16 for a 16x16 puzzle, etc.
105
- for (int num = 1 ; num <= n ; num ++) {
106
- if (isSafe (board , row , col , num )) {
107
- board [row ][col ] = num ;
108
- if (solveSudoku (board , n )) {
109
- return true ;
110
- } else {
111
- // replace it
112
- board [row ][col ] = 0 ;
113
- }
114
- }
155
+ /**
156
+ * Returns the row index of this cell.
157
+ *
158
+ * @return the row index
159
+ */
160
+ public int getRow () {
161
+ return row ;
162
+ }
163
+
164
+ /**
165
+ * Returns the column index of this cell.
166
+ *
167
+ * @return the column index
168
+ */
169
+ public int getCol () {
170
+ return col ;
171
+ }
172
+
173
+ /**
174
+ * Gets the current value stored in this cell.
175
+ *
176
+ * @return the cell value
177
+ */
178
+ public int getValue () {
179
+ return board [row ][col ];
180
+ }
181
+
182
+ /**
183
+ * Sets a value in this cell.
184
+ *
185
+ * @param value the value to set
186
+ */
187
+ public void setValue (int value ) {
188
+ SudokuBoard .this .set (row , col , value );
115
189
}
116
- return false ;
117
190
}
118
191
119
192
/**
120
- * Prints the current state of the Sudoku board in a readable format.
121
- * Each row is printed on a new line, with numbers separated by spaces.
122
- *
123
- * @param board The current state of the Sudoku board.
124
- * @param n The size of the Sudoku board (typically 9 for a standard puzzle).
193
+ * Iterator for traversing all cells in the board.
125
194
*/
126
- public static void print (int [][] board , int n ) {
127
- // Print the board in a nxn grid format
128
- // if n=9, print the board in a 9x9 grid format
129
- // if n=16, print the board in a 16x16 grid format
130
- for (int r = 0 ; r < n ; r ++) {
131
- for (int d = 0 ; d < n ; d ++) {
132
- System .out .print (board [r ][d ]);
133
- System .out .print (" " );
134
- }
135
- System .out .print ("\n " );
195
+ private class CellIterator implements Iterator <Cell > {
196
+ private int row = 0 ;
197
+ private int col = 0 ;
136
198
137
- if ((r + 1 ) % (int ) Math .sqrt (n ) == 0 ) {
138
- System .out .print ("" );
199
+ @ Override
200
+ public boolean hasNext () {
201
+ return row < size ;
202
+ }
203
+
204
+ @ Override
205
+ public Cell next () {
206
+ if (!hasNext ()) {
207
+ throw new NoSuchElementException ();
208
+ }
209
+ Cell cell = new Cell (row , col );
210
+ col ++;
211
+ if (col == size ) {
212
+ col = 0 ;
213
+ row ++;
139
214
}
215
+ return cell ;
140
216
}
141
217
}
142
218
143
- /**
144
- * The driver method to demonstrate solving a Sudoku puzzle.
145
- * A sample 9x9 Sudoku puzzle is provided, and the program attempts to solve it
146
- * using the `solveSudoku` method. If a solution is found, it is printed to the console.
147
- *
148
- * @param args Command-line arguments (not used in this program).
149
- */
150
- public static void main (String [] args ) {
151
- int [][] board = new int [][] {
152
- {3 , 0 , 6 , 5 , 0 , 8 , 4 , 0 , 0 },
153
- {5 , 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
154
- {0 , 8 , 7 , 0 , 0 , 0 , 0 , 3 , 1 },
155
- {0 , 0 , 3 , 0 , 1 , 0 , 0 , 8 , 0 },
156
- {9 , 0 , 0 , 8 , 6 , 3 , 0 , 0 , 5 },
157
- {0 , 5 , 0 , 0 , 9 , 0 , 6 , 0 , 0 },
158
- {1 , 3 , 0 , 0 , 0 , 0 , 2 , 5 , 0 },
159
- {0 , 0 , 0 , 0 , 0 , 0 , 0 , 7 , 4 },
160
- {0 , 0 , 5 , 2 , 0 , 6 , 3 , 0 , 0 },
161
- };
162
- int n = board .length ;
163
-
164
- if (solveSudoku (board , n )) {
165
- print (board , n );
166
- } else {
167
- System .out .println ("No solution" );
168
- }
219
+ @ Override
220
+ public Iterator <Cell > iterator () {
221
+ return new CellIterator ();
169
222
}
170
223
}
0 commit comments