OSDN Git Service

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