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];
83 for (int i=0; i<81; i++) {
84 cellInfos[i] = externalCellInfos[i];
88 private void newGrid() {
89 final int minRating = UserPreferences.getInstance().getInteger("minRating", 0);
90 final int maxRating = UserPreferences.getInstance().getInteger("maxRating", Integer.MAX_VALUE);
91 SudokuGrid grid = SudokuGeneratorFactory.getGenerator().generateGrid(minRating, maxRating);
93 for (int i = 0; i < cellInfos.length; i++) {
94 short value = (short) grid.getValueAt(i);
96 if (1 <= value && value <= 9) {
97 cellInfos[i] |= FLAG_CELL_READ_ONLY;
103 * FIXME: For the moment, this constructor is for testing use only....
107 public GridModel(String strValues) {
108 if (strValues == null) {
112 .println("GridModel.GridModel() length:" + strValues.length());
113 System.out.println("GridModel.GridModel() strValues:" + strValues);
114 for (int i = 0; i < strValues.length(); i++) {
115 short value = Short.valueOf(strValues.substring(i, i + 1));
116 System.out.print(value);
117 cellInfos[i] = value;
118 if (1 <= value && value <= 9) {
119 cellInfos[i] |= FLAG_CELL_READ_ONLY;
122 System.out.println();
125 public void addGridListener(GridListener view) {
129 short getCellInfosAt(int li, int co) {
130 return cellInfos[9 * li + co];
133 public void setMemosForAllCells() {
134 for (int li = 0; li < 9; li++) {
135 for (int co = 0; co < 9; co++) {
136 if (!isCellFilled(li, co)) {
137 cellInfos[9 * li + co] = MASK_CELL_MEMOS;
142 // parcourir tous les carres
143 // - pour chaque carre, cribler les memos
144 // -- pour chaque cellule, cribler les memos
146 for (int X = 0; X < 9; X += 3) { // left pos of a square
147 for (int Y = 0; Y < 9; Y += 3) { // top pos of a square
148 short currentValuesMask = 0;
149 for (int x = 0; x < 3; x++) {
150 for (int y = 0; y < 3; y++) {
153 if (isCellFilled(li, co)) {
154 byte value = getValueAt(li, co);
155 currentValuesMask |= getMemoFlag(value);
159 for (int x = 0; x < 3; x++) {
160 for (int y = 0; y < 3; y++) {
163 if (!isCellFilled(li, co)) {
164 cellInfos[9 * li + co] &= ~currentValuesMask;
171 // parcourir toutes les lignes
172 // - pour chaque ligne, cribler les memos
173 // -- pour chaque cellule, cribler les memos
175 for (int li = 0; li < 9; li++) { // each line
176 short currentValuesMask = 0;
177 for (int co = 0; co < 9; co++) {
178 if (isCellFilled(li, co)) {
179 byte value = getValueAt(li, co);
180 currentValuesMask |= getMemoFlag(value);
183 for (int co = 0; co < 9; co++) {
184 if (!isCellFilled(li, co)) {
185 cellInfos[9 * li + co] &= ~currentValuesMask;
190 // parcourir toutes les colonnes
191 // - pour chaque colonne, cribler les memos
192 // -- pour chaque cellule, cribler les memos
193 for (int co = 0; co < 9; co++) { // each column
194 short currentValuesMask = 0;
195 for (int li = 0; li < 9; li++) {
196 if (isCellFilled(li, co)) {
197 byte value = getValueAt(li, co);
198 currentValuesMask |= getMemoFlag(value);
201 for (int li = 0; li < 9; li++) {
202 if (!isCellFilled(li, co)) {
203 cellInfos[9 * li + co] &= ~currentValuesMask;
207 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
210 public void setMemosForThisCell(int cellLi, int cellCo) {
211 if (isCellFilled(cellLi, cellCo))
213 cellInfos[9 * cellLi + cellCo] = MASK_CELL_MEMOS;
216 // parcourir le carre courant
217 // - pour chaque carre, cribler les memos
218 // -- pour chaque cellule, cribler les memos
220 { // left pos of a square
222 int X = 3 * (cellCo / 3);
223 int Y = 3 * (cellLi / 3);
224 short currentValuesMask = 0;
225 for (int x = 0; x < 3; x++) {
226 for (int y = 0; y < 3; y++) {
229 if (isCellFilled(li, co)) {
230 byte value = getValueAt(li, co);
231 currentValuesMask |= getMemoFlag(value);
235 cellInfos[9 * cellLi + cellCo] &= ~currentValuesMask;
238 // parcourir la ligne de cette cellule
239 // - pour chaque ligne, cribler les memos
240 // -- pour chaque cellule, cribler les memos
243 short currentValuesMask = 0;
244 for (int co = 0; co < 9; co++) {
245 if (isCellFilled(cellLi, co)) {
246 byte value = getValueAt(cellLi, co);
247 currentValuesMask |= getMemoFlag(value);
250 for (int co = 0; co < 9; co++) {
251 if (!isCellFilled(cellLi, co)) {
252 cellInfos[9 * cellLi + co] &= ~currentValuesMask;
257 // parcourir la colonne de cette cellule
258 // - pour chaque colonne, cribler les memos
259 // -- pour chaque cellule, cribler les memos
261 short currentValuesMask = 0;
262 for (int li = 0; li < 9; li++) {
263 if (isCellFilled(li, cellCo)) {
264 byte value = getValueAt(li, cellCo);
265 currentValuesMask |= getMemoFlag(value);
268 for (int li = 0; li < 9; li++) {
269 if (!isCellFilled(li, cellCo)) {
270 cellInfos[9 * li + cellCo] &= ~currentValuesMask;
274 fireGridChanged(new GridChangedEvent(this, cellLi, cellCo, cellInfos[9
275 * cellLi + cellCo]));
278 public void setCellMemos(int li, int co, byte[] values) {
279 for (int i = 0; i < values.length; i++) {
280 setCellMemo(li, co, values[i]);
282 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
286 private short getMemoFlag(byte value) {
289 return FLAG_CELL_MEMO_1;
291 return FLAG_CELL_MEMO_2;
293 return FLAG_CELL_MEMO_3;
295 return FLAG_CELL_MEMO_4;
297 return FLAG_CELL_MEMO_5;
299 return FLAG_CELL_MEMO_6;
301 return FLAG_CELL_MEMO_7;
303 return FLAG_CELL_MEMO_8;
305 return FLAG_CELL_MEMO_9;
307 throw new IllegalArgumentException("GridModel.getMemoFlag() value "
308 + value + " is illegal");
312 public byte getNbOfPossibleValues(int li, int co) {
313 short infos = cellInfos[9 * li + co];
315 for (byte i = 1; i <= 9; i++) {
316 nb += ((infos & getMemoFlag(i)) != 0) ? 1 : 0;
321 private void setCellMemo(int li, int co, byte value) {
322 cellInfos[9 * li + co] |= getMemoFlag(value);
325 public void clearCellMemos(int li, int co) {
326 cellInfos[9 * li + co] &= ~MASK_CELL_MEMOS;
329 public boolean isCellValueSet(int li, int co, Byte value) {
330 return (cellInfos[9 * li + co] & value.byteValue()) != 0;
333 public boolean isCellFilled(int li, int co) {
334 return (cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0;
338 * For use for initial values in the grid, and when doing a custom grid, for
344 public void setCellReadOnly(int li, int co) {
345 cellInfos[9 * li + co] |= FLAG_CELL_READ_ONLY;
348 public void setCellValue(int li, int co, int value, boolean silent) {
349 cellInfos[9 * li + co] &= ~MASK_CELL_VALUES;
350 cellInfos[9 * li + co] |= value;
351 clearCellMemos(li, co);
353 if (silent == false) {
354 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
358 if (!silent && isGridFull()) {
359 GridValidity validity = checkGridValidity();
360 if (validity.isGridValid()) {
363 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
367 public void fireGridChanged(GridChangedEvent event) {
369 for (GridListener listener : listeners) {
370 listener.gridChanged(event);
374 public boolean isCellMemoSet(int li, int co, byte value) {
375 return (cellInfos[9 * li + co] & getMemoFlag(value)) != 0;
378 public boolean isCellReadOnly(int li, int co) {
379 return !isCustomGridModeON && (cellInfos[9 * li + co] & FLAG_CELL_READ_ONLY) != 0;
382 public boolean isGridFull() {
383 for (int li=0; li<9; li++) {
384 for (int co=0; co<9; co++) {
385 if (!isCellFilled(li, co)) {
393 public boolean isGridComplete() {
394 return (cellInfos[0] & FLAG_GRID_COMPLETE) != 0;
397 public void setGridResolved() {
398 cellInfos[0] |= FLAG_GRID_COMPLETE;
402 private void fireGridResolved() {
403 for (GridListener listener : listeners) {
404 listener.gridResolved();
408 public void setGridComplete() {
409 cellInfos[0] |= FLAG_GRID_COMPLETE;
413 private void fireGridComplete() {
414 for (GridListener listener : listeners) {
415 listener.gridComplete();
419 public byte getValueAt(int li, int co) {
420 byte value = (byte) (cellInfos[9 * li + co] & MASK_CELL_VALUES);
424 public int[] cloneCellInfosAsInts() {
425 int[] ints = new int[cellInfos.length];
426 for (int i=0; i<cellInfos.length; i++) {
427 ints[i] = cellInfos[i];
432 public void requestNewGrid() {
436 public void clearAllUserMoves() {
437 for (int li = 0; li < 9; li++) {
438 for (int co = 0; co < 9; co++) {
439 if (!isCellReadOnly(li, co)) {
440 cellInfos[9 * li + co] &= ~MASK_CELL_VALUES;
444 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
447 public void clearAllUserMemos() {
448 for (int li = 0; li < 9; li++) {
449 for (int co = 0; co < 9; co++) {
450 clearCellMemos(li, co);
453 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
456 public void enterCustomGridMode() {
457 isCustomGridModeON = true;
458 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
461 public GridValidity getGridValidity() {
462 if (!isCustomGridModeON && !isGridFull()) {
463 return GridValidity.VALID;
465 return checkGridValidity();
468 public void exitCustomGridMode() {
469 isCustomGridModeON = false;
470 for (int li = 0; li < 9; li++) {
471 for (int co = 0; co < 9; co++) {
472 if (isCellFilled(li, co)) {
473 setCellReadOnly(li, co);
475 cellInfos[9 * li + co] &= ~FLAG_CELL_READ_ONLY;
479 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
482 public boolean getCustomGridMode() {
483 return isCustomGridModeON;
486 public static class GridValidity {
487 public static final GridValidity VALID = new GridValidity(true, null, null, null, null);
489 private boolean isValid = true;
490 private Integer firstErrorLine = null;
491 private Integer firstErrorColumn = null;
492 private Integer firstErrorSquareX = null;
493 private Integer firstErrorSquareY = null;
495 private GridValidity(boolean isValid, Integer lineWithError, Integer columnWithError,
496 Integer squareWithErrorX, Integer squareWithErrorY) {
497 this.isValid = isValid;
498 this.firstErrorLine = lineWithError;
499 this.firstErrorColumn = columnWithError;
500 this.firstErrorSquareX = squareWithErrorX;
501 this.firstErrorSquareY = squareWithErrorY;
504 private static GridValidity valueOf(boolean isValid, Integer lineWithError, Integer columnWithError,
505 Integer squareWithErrorX, Integer squareWithErrorY) {
509 return new GridValidity(isValid, lineWithError, columnWithError,
510 squareWithErrorX, squareWithErrorY);
515 public boolean isGridValid() {
519 public Integer getFirstErrorLine() {
520 return firstErrorLine;
523 public Integer getFirstErrorColumn() {
524 return firstErrorColumn;
527 public Integer getFirstErrorSquareX() {
528 return firstErrorSquareX;
531 public Integer getFirstErrorSquareY() {
532 return firstErrorSquareY;
537 private GridValidity checkGridValidity() {
538 boolean isValid = true;
539 Integer lineWithError = null;
540 Integer columnWithError = null;
541 Integer squareWithErrorX = null;
542 Integer squareWithErrorY = null;
544 // Check validity of all lines
545 for (int li = 0; isValid && li < 9; li++) {
546 byte[] numbers = new byte[10];
547 for (int co = 0; co < 9; co++) {
548 byte value = getValueAt(li, co);
549 if (numbers[value] != 0) {
551 lineWithError = Integer.valueOf(li);
554 numbers[value] = value;
557 // Check validity of all columns
558 for (int co = 0; isValid && co < 9; co++) {
559 byte[] numbers = new byte[10];
560 for (int li = 0; li < 9; li++) {
561 byte value = getValueAt(li, co);
562 if (numbers[value] != 0) {
564 columnWithError = Integer.valueOf(co);
567 numbers[value] = value;
570 // Check validity of all squares
571 for (int X = 0; isValid && X < 9; X += 3) { // left pos of a square
572 for (int Y = 0; isValid && Y < 9; Y += 3) { // top pos of a square
573 int[] numbers = new int[10];
574 for (int x = 0; isValid && x < 3; x++) {
575 for (int y = 0; isValid && y < 3; y++) {
578 byte value = getValueAt(li, co);
579 if (numbers[value] != 0) {
581 squareWithErrorX = Integer.valueOf(X);
582 squareWithErrorY = Integer.valueOf(Y);
585 numbers[value] = value;
590 return GridValidity.valueOf(isValid, lineWithError, columnWithError,
591 squareWithErrorX, squareWithErrorY);
594 public boolean areSomeMemosSet() {
595 for (int li = 0; li < 9; li++) {
596 for (int co = 0; co < 9; co++) {
597 if ((cellInfos[9 * li + co] & MASK_CELL_MEMOS) != 0)
604 public boolean areSomeCellsFilled() {
605 for (int li = 0; li < 9; li++) {
606 for (int co = 0; co < 9; co++) {
607 if (isCellReadOnly(li, co))
609 if ((cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0)
616 public boolean areSomeCellsEmpty() {
617 for (int li = 0; li < 9; li++) {
618 for (int co = 0; co < 9; co++) {
619 if (isCellFilled(li, co)) {
629 public int[] asIntArray() {
630 int[] ints = new int[cellInfos.length];
631 for (int i=0; i<cellInfos.length; i++) {
632 ints[i] = cellInfos[i];