-
Notifications
You must be signed in to change notification settings - Fork 0
/
Game.java
368 lines (332 loc) · 11.2 KB
/
Game.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
package GamingPortal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Observable;
/**
* This class represents a Sudoku game. It contains the solution, the user
* input, the selected number and methods to check the validation of the user
* input.
*
* @author Eric Beijer
*/
public class Game extends Observable {
private int[][] solution; // Generated solution.
private int[][] game; // Generated game with user input.
private boolean[][] check; // Holder for checking validity of game.
private int selectedNumber; // Selected number by user.
private boolean help; // Help turned on or off.
/**
* Constructor
*/
public Game() {
newGame();
check = new boolean[9][9];
help = true;
}
/**
* Generates a new Sudoku game.<br />
* All observers will be notified, update action: new game.
*/
public void newGame() {
solution = generateSolution(new int[9][9], 0);
game = generateGame(copy(solution));
setChanged();
notifyObservers(UpdateAction.NEW_GAME);
}
/**
* Checks user input agains the solution and puts it into a check matrix.<br />
* All observers will be notified, update action: check.
*/
public void checkGame() {
selectedNumber = 0;
for (int y = 0; y < 9; y++) {
for (int x = 0; x < 9; x++)
check[y][x] = game[y][x] == solution[y][x];
}
setChanged();
notifyObservers(UpdateAction.CHECK);
}
/**
* Sets help turned on or off.<br />
* All observers will be notified, update action: help.
*
* @param help True for help on, false for help off.
*/
public void setHelp(boolean help) {
this.help = help;
setChanged();
notifyObservers(UpdateAction.HELP);
}
/**
* Sets selected number to user input.<br />
* All observers will be notified, update action: selected number.
*
* @param selectedNumber Number selected by user.
*/
public void setSelectedNumber(int selectedNumber) {
this.selectedNumber = selectedNumber;
setChanged();
notifyObservers(UpdateAction.SELECTED_NUMBER);
}
/**
* Returns number selected user.
*
* @return Number selected by user.
*/
public int getSelectedNumber() {
return selectedNumber;
}
/**
* Returns whether help is turned on or off.
*
* @return True if help is turned on, false if help is turned off.
*/
public boolean isHelp() {
return help;
}
/**
* Returns whether selected number is candidate at given position.
*
* @param x X position in game.
* @param y Y position in game.
* @return True if selected number on given position is candidate,
* false otherwise.
*/
public boolean isSelectedNumberCandidate(int x, int y) {
return game[y][x] == 0 && isPossibleX(game, y, selectedNumber)
&& isPossibleY(game, x, selectedNumber) && isPossibleBlock(game, x, y, selectedNumber);
}
/**
* Sets given number on given position in the game.
*
* @param x The x position in the game.
* @param y The y position in the game.
* @param number The number to be set.
*/
public void setNumber(int x, int y, int number) {
game[y][x] = number;
}
/**
* Returns number of given position.
*
* @param x X position in game.
* @param y Y position in game.
* @return Number of given position.
*/
public int getNumber(int x, int y) {
return game[y][x];
}
/**
* Returns whether user input is valid of given position.
*
* @param x X position in game.
* @param y Y position in game.
* @return True if user input of given position is valid, false
* otherwise.
*/
public boolean isCheckValid(int x, int y) {
return check[y][x];
}
/**
* Returns whether given number is candidate on x axis for given game.
*
* @param game Game to check.
* @param y Position of x axis to check.
* @param number Number to check.
* @return True if number is candidate on x axis, false otherwise.
*/
private boolean isPossibleX(int[][] game, int y, int number) {
for (int x = 0; x < 9; x++) {
if (game[y][x] == number)
return false;
}
return true;
}
/**
* Returns whether given number is candidate on y axis for given game.
*
* @param game Game to check.
* @param x Position of y axis to check.
* @param number Number to check.
* @return True if number is candidate on y axis, false otherwise.
*/
private boolean isPossibleY(int[][] game, int x, int number) {
for (int y = 0; y < 9; y++) {
if (game[y][x] == number)
return false;
}
return true;
}
/**
* Returns whether given number is candidate in block for given game.
*
* @param game Game to check.
* @param x Position of number on x axis in game to check.
* @param y Position of number on y axis in game to check.
* @param number Number to check.
* @return True if number is candidate in block, false otherwise.
*/
private boolean isPossibleBlock(int[][] game, int x, int y, int number) {
int x1 = x < 3 ? 0 : x < 6 ? 3 : 6;
int y1 = y < 3 ? 0 : y < 6 ? 3 : 6;
for (int yy = y1; yy < y1 + 3; yy++) {
for (int xx = x1; xx < x1 + 3; xx++) {
if (game[yy][xx] == number)
return false;
}
}
return true;
}
/**
* Returns next posible number from list for given position or -1 when list
* is empty.
*
* @param game Game to check.
* @param x X position in game.
* @param y Y position in game.
* @param numbers List of remaining numbers.
* @return Next possible number for position in game or -1 when
* list is empty.
*/
private int getNextPossibleNumber(int[][] game, int x, int y, List<Integer> numbers) {
while (numbers.size() > 0) {
int number = numbers.remove(0);
if (isPossibleX(game, y, number) && isPossibleY(game, x, number) && isPossibleBlock(game, x, y, number))
return number;
}
return -1;
}
/**
* Generates Sudoku game solution.
*
* @param game Game to fill, user should pass 'new int[9][9]'.
* @param index Current index, user should pass 0.
* @return Sudoku game solution.
*/
private int[][] generateSolution(int[][] game, int index) {
if (index > 80)
return game;
int x = index % 9;
int y = index / 9;
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 1; i <= 9; i++) numbers.add(i);
Collections.shuffle(numbers);
while (numbers.size() > 0) {
int number = getNextPossibleNumber(game, x, y, numbers);
if (number == -1)
return null;
game[y][x] = number;
int[][] tmpGame = generateSolution(game, index + 1);
if (tmpGame != null)
return tmpGame;
game[y][x] = 0;
}
return null;
}
/**
* Generates Sudoku game from solution.
*
* @param game Game to be generated, user should pass a solution.
* @return Generated Sudoku game.
*/
private int[][] generateGame(int[][] game) {
List<Integer> positions = new ArrayList<Integer>();
for (int i = 0; i < 81; i++)
positions.add(i);
Collections.shuffle(positions);
return generateGame(game, positions);
}
/**
* Generates Sudoku game from solution, user should use the other
* generateGame method. This method simple removes a number at a position.
* If the game isn't anymore valid after this action, the game will be
* brought back to previous state.
*
* @param game Game to be generated.
* @param positions List of remaining positions to clear.
* @return Generated Sudoku game.
*/
private int[][] generateGame(int[][] game, List<Integer> positions) {
while (positions.size() > 0) {
int position = positions.remove(0);
int x = position % 9;
int y = position / 9;
int temp = game[y][x];
game[y][x] = 0;
if (!isValid(game))
game[y][x] = temp;
}
return game;
}
/**
* Checks whether given game is valid.
*
* @param game Game to check.
* @return True if game is valid, false otherwise.
*/
private boolean isValid(int[][] game) {
return isValid(game, 0, new int[] { 0 });
}
/**
* Checks whether given game is valid, user should use the other isValid
* method. There may only be one solution.
*
* @param game Game to check.
* @param index Current index to check.
* @param numberOfSolutions Number of found solutions. Int[] instead of
* int because of pass by reference.
* @return True if game is valid, false otherwise.
*/
private boolean isValid(int[][] game, int index, int[] numberOfSolutions) {
if (index > 80)
return ++numberOfSolutions[0] == 1;
int x = index % 9;
int y = index / 9;
if (game[y][x] == 0) {
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 1; i <= 9; i++)
numbers.add(i);
while (numbers.size() > 0) {
int number = getNextPossibleNumber(game, x, y, numbers);
if (number == -1)
break;
game[y][x] = number;
if (!isValid(game, index + 1, numberOfSolutions)) {
game[y][x] = 0;
return false;
}
game[y][x] = 0;
}
} else if (!isValid(game, index + 1, numberOfSolutions))
return false;
return true;
}
/**
* Copies a game.
*
* @param game Game to be copied.
* @return Copy of given game.
*/
private int[][] copy(int[][] game) {
int[][] copy = new int[9][9];
for (int y = 0; y < 9; y++) {
for (int x = 0; x < 9; x++)
copy[y][x] = game[y][x];
}
return copy;
}
/*
* Prints given game to console. Used for debug.
*
* @param game Game to be printed.
*
private void print(int[][] game) {
System.out.println();
for (int y = 0; y < 9; y++) {
for (int x = 0; x < 9; x++)
System.out.print(" " + game[y][x]);
System.out.println();
}
}*/
}