2 * Sudokuki - essential sudoku game
3 * Copyright (C) 2007-2011 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.model.GridModel.GridValidity;
26 import net.jankenpoi.sudokuki.preferences.UserPreferences;
27 import net.jankenpoi.sudokuki.view.GridListener;
30 * (MVC) model for a sudoku grid
32 * @author Sylvain Vedrenne
35 public class GridModel implements Cloneable {
37 public static final int MASK_CELL_VALUES = 0x000F; // ___0000.0000-0000.1111
38 public static final int MASK_CELL_MEMOS = 0x1FF0; // ____0001.1111-1111.0000
40 public static final int FLAG_CELL_MEMO_1 = 0x0010; // ___0000.0000-0001.0000
41 public static final int FLAG_CELL_MEMO_2 = 0x0020; // ___0000.0000-0010.0000
42 public static final int FLAG_CELL_MEMO_3 = 0x0040; // ___0000.0000-0100.0000
43 public static final int FLAG_CELL_MEMO_4 = 0x0080; // ___0000.0000-1000.0000
44 public static final int FLAG_CELL_MEMO_5 = 0x0100; // ___0000.0001-0000.0000
45 public static final int FLAG_CELL_MEMO_6 = 0x0200; // ___0000.0010-0000.0000
46 public static final int FLAG_CELL_MEMO_7 = 0x0400; // ___0000.0100-0000.0000
47 public static final int FLAG_CELL_MEMO_8 = 0x0800; // ___0000.1000-0000.0000
48 public static final int FLAG_CELL_MEMO_9 = 0x1000; // ___0001.0000-0000.0000
49 public static final int FLAG_CELL_READ_ONLY = 0x2000; // 0010.0000-0000.0000
51 // public static final int FLAG_CELL_SCREENED = 0x4000; // _0100.0000-0000.0000
52 // public static final int FLAG_CELL_FILLED = 0x8000; // ___1000.0000-0000.0000
54 private List<GridListener> listeners = new ArrayList<GridListener>();
56 private boolean isCustomGridModeON = false;
60 * Values and flags for all cells
62 private short[] cellInfos = new short[81];
64 public GridModel(short[] flagsTable, int startIdx) {
65 for (int i = 0; i < cellInfos.length; i++) {
66 short value = (short) (flagsTable[startIdx + i] & MASK_CELL_VALUES);
68 if (1 <= value && value <= 9) {
69 cellInfos[i] |= FLAG_CELL_READ_ONLY;
78 public void resetGridModelFromShorts(short[] externalCellInfos) {
79 cellInfos = new short[81];
81 for (int i=0; i<81; i++) {
82 cellInfos[i] = externalCellInfos[i];
86 private void newGrid() {
87 // SudokuGrid gridFast = SuexgProxy.getInstance().generateGrid();
88 // SudokuGrid grid = SuexgJava.getInstance().generateGrid(5000, 6000);
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 z=0; z<1000; z++) {
94 // gridFast = SuexgProxy.getInstance().generateGrid();
95 // grid = SuexgGenerator.getInstance().generateGrid();
96 // for (int k=0; k<81; k++) {
97 // if (grid.getValueAt(k) != gridFast.getValueAt(k)) {
98 // throw new IllegalStateException("GRIDS AREN'T IDENTICAL !!!");
103 for (int i = 0; i < cellInfos.length; i++) {
104 short value = (short) grid.getValueAt(i);
105 cellInfos[i] = value;
106 if (1 <= value && value <= 9) {
107 cellInfos[i] |= FLAG_CELL_READ_ONLY;
113 * FIXME: For the moment, this constructor is for testing use only....
117 public GridModel(String strValues) {
118 if (strValues == null) {
122 .println("GridModel.GridModel() length:" + strValues.length());
123 System.out.println("GridModel.GridModel() strValues:" + strValues);
124 for (int i = 0; i < strValues.length(); i++) {
125 short value = Short.valueOf(strValues.substring(i, i + 1));
126 System.out.print(value);
127 cellInfos[i] = value;
128 if (1 <= value && value <= 9) {
129 cellInfos[i] |= FLAG_CELL_READ_ONLY;
132 System.out.println();
135 public void addGridListener(GridListener view) {
139 short getCellInfosAt(int li, int co) {
140 return cellInfos[9 * li + co];
143 public void setMemosForAllCells() {
144 for (int li = 0; li < 9; li++) {
145 for (int co = 0; co < 9; co++) {
146 if (!isCellFilled(li, co)) {
147 cellInfos[9 * li + co] = MASK_CELL_MEMOS;
152 // parcourir tous les carres
153 // - pour chaque carre, cribler les memos
154 // -- pour chaque cellule, cribler les memos
156 for (int X = 0; X < 9; X += 3) { // left pos of a square
157 for (int Y = 0; Y < 9; Y += 3) { // top pos of a square
158 short currentValuesMask = 0;
159 for (int x = 0; x < 3; x++) {
160 for (int y = 0; y < 3; y++) {
163 if (isCellFilled(li, co)) {
164 byte value = getValueAt(li, co);
165 currentValuesMask |= getMemoFlag(value);
169 for (int x = 0; x < 3; x++) {
170 for (int y = 0; y < 3; y++) {
173 if (!isCellFilled(li, co)) {
174 cellInfos[9 * li + co] &= ~currentValuesMask;
181 // parcourir toutes les lignes
182 // - pour chaque ligne, cribler les memos
183 // -- pour chaque cellule, cribler les memos
185 for (int li = 0; li < 9; li++) { // each line
186 short currentValuesMask = 0;
187 for (int co = 0; co < 9; co++) {
188 if (isCellFilled(li, co)) {
189 byte value = getValueAt(li, co);
190 currentValuesMask |= getMemoFlag(value);
193 for (int co = 0; co < 9; co++) {
194 if (!isCellFilled(li, co)) {
196 // .println("GridModel.setMemosForAllCells() currentValuesMask:"
197 // + Integer.toHexString(currentValuesMask));
198 cellInfos[9 * li + co] &= ~currentValuesMask;
203 // parcourir toutes les colonnes
204 // - pour chaque colonne, cribler les memos
205 // -- pour chaque cellule, cribler les memos
206 for (int co = 0; co < 9; co++) { // each column
207 short currentValuesMask = 0;
208 for (int li = 0; li < 9; li++) {
209 if (isCellFilled(li, co)) {
210 byte value = getValueAt(li, co);
211 currentValuesMask |= getMemoFlag(value);
214 for (int li = 0; li < 9; li++) {
215 if (!isCellFilled(li, co)) {
217 .println("GridModel.setMemosForAllCells() currentValuesMask:"
218 + Integer.toHexString(currentValuesMask));
219 cellInfos[9 * li + co] &= ~currentValuesMask;
223 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
226 public void setMemosForThisCell(int cellLi, int cellCo) {
227 if (isCellFilled(cellLi, cellCo))
229 cellInfos[9 * cellLi + cellCo] = MASK_CELL_MEMOS;
232 // parcourir le carre courant
233 // - pour chaque carre, cribler les memos
234 // -- pour chaque cellule, cribler les memos
236 { // left pos of a square
238 int X = 3 * (cellCo / 3);
239 int Y = 3 * (cellLi / 3);
240 System.out.println("GridModel.setMemosForThisCell() cellLi:"+cellLi+" cellCo:"+cellCo+" X:"+X+" Y:"+Y);
241 short currentValuesMask = 0;
242 for (int x = 0; x < 3; x++) {
243 for (int y = 0; y < 3; y++) {
246 if (isCellFilled(li, co)) {
247 byte value = getValueAt(li, co);
248 currentValuesMask |= getMemoFlag(value);
252 cellInfos[9 * cellLi + cellCo] &= ~currentValuesMask;
255 // parcourir la ligne de cette cellule
256 // - pour chaque ligne, cribler les memos
257 // -- pour chaque cellule, cribler les memos
260 short currentValuesMask = 0;
261 for (int co = 0; co < 9; co++) {
262 if (isCellFilled(cellLi, co)) {
263 byte value = getValueAt(cellLi, co);
264 currentValuesMask |= getMemoFlag(value);
267 for (int co = 0; co < 9; co++) {
268 if (!isCellFilled(cellLi, co)) {
270 // .println("GridModel.setMemosForAllCells() currentValuesMask:"
271 // + Integer.toHexString(currentValuesMask));
272 cellInfos[9 * cellLi + co] &= ~currentValuesMask;
277 // parcourir la colonne de cette cellule
278 // - pour chaque colonne, cribler les memos
279 // -- pour chaque cellule, cribler les memos
281 short currentValuesMask = 0;
282 for (int li = 0; li < 9; li++) {
283 if (isCellFilled(li, cellCo)) {
284 byte value = getValueAt(li, cellCo);
285 currentValuesMask |= getMemoFlag(value);
288 for (int li = 0; li < 9; li++) {
289 if (!isCellFilled(li, cellCo)) {
291 .println("GridModel.setMemosForAllCells() currentValuesMask:"
292 + Integer.toHexString(currentValuesMask));
293 cellInfos[9 * li + cellCo] &= ~currentValuesMask;
297 fireGridChanged(new GridChangedEvent(this, cellLi, cellCo, cellInfos[9
298 * cellLi + cellCo]));
301 public void setCellMemos(int li, int co, byte[] values) {
302 for (int i = 0; i < values.length; i++) {
303 setCellMemo(li, co, values[i]);
305 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
309 private short getMemoFlag(byte value) {
312 return FLAG_CELL_MEMO_1;
314 return FLAG_CELL_MEMO_2;
316 return FLAG_CELL_MEMO_3;
318 return FLAG_CELL_MEMO_4;
320 return FLAG_CELL_MEMO_5;
322 return FLAG_CELL_MEMO_6;
324 return FLAG_CELL_MEMO_7;
326 return FLAG_CELL_MEMO_8;
328 return FLAG_CELL_MEMO_9;
330 throw new IllegalArgumentException("GridModel.getMemoFlag() value "
331 + value + " is illegal");
335 public byte getNbOfPossibleValues(int li, int co) {
336 short infos = cellInfos[9 * li + co];
338 for (byte i = 1; i <= 9; i++) {
339 nb += ((infos & getMemoFlag(i)) != 0) ? 1 : 0;
344 private void setCellMemo(int li, int co, byte value) {
345 cellInfos[9 * li + co] |= getMemoFlag(value);
348 public void clearCellMemos(int li, int co) {
349 cellInfos[9 * li + co] &= ~MASK_CELL_MEMOS;
352 public boolean isCellValueSet(int li, int co, Byte value) {
353 return (cellInfos[9 * li + co] & value.byteValue()) != 0;
356 public boolean isCellFilled(int li, int co) {
357 return (cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0;
361 * For use for initial values in the grid, and when doing a custom grid, for
367 public void setCellReadOnly(int li, int co) {
368 cellInfos[9 * li + co] |= FLAG_CELL_READ_ONLY;
371 public void setCellValue(int li, int co, int value) {
372 // System.out.println("GridModel.setCellValue() li:" + li + " co:" + co
373 // + " value:" + value);
374 // System.out.println("GridModel.setCellValue() cellInfos[9*li+co]:"
375 // + cellInfos[9 * li + co]);
376 cellInfos[9 * li + co] &= ~MASK_CELL_VALUES;
377 cellInfos[9 * li + co] |= value;
378 clearCellMemos(li, co);
379 // System.out.println("GridModel.setCellValue() cellInfos[9*li+co]:"
380 // + cellInfos[9 * li + co] + " (after)...");
381 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
385 public void fireGridChanged(GridChangedEvent event) {
387 for (GridListener listener : listeners) {
388 listener.gridChanged(event);
392 public boolean isCellMemoSet(int li, int co, byte value) {
393 return (cellInfos[9 * li + co] & getMemoFlag(value)) != 0;
396 public boolean isCellReadOnly(int li, int co) {
397 // System.out.println("GridView.isCellReadOnly() cellInfos[9 * li + co]:"+cellInfos[9
399 return !isCustomGridModeON && (cellInfos[9 * li + co] & FLAG_CELL_READ_ONLY) != 0;
402 public byte getValueAt(int li, int co) {
403 byte value = (byte) (cellInfos[9 * li + co] & MASK_CELL_VALUES);
407 public int[] cloneCellInfosAsInts() {
408 int[] ints = new int[cellInfos.length];
409 for (int i=0; i<cellInfos.length; i++) {
410 ints[i] = cellInfos[i];
411 // System.out.println("GridModel.cloneCellInfosAsInts() cellInfos["+i+"]:"+cellInfos[i]);
412 // System.out.println("GridModel.cloneCellInfosAsInts() ints["+i+"]:"+ints[i]);
417 public void requestNewGrid() {
421 public void clearAllUserMoves() {
422 for (int li = 0; li < 9; li++) {
423 for (int co = 0; co < 9; co++) {
424 if (!isCellReadOnly(li, co)) {
425 cellInfos[9 * li + co] &= ~MASK_CELL_VALUES;
429 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
432 public void clearAllUserMemos() {
433 for (int li = 0; li < 9; li++) {
434 for (int co = 0; co < 9; co++) {
435 clearCellMemos(li, co);
438 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
441 public void enterCustomGridMode() {
442 isCustomGridModeON = true;
443 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
446 public GridValidity getGridValidity() {
447 if (!isCustomGridModeON) {
448 return GridValidity.VALID;
450 return checkGridValidity();
453 public void exitCustomGridMode() {
454 isCustomGridModeON = false;
455 for (int li = 0; li < 9; li++) {
456 for (int co = 0; co < 9; co++) {
457 if (isCellFilled(li, co)) {
458 setCellReadOnly(li, co);
460 cellInfos[9 * li + co] &= ~FLAG_CELL_READ_ONLY;
464 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
467 public boolean getCustomGridMode() {
468 return isCustomGridModeON;
471 public static class GridValidity {
472 public static final GridValidity VALID = new GridValidity(true, null, null, null, null);
474 private boolean isValid = true;
475 private Integer firstErrorLine = null;
476 private Integer firstErrorColumn = null;
477 private Integer firstErrorSquareX = null;
478 private Integer firstErrorSquareY = null;
480 public GridValidity(boolean isValid, Integer lineWithError, Integer columnWithError,
481 Integer squareWithErrorX, Integer squareWithErrorY) {
482 this.isValid = isValid;
483 this.firstErrorLine = lineWithError;
484 this.firstErrorColumn = columnWithError;
485 this.firstErrorSquareX = squareWithErrorX;
486 this.firstErrorSquareY = squareWithErrorY;
489 public boolean isGridValid() {
493 public Integer getFirstErrorLine() {
494 return firstErrorLine;
497 public Integer getFirstErrorColumn() {
498 return firstErrorColumn;
501 public Integer getFirstErrorSquareX() {
502 return firstErrorSquareX;
505 public Integer getFirstErrorSquareY() {
506 return firstErrorSquareY;
511 private GridValidity checkGridValidity() {
512 boolean isValid = true;
513 Integer lineWithError = null;
514 Integer columnWithError = null;
515 Integer squareWithErrorX = null;
516 Integer squareWithErrorY = null;
518 // Check validity of all lines
519 for (int li = 0; isValid && li < 9; li++) {
520 byte[] numbers = new byte[10];
521 for (int co = 0; co < 9; co++) {
522 byte value = getValueAt(li, co);
523 System.out.println("GridModel.checkGridValidity() //lines// value:"+value);
524 if (numbers[value] != 0) {
526 lineWithError = Integer.valueOf(li);
529 numbers[value] = value;
532 // Check validity of all columns
533 for (int co = 0; isValid && co < 9; co++) {
534 byte[] numbers = new byte[10];
535 for (int li = 0; li < 9; li++) {
536 byte value = getValueAt(li, co);
537 System.out.println("GridModel.checkGridValidity() //columns// value:"+value);
538 if (numbers[value] != 0) {
540 columnWithError = Integer.valueOf(co);
543 numbers[value] = value;
546 // Check validity of all squares
547 for (int X = 0; isValid && X < 9; X += 3) { // left pos of a square
548 for (int Y = 0; isValid && Y < 9; Y += 3) { // top pos of a square
549 int[] numbers = new int[10];
550 for (int x = 0; isValid && x < 3; x++) {
551 for (int y = 0; isValid && y < 3; y++) {
554 byte value = getValueAt(li, co);
555 if (numbers[value] != 0) {
557 System.out.println("GridModel.checkGridValidity() is not valid SQUARE");
558 squareWithErrorX = Integer.valueOf(X);
559 squareWithErrorY = Integer.valueOf(Y);
562 numbers[value] = value;
567 return new GridValidity(isValid, lineWithError, columnWithError,
568 squareWithErrorX, squareWithErrorY);
571 public boolean areSomeMemosSet() {
572 for (int li = 0; li < 9; li++) {
573 for (int co = 0; co < 9; co++) {
574 if ((cellInfos[9 * li + co] & MASK_CELL_MEMOS) != 0)
581 public boolean areSomeCellsFilled() {
582 for (int li = 0; li < 9; li++) {
583 for (int co = 0; co < 9; co++) {
584 if (isCellReadOnly(li, co))
586 if ((cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0)
593 public boolean areSomeCellsEmpty() {
594 for (int li = 0; li < 9; li++) {
595 for (int co = 0; co < 9; co++) {
596 if (isCellFilled(li, co)) {
606 public int[] asIntArray() {
607 int[] ints = new int[cellInfos.length];
608 for (int i=0; i<cellInfos.length; i++) {
609 ints[i] = cellInfos[i];