2 * Sudokuki - essential sudoku game
3 * Copyright (C) 2007-2012 Sylvain Vedrenne
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.jankenpoi.sudokuki.model;
20 import java.util.ArrayList;
21 import java.util.List;
23 import net.jankenpoi.sudokuki.SudokuGrid;
24 import net.jankenpoi.sudokuki.generator.SudokuGeneratorFactory;
25 import net.jankenpoi.sudokuki.preferences.UserPreferences;
26 import net.jankenpoi.sudokuki.view.GridListener;
29 * (MVC) model for a sudoku grid
31 * @author Sylvain Vedrenne
34 public class GridModel implements Cloneable {
36 public static final int MASK_CELL_VALUES = 0x000F; // ___0000.0000-0000.1111
37 public static final int MASK_CELL_MEMOS = 0x1FF0; // ____0001.1111-1111.0000
39 public static final int FLAG_CELL_MEMO_1 = 0x0010; // ___0000.0000-0001.0000
40 public static final int FLAG_CELL_MEMO_2 = 0x0020; // ___0000.0000-0010.0000
41 public static final int FLAG_CELL_MEMO_3 = 0x0040; // ___0000.0000-0100.0000
42 public static final int FLAG_CELL_MEMO_4 = 0x0080; // ___0000.0000-1000.0000
43 public static final int FLAG_CELL_MEMO_5 = 0x0100; // ___0000.0001-0000.0000
44 public static final int FLAG_CELL_MEMO_6 = 0x0200; // ___0000.0010-0000.0000
45 public static final int FLAG_CELL_MEMO_7 = 0x0400; // ___0000.0100-0000.0000
46 public static final int FLAG_CELL_MEMO_8 = 0x0800; // ___0000.1000-0000.0000
47 public static final int FLAG_CELL_MEMO_9 = 0x1000; // ___0001.0000-0000.0000
48 public static final int FLAG_CELL_READ_ONLY = 0x2000; // 0010.0000-0000.0000
50 public static final int FLAG_GRID_COMPLETE = 0x4000; // _0100.0000-0000.0000
52 private List<GridListener> listeners = new ArrayList<GridListener>();
54 private boolean isCustomGridModeON = false;
58 * Values and flags for all cells
60 private short[] cellInfos = new short[81];
62 public GridModel(short[] flagsTable, int startIdx) {
63 copyFlagsToGrid(flagsTable, startIdx);
66 private void copyFlagsToGrid(short [] flagsTable, int startIdx) {
67 for (int i = 0; i < cellInfos.length; i++) {
68 short value = (short) (flagsTable[startIdx + i] & MASK_CELL_VALUES);
70 if (1 <= value && value <= 9) {
71 cellInfos[i] |= FLAG_CELL_READ_ONLY;
80 public void resetGridModelFromShorts(short[] externalCellInfos) {
81 cellInfos = new short[81];
82 for (int i=0; i<81; i++) {
83 cellInfos[i] = externalCellInfos[i];
87 private void newGrid() {
88 final int minRating = UserPreferences.getInstance().getInteger("minRating", Integer.valueOf(0)).intValue();
89 final int maxRating = UserPreferences.getInstance().getInteger("maxRating", Integer.valueOf(Integer.MAX_VALUE)).intValue();
90 SudokuGrid grid = SudokuGeneratorFactory.getGenerator().generateGrid(minRating, maxRating);
92 for (int i = 0; i < cellInfos.length; i++) {
93 short value = (short) grid.getValueAt(i);
95 if (1 <= value && value <= 9) {
96 cellInfos[i] |= FLAG_CELL_READ_ONLY;
102 * FIXME: For the moment, this constructor is for testing use only....
106 public GridModel(String strValues) {
107 if (strValues == null) {
111 .println("GridModel.GridModel() length:" + strValues.length());
112 System.out.println("GridModel.GridModel() strValues:" + strValues);
113 for (int i = 0; i < strValues.length(); i++) {
114 short value = Short.valueOf(strValues.substring(i, i + 1)).shortValue();
115 System.out.print(value);
116 cellInfos[i] = value;
117 if (1 <= value && value <= 9) {
118 cellInfos[i] |= FLAG_CELL_READ_ONLY;
121 System.out.println();
124 public void addGridListener(GridListener view) {
128 short getCellInfosAt(int li, int co) {
129 return cellInfos[9 * li + co];
132 public void setMemosForAllCells() {
133 for (int li = 0; li < 9; li++) {
134 for (int co = 0; co < 9; co++) {
135 if (!isCellFilled(li, co)) {
136 cellInfos[9 * li + co] = MASK_CELL_MEMOS;
141 // parcourir tous les carres
142 // - pour chaque carre, cribler les memos
143 // -- pour chaque cellule, cribler les memos
145 for (int X = 0; X < 9; X += 3) { // left pos of a square
146 for (int Y = 0; Y < 9; Y += 3) { // top pos of a square
147 short currentValuesMask = 0;
148 for (int x = 0; x < 3; x++) {
149 for (int y = 0; y < 3; y++) {
152 if (isCellFilled(li, co)) {
153 byte value = getValueAt(li, co);
154 currentValuesMask |= getMemoFlag(value);
158 for (int x = 0; x < 3; x++) {
159 for (int y = 0; y < 3; y++) {
162 if (!isCellFilled(li, co)) {
163 cellInfos[9 * li + co] &= ~currentValuesMask;
170 // parcourir toutes les lignes
171 // - pour chaque ligne, cribler les memos
172 // -- pour chaque cellule, cribler les memos
174 for (int li = 0; li < 9; li++) { // each line
175 short currentValuesMask = 0;
176 for (int co = 0; co < 9; co++) {
177 if (isCellFilled(li, co)) {
178 byte value = getValueAt(li, co);
179 currentValuesMask |= getMemoFlag(value);
182 for (int co = 0; co < 9; co++) {
183 if (!isCellFilled(li, co)) {
184 cellInfos[9 * li + co] &= ~currentValuesMask;
189 // parcourir toutes les colonnes
190 // - pour chaque colonne, cribler les memos
191 // -- pour chaque cellule, cribler les memos
192 for (int co = 0; co < 9; co++) { // each column
193 short currentValuesMask = 0;
194 for (int li = 0; li < 9; li++) {
195 if (isCellFilled(li, co)) {
196 byte value = getValueAt(li, co);
197 currentValuesMask |= getMemoFlag(value);
200 for (int li = 0; li < 9; li++) {
201 if (!isCellFilled(li, co)) {
202 cellInfos[9 * li + co] &= ~currentValuesMask;
206 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
209 public void setMemosForThisCell(int cellLi, int cellCo) {
210 if (isCellFilled(cellLi, cellCo))
212 cellInfos[9 * cellLi + cellCo] = MASK_CELL_MEMOS;
215 // parcourir le carre courant
216 // - pour chaque carre, cribler les memos
217 // -- pour chaque cellule, cribler les memos
219 { // left pos of a square
221 int X = 3 * (cellCo / 3);
222 int Y = 3 * (cellLi / 3);
223 short currentValuesMask = 0;
224 for (int x = 0; x < 3; x++) {
225 for (int y = 0; y < 3; y++) {
228 if (isCellFilled(li, co)) {
229 byte value = getValueAt(li, co);
230 currentValuesMask |= getMemoFlag(value);
234 cellInfos[9 * cellLi + cellCo] &= ~currentValuesMask;
237 // parcourir la ligne de cette cellule
238 // - pour chaque ligne, cribler les memos
239 // -- pour chaque cellule, cribler les memos
242 short currentValuesMask = 0;
243 for (int co = 0; co < 9; co++) {
244 if (isCellFilled(cellLi, co)) {
245 byte value = getValueAt(cellLi, co);
246 currentValuesMask |= getMemoFlag(value);
249 for (int co = 0; co < 9; co++) {
250 if (!isCellFilled(cellLi, co)) {
251 cellInfos[9 * cellLi + co] &= ~currentValuesMask;
256 // parcourir la colonne de cette cellule
257 // - pour chaque colonne, cribler les memos
258 // -- pour chaque cellule, cribler les memos
260 short currentValuesMask = 0;
261 for (int li = 0; li < 9; li++) {
262 if (isCellFilled(li, cellCo)) {
263 byte value = getValueAt(li, cellCo);
264 currentValuesMask |= getMemoFlag(value);
267 for (int li = 0; li < 9; li++) {
268 if (!isCellFilled(li, cellCo)) {
269 cellInfos[9 * li + cellCo] &= ~currentValuesMask;
273 fireGridChanged(new GridChangedEvent(this, cellLi, cellCo, cellInfos[9
274 * cellLi + cellCo]));
277 public void setCellMemos(int li, int co, byte[] values) {
278 for (int i = 0; i < values.length; i++) {
279 setCellMemo(li, co, values[i]);
281 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
285 private short getMemoFlag(byte value) {
288 return FLAG_CELL_MEMO_1;
290 return FLAG_CELL_MEMO_2;
292 return FLAG_CELL_MEMO_3;
294 return FLAG_CELL_MEMO_4;
296 return FLAG_CELL_MEMO_5;
298 return FLAG_CELL_MEMO_6;
300 return FLAG_CELL_MEMO_7;
302 return FLAG_CELL_MEMO_8;
304 return FLAG_CELL_MEMO_9;
306 throw new IllegalArgumentException("GridModel.getMemoFlag() value "
307 + value + " is illegal");
311 public byte getNbOfPossibleValues(int li, int co) {
312 short infos = cellInfos[9 * li + co];
314 for (byte i = 1; i <= 9; i++) {
315 nb += ((infos & getMemoFlag(i)) != 0) ? 1 : 0;
320 private void setCellMemo(int li, int co, byte value) {
321 cellInfos[9 * li + co] |= getMemoFlag(value);
324 public void clearCellMemos(int li, int co) {
325 cellInfos[9 * li + co] &= ~MASK_CELL_MEMOS;
328 public boolean isCellValueSet(int li, int co, Byte value) {
329 return (cellInfos[9 * li + co] & value.byteValue()) != 0;
332 public boolean isCellFilled(int li, int co) {
333 return (cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0;
337 * For use for initial values in the grid, and when doing a custom grid, for
343 public void setCellReadOnly(int li, int co) {
344 cellInfos[9 * li + co] |= FLAG_CELL_READ_ONLY;
347 public void setCellValue(int li, int co, int value, boolean silent) {
348 cellInfos[9 * li + co] &= ~MASK_CELL_VALUES;
349 cellInfos[9 * li + co] |= value;
350 clearCellMemos(li, co);
352 if (silent == false) {
353 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
357 if (!silent && isGridFull()) {
358 GridValidity validity = checkGridValidity();
359 if (validity.isGridValid()) {
362 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
366 public void fireGridChanged(GridChangedEvent event) {
368 for (GridListener listener : listeners) {
369 listener.gridChanged(event);
373 public boolean isCellMemoSet(int li, int co, byte value) {
374 return (cellInfos[9 * li + co] & getMemoFlag(value)) != 0;
377 public boolean isCellReadOnly(int li, int co) {
378 return !isCustomGridModeON && (cellInfos[9 * li + co] & FLAG_CELL_READ_ONLY) != 0;
381 public boolean isGridFull() {
382 for (int li=0; li<9; li++) {
383 for (int co=0; co<9; co++) {
384 if (!isCellFilled(li, co)) {
392 public boolean isGridComplete() {
393 return (cellInfos[0] & FLAG_GRID_COMPLETE) != 0;
396 public void setGridResolved() {
397 cellInfos[0] |= FLAG_GRID_COMPLETE;
401 private void fireGridResolved() {
402 for (GridListener listener : listeners) {
403 listener.gridResolved();
407 public void setGridComplete() {
408 cellInfos[0] |= FLAG_GRID_COMPLETE;
412 private void fireGridComplete() {
413 for (GridListener listener : listeners) {
414 listener.gridComplete();
418 public byte getValueAt(int li, int co) {
419 byte value = (byte) (cellInfos[9 * li + co] & MASK_CELL_VALUES);
423 public int[] cloneCellInfosAsInts() {
424 int[] ints = new int[cellInfos.length];
425 for (int i=0; i<cellInfos.length; i++) {
426 ints[i] = cellInfos[i];
431 public void requestNewGrid() {
435 public void clearAllUserMoves() {
436 for (int li = 0; li < 9; li++) {
437 for (int co = 0; co < 9; co++) {
438 if (!isCellReadOnly(li, co)) {
439 cellInfos[9 * li + co] &= ~MASK_CELL_VALUES;
443 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
446 public void clearAllUserMemos() {
447 for (int li = 0; li < 9; li++) {
448 for (int co = 0; co < 9; co++) {
449 clearCellMemos(li, co);
452 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
455 public void enterCustomGridMode() {
456 isCustomGridModeON = true;
457 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
460 public GridValidity getGridValidity() {
461 if (!isCustomGridModeON && !isGridFull()) {
462 return GridValidity.VALID;
464 return checkGridValidity();
467 public void exitCustomGridMode() {
468 isCustomGridModeON = false;
469 for (int li = 0; li < 9; li++) {
470 for (int co = 0; co < 9; co++) {
471 if (isCellFilled(li, co)) {
472 setCellReadOnly(li, co);
474 cellInfos[9 * li + co] &= ~FLAG_CELL_READ_ONLY;
478 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
481 public boolean getCustomGridMode() {
482 return isCustomGridModeON;
485 public static class GridValidity {
486 public static final GridValidity VALID = new GridValidity(true, null, null, null, null);
488 private boolean isValid = true;
489 private Integer firstErrorLine = null;
490 private Integer firstErrorColumn = null;
491 private Integer firstErrorSquareX = null;
492 private Integer firstErrorSquareY = null;
494 private GridValidity(boolean isValid, Integer lineWithError, Integer columnWithError,
495 Integer squareWithErrorX, Integer squareWithErrorY) {
496 this.isValid = isValid;
497 this.firstErrorLine = lineWithError;
498 this.firstErrorColumn = columnWithError;
499 this.firstErrorSquareX = squareWithErrorX;
500 this.firstErrorSquareY = squareWithErrorY;
503 private static GridValidity valueOf(boolean isValid, Integer lineWithError, Integer columnWithError,
504 Integer squareWithErrorX, Integer squareWithErrorY) {
508 return new GridValidity(isValid, lineWithError, columnWithError,
509 squareWithErrorX, squareWithErrorY);
514 public boolean isGridValid() {
518 public Integer getFirstErrorLine() {
519 return firstErrorLine;
522 public Integer getFirstErrorColumn() {
523 return firstErrorColumn;
526 public Integer getFirstErrorSquareX() {
527 return firstErrorSquareX;
530 public Integer getFirstErrorSquareY() {
531 return firstErrorSquareY;
536 private GridValidity checkGridValidity() {
537 boolean isValid = true;
538 Integer lineWithError = null;
539 Integer columnWithError = null;
540 Integer squareWithErrorX = null;
541 Integer squareWithErrorY = null;
543 // Check validity of all lines
544 for (int li = 0; isValid && li < 9; li++) {
545 byte[] numbers = new byte[10];
546 for (int co = 0; co < 9; co++) {
547 byte value = getValueAt(li, co);
548 if (numbers[value] != 0) {
550 lineWithError = Integer.valueOf(li);
553 numbers[value] = value;
556 // Check validity of all columns
557 for (int co = 0; isValid && co < 9; co++) {
558 byte[] numbers = new byte[10];
559 for (int li = 0; li < 9; li++) {
560 byte value = getValueAt(li, co);
561 if (numbers[value] != 0) {
563 columnWithError = Integer.valueOf(co);
566 numbers[value] = value;
569 // Check validity of all squares
570 for (int X = 0; isValid && X < 9; X += 3) { // left pos of a square
571 for (int Y = 0; isValid && Y < 9; Y += 3) { // top pos of a square
572 int[] numbers = new int[10];
573 for (int x = 0; isValid && x < 3; x++) {
574 for (int y = 0; isValid && y < 3; y++) {
577 byte value = getValueAt(li, co);
578 if (numbers[value] != 0) {
580 squareWithErrorX = Integer.valueOf(X);
581 squareWithErrorY = Integer.valueOf(Y);
584 numbers[value] = value;
589 return GridValidity.valueOf(isValid, lineWithError, columnWithError,
590 squareWithErrorX, squareWithErrorY);
593 public boolean areSomeMemosSet() {
594 for (int li = 0; li < 9; li++) {
595 for (int co = 0; co < 9; co++) {
596 if ((cellInfos[9 * li + co] & MASK_CELL_MEMOS) != 0)
603 public boolean areSomeCellsFilled() {
604 for (int li = 0; li < 9; li++) {
605 for (int co = 0; co < 9; co++) {
606 if (isCellReadOnly(li, co))
608 if ((cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0)
615 public boolean areSomeCellsEmpty() {
616 for (int li = 0; li < 9; li++) {
617 for (int co = 0; co < 9; co++) {
618 if (isCellFilled(li, co)) {
628 public int[] asIntArray() {
629 int[] ints = new int[cellInfos.length];
630 for (int i=0; i<cellInfos.length; i++) {
631 ints[i] = cellInfos[i];