1
1
package backtracking ;
2
2
3
+ import java .util .InputMismatchException ;
3
4
import java .util .Random ;
4
5
import java .util .Scanner ;
5
- import java .util .Stack ;
6
+ import java .util .Deque ;
7
+ import java .util .ArrayDeque ;
6
8
7
9
public class Sudoku {
8
10
private static final int N =9 ;// board size
9
- private static final int Nsqrt =3 ;// block size
10
- char [][]board =new char [N ][N ];// Sudoku board
11
+ private static final int Nsqrt =( int ) Math . sqrt ( N ) ;// block size
12
+ int [][]board =new int [N ][N ];// Sudoku board
11
13
/*
12
14
* level:
13
15
* 1 (EASY) - mostly filled board
14
16
* 2 (MEDIUM) - halfly filled board
15
- *3 (HARD) - mostly empty board
17
+ * (HARD) - mostly empty board
16
18
*/
17
- private static final double EMPTY_RATIO_EASY =0.25 ,
19
+ private static final double EMPTY_RATIO_EASY =0.3 ,
18
20
EMPTY_RATIO_MEDIUM =0.5 ,
19
- EMPTY_RATIO_HARD =0.75 ;
21
+ EMPTY_RATIO_HARD =0.7 ;
20
22
21
23
/**
22
24
* construct a partially filled board randomly. Difficulty level is set to default, i.e., 1
@@ -35,21 +37,30 @@ public Sudoku(int level) {
35
37
}
36
38
37
39
/**
38
- * construct a partially filled board with input
40
+ * construct a partially filled board with input (2D int matrix)
41
+ * @param board - specified input
42
+ */
43
+ public Sudoku (int [][]board ) {
44
+ this .board =board ;
45
+ System .out .println ("Original board:" );
46
+ printSudoku ();
47
+ assert (isValid ());
48
+ }
49
+
50
+ /**
51
+ * construct a partially filled board with input (string array)
39
52
* @param board - specified input
40
53
*/
41
54
public Sudoku (String []board ) {
42
55
for (int i =0 ;i <N ;i ++) {
43
56
String []str =board [i ].split ("," );
44
57
for (int j =0 ;j <N ;j ++) {
45
- this .board [i ][j ] =str [j ].charAt (0 );
46
- if (this .board [i ][j ]=='0' ) {
47
- this .board [i ][j ] =0 ;
48
- }
58
+ this .board [i ][j ] =Integer .parseInt (str [j ]);
49
59
}
50
60
}
51
61
System .out .println ("Original board:" );
52
62
printSudoku ();
63
+ assert (isValid ());
53
64
}
54
65
55
66
/**
@@ -60,7 +71,7 @@ public void createRandomSudoku(int level) {
60
71
int totalLocations =N *N ,remove =0 ;
61
72
62
73
//push all N*N empty spots into the stack
63
- Stack <Integer >emptyLocationList =new Stack <>();
74
+ Deque <Integer >emptyLocationList =new ArrayDeque <>();
64
75
for (int i =totalLocations -1 ;i >=0 ;i --) {
65
76
emptyLocationList .push (i );
66
77
}
@@ -132,7 +143,7 @@ private void swap(int[] nums, int i, int j) {
132
143
* solve Sudoku main function
133
144
*/
134
145
public void solveSudoku () {
135
- Stack <Integer >emptyLocationList =getEmptySpots (0 ,0 );
146
+ Deque <Integer >emptyLocationList =getEmptySpots (0 ,0 );
136
147
if (!solve (emptyLocationList ,false ,new Random ())) {
137
148
System .out .println ("No solution!" );
138
149
}
@@ -149,18 +160,22 @@ public void solveSudoku() {
149
160
* @param rm - Random instance
150
161
* @return true if all empty spots are filled
151
162
*/
152
- private boolean solve (Stack <Integer >emptyLocationList ,boolean startWithRandomInitialValue ,Random rm ) {
163
+ private boolean solve (Deque <Integer >emptyLocationList ,boolean startWithRandomInitialValue ,Random rm ) {
153
164
if (emptyLocationList .size ()==0 )return true ;
165
+ /*{
166
+ printSudoku();
167
+ return false;
168
+ }*/
154
169
int firstValue =emptyLocationList .peek ();
155
170
int row =firstValue /N ,col =firstValue %N ;
156
- int startIndex =startWithRandomInitialValue ?rm .nextInt (N )+ 1 :1 ;
157
- for (int k =startIndex ;k <=N +startIndex ;k ++) {
171
+ int startValue =startWithRandomInitialValue ?rm .nextInt (N ) :0 ;
172
+ for (int k =startValue ;k <=N +startValue ;k ++) {
158
173
int trueValue =k %N +1 ;
159
- if (isSafe (board ,row ,col ,( char )( trueValue + '0' ) )) {
160
- board [row ][col ] =( char )( trueValue + '0' ) ;
161
- emptyLocationList .pop ();
174
+ if (isSafe (board ,row ,col ,trueValue )) {
175
+ board [row ][col ] =trueValue ;
176
+ emptyLocationList .pop ();
162
177
if (solve (emptyLocationList ,startWithRandomInitialValue ,rm ))return true ;
163
- board [row ][col ] =' ' ;
178
+ board [row ][col ] =0 ;
164
179
emptyLocationList .push (firstValue );
165
180
}
166
181
}
@@ -175,7 +190,7 @@ private boolean solve(Stack<Integer> emptyLocationList, boolean startWithRandomI
175
190
* @param ch - number to be placed at location (i, j)
176
191
* @return true if placing 'ch' at location (i, j) is allowed, otherwise return false
177
192
*/
178
- private boolean isSafe (char [][]board ,int i ,int j ,char ch ) {
193
+ private boolean isSafe (int [][]board ,int i ,int j ,int ch ) {
179
194
for (int k =0 ;k <N ;k ++) {
180
195
if (board [k ][j ]==ch )return false ;
181
196
if (board [i ][k ]==ch )return false ;
@@ -195,8 +210,8 @@ private boolean isSafe(char[][] board, int i, int j, char ch) {
195
210
* @param startj - start column index
196
211
* @return stack of all empty spots
197
212
*/
198
- private Stack <Integer >getEmptySpots (int starti ,int startj ) {
199
- Stack <Integer >emptyLocationList =new Stack <>();
213
+ private Deque <Integer >getEmptySpots (int starti ,int startj ) {
214
+ Deque <Integer >emptyLocationList =new ArrayDeque <>();
200
215
for (int i =starti ;i <N +starti ;i ++) {
201
216
for (int j =startj ;j <N +startj ;j ++) {
202
217
int row =i %N ,col =j %N ;
@@ -207,33 +222,88 @@ private Stack<Integer> getEmptySpots(int starti, int startj) {
207
222
}
208
223
return emptyLocationList ;
209
224
}
225
+
226
+ /**
227
+ * check if a Sudoku board is valid
228
+ * @return true if the board is valid; false if not
229
+ */
230
+ public boolean isValid () {
231
+ for (int i =0 ;i <N ;i ++) {
232
+ for (int j =0 ;j <N ;j ++) {
233
+ if (!isValidBlock (0 ,j ,N ,j ) || !isValidBlock (i ,0 ,i ,N ))return false ;
234
+ if (i %Nsqrt ==0 &&j %Nsqrt ==0 && !isValidBlock (i ,j ,i +Nsqrt ,j +Nsqrt ))return false ;
235
+ }
236
+ }
237
+ return true ;
238
+ }
239
+ /**
240
+ * check if a given block defined by start and end row and column index is valid
241
+ * @param starti - start row index
242
+ * @param startj - start column index
243
+ * @param endi - end row index
244
+ * @param endj - end column index
245
+ * @return true if the block is valid
246
+ */
247
+ private boolean isValidBlock (int starti ,int startj ,int endi ,int endj ) {
248
+ int []digits =new int [N ];
249
+ for (int i =starti ;i <endi ;i ++) {
250
+ for (int j =startj ;j <endj ;j ++) {
251
+ if (board [i ][j ]>0 &&board [i ][j ]<=N && ++digits [board [i ][j ]-1 ]>1 )return false ;
252
+ }
253
+ }
254
+ return true ;
255
+ }
210
256
211
257
/**
212
258
* print Sudoku board
213
259
*/
214
260
private void printSudoku () {
261
+ System .out .print ("╔" );
262
+ int col =0 ;
263
+ while (++col <N ) {
264
+ System .out .print ("═══╦" );
265
+ }
266
+ System .out .println ("═══╗" );
215
267
for (int i =0 ;i <N ;i ++) {
216
- if (i %3 ==0 ) {
217
- if (i ==0 ||i ==N -1 )
218
- System .out .println ("╔═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╦═══╗" );
219
- else
220
- System .out .println ("╠═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╬═══╣" );
268
+ if (i %Nsqrt ==0 &&i >0 ) {
269
+ System .out .print ("╠" );
270
+ col =0 ;
271
+ while (++col <N ) {
272
+ System .out .print ("═══╬" );
273
+ }
274
+ System .out .println ("═══╣" );
221
275
}
222
- else {
223
- System .out .println ("╠───┼───┼───╬───┼───┼───╬───┼───┼───╣" );
276
+ else if (i >0 ) {
277
+ System .out .print ("╠" );
278
+ col =0 ;
279
+ while (++col <N ) {
280
+ if (col %Nsqrt ==0 )System .out .print ("───╬" );
281
+ else System .out .print ("───┼" );
282
+ }
283
+ System .out .println ("───╣" );
224
284
}
225
285
226
286
for (int j =0 ;j <N ;j ++) {
227
- if (j %3 ==0 )System .out .print ("║ " );
228
- else System .out .print ("│ " );
229
- System .out .print (board [i ][j ] +" " );
287
+ if (j %Nsqrt ==0 )System .out .print ("║" );
288
+ else System .out .print ("│" );
289
+ String show ="" ;
290
+ if (board [i ][j ]==0 )show =" " ;
291
+ else if (board [i ][j ]<10 )show =" " +board [i ][j ] +" " ;
292
+ else show =board [i ][j ] +" " ;
293
+ System .out .print (show );
230
294
}
231
295
System .out .println ("║" );
232
296
}
233
- System .out .println ("╚═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╩═══╝" );
297
+
298
+ System .out .print ("╚" );
299
+ col =0 ;
300
+ while (++col <N ) {
301
+ System .out .print ("═══╩" );
302
+ }
303
+ System .out .println ("═══╝" );
234
304
System .out .println ();
235
305
}
236
-
306
+
237
307
public static void main (String []args ) {
238
308
239
309
/*
@@ -244,15 +314,45 @@ public static void main(String[] args) {
244
314
"0,2,0,1,0,9,0,0,0",
245
315
"0,0,7,0,0,0,2,4,0",
246
316
"0,6,4,0,1,0,5,9,0",
247
- "0,9,8,0,0,0,3 ,0,0",
248
- "0,0,0,8,0,3 ,0,2,0",
317
+ "0,9,8,0,0,0,0 ,0,0",
318
+ "0,0,0,8,0,0 ,0,2,0",
249
319
"0,0,0,0,0,0,0,0,6",
250
320
"0,0,0,2,7,5,9,0,0",
251
321
};
252
322
Sudoku q = new Sudoku(board);
253
323
q.solveSudoku();
254
324
*/
255
325
326
+ /*
327
+ // get the solution of a given board:
328
+ Scanner sc = new Scanner(System.in);
329
+ int[][] board = new int[N][N];
330
+ System.out.println("Type the values line by line and seperated by a space:\n"
331
+ + "Example input:\n"
332
+ + "1 2 3 4 5 6 7 8 9\n"
333
+ + "0 0 0 0 0 0 0 0 0\n"
334
+ + "0 0 0 0 0 0 0 0 0\n"
335
+ + "0 0 0 0 0 0 0 0 0\n"
336
+ + "0 0 0 0 0 0 0 0 0\n"
337
+ + "0 0 0 0 0 0 0 0 0\n"
338
+ + "0 0 0 0 0 0 0 0 0\n"
339
+ + "0 0 0 0 0 0 0 0 0\n"
340
+ + "0 0 0 0 0 0 0 0 0\n");
341
+ for(int i=0; i<N; i++) {
342
+ for(int j=0; j<N; j++) {
343
+ try {
344
+ int next = sc.nextInt();
345
+ assert(next>=0 && next<=N);
346
+ board[i][j] = next;
347
+ }
348
+ catch(InputMismatchException e) { }
349
+ }
350
+ }
351
+ sc.close();
352
+ Sudoku q = new Sudoku(board);
353
+ q.solveSudoku();
354
+ */
355
+
256
356
// generate a random board and get the solution:
257
357
Scanner sc =new Scanner (System .in );
258
358
System .out .print ("1 - easy\n 2 - medium\n 3 - hard\n Select level: " );