OSDN Git Service

Removed spurious System.out.println calls.
[sudokuki/sudokuki.git] / src / classes / net / jankenpoi / sudokuki / model / GridModel.java
1 /*
2  * Sudokuki - essential sudoku game
3  * Copyright (C) 2007-2012 Sylvain Vedrenne
4  *
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.
9  * 
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.
14  * 
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/>.
17  */
18 package net.jankenpoi.sudokuki.model;
19
20 import java.util.ArrayList;
21 import java.util.List;
22
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;
27
28 /**
29  * (MVC) model for a sudoku grid
30  * 
31  * @author Sylvain Vedrenne
32  * 
33  */
34 public class GridModel implements Cloneable {
35
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
38
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
49         
50         public static final int FLAG_GRID_COMPLETE = 0x4000; // _0100.0000-0000.0000
51
52         private List<GridListener> listeners = new ArrayList<GridListener>();
53         
54         private boolean isCustomGridModeON = false;
55
56         /**
57          * 
58          * Values and flags for all cells
59          */
60         private short[] cellInfos = new short[81];
61
62         public GridModel(short[] flagsTable, int startIdx) {
63                 copyFlagsToGrid(flagsTable, startIdx);
64         }
65         
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);
69                         cellInfos[i] = value;
70                         if (1 <= value && value <= 9) {
71                                 cellInfos[i] |= FLAG_CELL_READ_ONLY;
72                         }
73                 }
74         }
75         
76         public GridModel() {
77                 newGrid();
78         }
79
80         public void resetGridModelFromShorts(short[] externalCellInfos) {
81                 cellInfos = new short[81];
82                 
83                 for (int i=0; i<81; i++) {
84                         cellInfos[i] = externalCellInfos[i];
85                 }
86         }
87         
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);
92
93                 for (int i = 0; i < cellInfos.length; i++) {
94                         short value = (short) grid.getValueAt(i);
95                         cellInfos[i] = value;
96                         if (1 <= value && value <= 9) {
97                                 cellInfos[i] |= FLAG_CELL_READ_ONLY;
98                         }
99                 }
100         }
101
102         /**
103          * FIXME: For the moment, this constructor is for testing use only....
104          * 
105          * @param strValues
106          */
107         public GridModel(String strValues) {
108                 if (strValues == null) {
109                         return;
110                 }
111                 System.out
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;
120                         }
121                 }
122                 System.out.println();
123         }
124
125         public void addGridListener(GridListener view) {
126                 listeners.add(view);
127         }
128
129         short getCellInfosAt(int li, int co) {
130                 return cellInfos[9 * li + co];
131         }
132
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;
138                                 }
139                         }
140                 }
141
142                 // parcourir tous les carres
143                 // - pour chaque carre, cribler les memos
144                 // -- pour chaque cellule, cribler les memos
145                 //
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++) {
151                                                 int co = X + x;
152                                                 int li = Y + y;
153                                                 if (isCellFilled(li, co)) {
154                                                         byte value = getValueAt(li, co);
155                                                         currentValuesMask |= getMemoFlag(value);
156                                                 }
157                                         }
158                                 }
159                                 for (int x = 0; x < 3; x++) {
160                                         for (int y = 0; y < 3; y++) {
161                                                 int co = X + x;
162                                                 int li = Y + y;
163                                                 if (!isCellFilled(li, co)) {
164                                                         cellInfos[9 * li + co] &= ~currentValuesMask;
165                                                 }
166                                         }
167                                 }
168                         }
169                 }
170
171                 // parcourir toutes les lignes
172                 // - pour chaque ligne, cribler les memos
173                 // -- pour chaque cellule, cribler les memos
174                 //
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);
181                                 }
182                         }
183                         for (int co = 0; co < 9; co++) {
184                                 if (!isCellFilled(li, co)) {
185                                         cellInfos[9 * li + co] &= ~currentValuesMask;
186                                 }
187                         }
188                 }
189
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);
199                                 }
200                         }
201                         for (int li = 0; li < 9; li++) {
202                                 if (!isCellFilled(li, co)) {
203                                         cellInfos[9 * li + co] &= ~currentValuesMask;
204                                 }
205                         }
206                 }
207                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
208         }
209
210         public void setMemosForThisCell(int cellLi, int cellCo) {
211                 if (isCellFilled(cellLi, cellCo))
212                         return;
213                 cellInfos[9 * cellLi + cellCo] = MASK_CELL_MEMOS;
214
215                 
216                 // parcourir le carre courant
217                 // - pour chaque carre, cribler les memos
218                 // -- pour chaque cellule, cribler les memos
219                 //
220                 { // left pos of a square
221
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++) {
227                                         int co = X + x;
228                                         int li = Y + y;
229                                         if (isCellFilled(li, co)) {
230                                                 byte value = getValueAt(li, co);
231                                                 currentValuesMask |= getMemoFlag(value);
232                                         }
233                                 }
234                         }
235                         cellInfos[9 * cellLi + cellCo] &= ~currentValuesMask;
236                 }
237
238                 // parcourir la ligne de cette cellule
239                 // - pour chaque ligne, cribler les memos
240                 // -- pour chaque cellule, cribler les memos
241                 //
242                 {
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);
248                                 }
249                         }
250                         for (int co = 0; co < 9; co++) {
251                                 if (!isCellFilled(cellLi, co)) {
252                                         cellInfos[9 * cellLi + co] &= ~currentValuesMask;
253                                 }
254                         }
255                 }
256
257                 // parcourir la colonne de cette cellule
258                 // - pour chaque colonne, cribler les memos
259                 // -- pour chaque cellule, cribler les memos
260                 {
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);
266                                 }
267                         }
268                         for (int li = 0; li < 9; li++) {
269                                 if (!isCellFilled(li, cellCo)) {
270                                         cellInfos[9 * li + cellCo] &= ~currentValuesMask;
271                                 }
272                         }
273                 }
274                 fireGridChanged(new GridChangedEvent(this, cellLi, cellCo, cellInfos[9
275                                 * cellLi + cellCo]));
276         }
277         
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]);
281                 }
282                 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
283                                 + co]));
284         }
285
286         private short getMemoFlag(byte value) {
287                 switch (value) {
288                 case 1:
289                         return FLAG_CELL_MEMO_1;
290                 case 2:
291                         return FLAG_CELL_MEMO_2;
292                 case 3:
293                         return FLAG_CELL_MEMO_3;
294                 case 4:
295                         return FLAG_CELL_MEMO_4;
296                 case 5:
297                         return FLAG_CELL_MEMO_5;
298                 case 6:
299                         return FLAG_CELL_MEMO_6;
300                 case 7:
301                         return FLAG_CELL_MEMO_7;
302                 case 8:
303                         return FLAG_CELL_MEMO_8;
304                 case 9:
305                         return FLAG_CELL_MEMO_9;
306                 default:
307                         throw new IllegalArgumentException("GridModel.getMemoFlag() value "
308                                         + value + " is illegal");
309                 }
310         }
311
312         public byte getNbOfPossibleValues(int li, int co) {
313                 short infos = cellInfos[9 * li + co];
314                 byte nb = 0;
315                 for (byte i = 1; i <= 9; i++) {
316                         nb += ((infos & getMemoFlag(i)) != 0) ? 1 : 0;
317                 }
318                 return nb;
319         }
320
321         private void setCellMemo(int li, int co, byte value) {
322                 cellInfos[9 * li + co] |= getMemoFlag(value);
323         }
324
325         public void clearCellMemos(int li, int co) {
326                 cellInfos[9 * li + co] &= ~MASK_CELL_MEMOS;
327         }
328
329         public boolean isCellValueSet(int li, int co, Byte value) {
330                 return (cellInfos[9 * li + co] & value.byteValue()) != 0;
331         }
332
333         public boolean isCellFilled(int li, int co) {
334                 return (cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0;
335         }
336
337         /**
338          * For use for initial values in the grid, and when doing a custom grid, for
339          * instance...
340          * 
341          * @param li
342          * @param co
343          */
344         public void setCellReadOnly(int li, int co) {
345                 cellInfos[9 * li + co] |= FLAG_CELL_READ_ONLY;
346         }
347
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);
352                 
353                 if (silent == false) {
354                         fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
355                                 + co]));
356                 }
357                 
358                 if (!silent && isGridFull()) {
359                         GridValidity validity = checkGridValidity();
360                         if (validity.isGridValid()) {
361                                 setGridComplete();
362                         }
363                         fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
364                 }
365         }
366
367         public void fireGridChanged(GridChangedEvent event) {
368
369                 for (GridListener listener : listeners) {
370                         listener.gridChanged(event);
371                 }
372         }
373
374         public boolean isCellMemoSet(int li, int co, byte value) {
375                 return (cellInfos[9 * li + co] & getMemoFlag(value)) != 0;
376         }
377
378         public boolean isCellReadOnly(int li, int co) {
379                 return !isCustomGridModeON && (cellInfos[9 * li + co] & FLAG_CELL_READ_ONLY) != 0;
380         }
381
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)) {
386                                         return false;
387                                 }
388                         }
389                 }
390                 return true;
391         }
392         
393         public boolean isGridComplete() {
394                 return (cellInfos[0] & FLAG_GRID_COMPLETE) != 0;
395         }
396
397         public void setGridResolved() {
398                 cellInfos[0] |= FLAG_GRID_COMPLETE;
399                 fireGridResolved();
400         }
401         
402         private void fireGridResolved() {
403                 for (GridListener listener : listeners) {
404                         listener.gridResolved();
405                 }
406         }
407         
408         public void setGridComplete() {
409                 cellInfos[0] |= FLAG_GRID_COMPLETE;
410                 fireGridComplete();
411         }
412
413         private void fireGridComplete() {
414                 for (GridListener listener : listeners) {
415                         listener.gridComplete();
416                 }
417         }
418         
419         public byte getValueAt(int li, int co) {
420                 byte value = (byte) (cellInfos[9 * li + co] & MASK_CELL_VALUES);
421                 return value;
422         }
423
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];
428                 }
429                 return ints;
430         }
431
432         public void requestNewGrid() {
433                 newGrid();
434         }
435
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;
441                                 }
442                         }
443                 }
444                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
445         }
446
447         public void clearAllUserMemos() {
448                 for (int li = 0; li < 9; li++) {
449                         for (int co = 0; co < 9; co++) {
450                                 clearCellMemos(li, co);
451                         }
452                 }
453                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
454         }
455         
456         public void enterCustomGridMode() {
457                 isCustomGridModeON = true;
458                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
459         }
460         
461         public GridValidity getGridValidity() {
462                 if (!isCustomGridModeON && !isGridFull()) {
463                         return GridValidity.VALID;
464                 }
465                 return checkGridValidity();
466         }
467         
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);
474                                 } else {
475                                         cellInfos[9 * li + co] &= ~FLAG_CELL_READ_ONLY;
476                                 }
477                         }
478                 }
479                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
480         }
481         
482         public boolean getCustomGridMode() {
483                 return isCustomGridModeON;
484         }
485         
486         public static class GridValidity {
487                 public static final GridValidity VALID = new GridValidity(true, null, null, null, null);
488                 
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;
494                 
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;
502                 }
503                 
504                 private static GridValidity valueOf(boolean isValid, Integer lineWithError, Integer columnWithError,
505                                 Integer squareWithErrorX, Integer squareWithErrorY) {
506                         if (isValid) {
507                                 return VALID;
508                         } else {
509                                 return new GridValidity(isValid, lineWithError, columnWithError,
510                                                 squareWithErrorX, squareWithErrorY);
511                         }
512                         
513                 }
514                 
515                 public boolean isGridValid() {
516                         return isValid;
517                 }
518                                 
519                 public Integer getFirstErrorLine() {
520                         return firstErrorLine;
521                 }
522                 
523                 public Integer getFirstErrorColumn() {
524                         return firstErrorColumn;
525                 }
526                 
527                 public Integer getFirstErrorSquareX() {
528                         return firstErrorSquareX;
529                 }
530                 
531                 public Integer getFirstErrorSquareY() {
532                         return firstErrorSquareY;
533                 }
534                 
535         }
536
537         private GridValidity checkGridValidity() {
538                 boolean isValid = true;
539                 Integer lineWithError = null;
540                 Integer columnWithError = null;
541                 Integer squareWithErrorX = null;
542                 Integer squareWithErrorY = null;
543                 
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) {
550                                         isValid = false;
551                                         lineWithError = Integer.valueOf(li);
552                                         break;
553                                 }
554                                 numbers[value] = value;
555                         }
556                 }
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) {
563                                         isValid = false;
564                                         columnWithError = Integer.valueOf(co);
565                                         break;
566                                 }
567                                 numbers[value] = value;
568                         }
569                 }
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++) {
576                                                 int co = X + x;
577                                                 int li = Y + y;
578                                                 byte value = getValueAt(li, co);
579                                                 if (numbers[value] != 0) {
580                                                         isValid = false;
581                                                         squareWithErrorX = Integer.valueOf(X);
582                                                         squareWithErrorY = Integer.valueOf(Y);
583                                                         break;
584                                                 }
585                                                 numbers[value] = value;
586                                         }
587                                 }
588                         }
589                 }
590                 return GridValidity.valueOf(isValid, lineWithError, columnWithError,
591                                 squareWithErrorX, squareWithErrorY);
592         }
593         
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)
598                                         return true;
599                         }
600                 }
601                 return false;
602         }
603         
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))
608                                         continue;
609                                 if ((cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0)
610                                         return true;
611                         }
612                 }
613                 return false;
614         }
615         
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)) {
620                                         continue;
621                                 } else {
622                                         return true;
623                                 }
624                         }
625                 }
626                 return false;
627         }
628
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];
633                 }
634                 return ints;
635         }
636         
637 }