OSDN Git Service

First GIT commit on Sourceforge.jp: added sudokuki main branch to GIT.
[sudokuki/sudokuki.git] / src / classes / net / jankenpoi / sudokuki / model / GridModel.java
1 /*
2  * Sudokuki - essential sudoku game
3  * Copyright (C) 2007-2011 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.model.GridModel.GridValidity;
26 import net.jankenpoi.sudokuki.preferences.UserPreferences;
27 import net.jankenpoi.sudokuki.view.GridListener;
28
29 /**
30  * (MVC) model for a sudoku grid
31  * 
32  * @author Sylvain Vedrenne
33  * 
34  */
35 public class GridModel implements Cloneable {
36
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
39
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
50
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
53
54         private List<GridListener> listeners = new ArrayList<GridListener>();
55         
56         private boolean isCustomGridModeON = false;
57
58         /**
59          * 
60          * Values and flags for all cells
61          */
62         private short[] cellInfos = new short[81];
63
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);
67                         cellInfos[i] = value;
68                         if (1 <= value && value <= 9) {
69                                 cellInfos[i] |= FLAG_CELL_READ_ONLY;
70                         }
71                 }
72         }
73         
74         public GridModel() {
75                 newGrid();
76         }
77
78         public void resetGridModelFromShorts(short[] externalCellInfos) {
79                 cellInfos = new short[81];
80                 
81                 for (int i=0; i<81; i++) {
82                         cellInfos[i] = externalCellInfos[i];
83                 }
84         }
85         
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);
92
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 !!!");
99 //                              }
100 //                      }
101 //              }
102
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;
108                         }
109                 }
110         }
111
112         /**
113          * FIXME: For the moment, this constructor is for testing use only....
114          * 
115          * @param strValues
116          */
117         public GridModel(String strValues) {
118                 if (strValues == null) {
119                         return;
120                 }
121                 System.out
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;
130                         }
131                 }
132                 System.out.println();
133         }
134
135         public void addGridListener(GridListener view) {
136                 listeners.add(view);
137         }
138
139         short getCellInfosAt(int li, int co) {
140                 return cellInfos[9 * li + co];
141         }
142
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;
148                                 }
149                         }
150                 }
151
152                 // parcourir tous les carres
153                 // - pour chaque carre, cribler les memos
154                 // -- pour chaque cellule, cribler les memos
155                 //
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++) {
161                                                 int co = X + x;
162                                                 int li = Y + y;
163                                                 if (isCellFilled(li, co)) {
164                                                         byte value = getValueAt(li, co);
165                                                         currentValuesMask |= getMemoFlag(value);
166                                                 }
167                                         }
168                                 }
169                                 for (int x = 0; x < 3; x++) {
170                                         for (int y = 0; y < 3; y++) {
171                                                 int co = X + x;
172                                                 int li = Y + y;
173                                                 if (!isCellFilled(li, co)) {
174                                                         cellInfos[9 * li + co] &= ~currentValuesMask;
175                                                 }
176                                         }
177                                 }
178                         }
179                 }
180
181                 // parcourir toutes les lignes
182                 // - pour chaque ligne, cribler les memos
183                 // -- pour chaque cellule, cribler les memos
184                 //
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);
191                                 }
192                         }
193                         for (int co = 0; co < 9; co++) {
194                                 if (!isCellFilled(li, co)) {
195                                         // System.out
196                                         // .println("GridModel.setMemosForAllCells() currentValuesMask:"
197                                         // + Integer.toHexString(currentValuesMask));
198                                         cellInfos[9 * li + co] &= ~currentValuesMask;
199                                 }
200                         }
201                 }
202
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);
212                                 }
213                         }
214                         for (int li = 0; li < 9; li++) {
215                                 if (!isCellFilled(li, co)) {
216                                         System.out
217                                                         .println("GridModel.setMemosForAllCells() currentValuesMask:"
218                                                                         + Integer.toHexString(currentValuesMask));
219                                         cellInfos[9 * li + co] &= ~currentValuesMask;
220                                 }
221                         }
222                 }
223                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
224         }
225
226         public void setMemosForThisCell(int cellLi, int cellCo) {
227                 if (isCellFilled(cellLi, cellCo))
228                         return;
229                 cellInfos[9 * cellLi + cellCo] = MASK_CELL_MEMOS;
230
231                 
232                 // parcourir le carre courant
233                 // - pour chaque carre, cribler les memos
234                 // -- pour chaque cellule, cribler les memos
235                 //
236                 { // left pos of a square
237
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++) {
244                                         int co = X + x;
245                                         int li = Y + y;
246                                         if (isCellFilled(li, co)) {
247                                                 byte value = getValueAt(li, co);
248                                                 currentValuesMask |= getMemoFlag(value);
249                                         }
250                                 }
251                         }
252                         cellInfos[9 * cellLi + cellCo] &= ~currentValuesMask;
253                 }
254
255                 // parcourir la ligne de cette cellule
256                 // - pour chaque ligne, cribler les memos
257                 // -- pour chaque cellule, cribler les memos
258                 //
259                 {
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);
265                                 }
266                         }
267                         for (int co = 0; co < 9; co++) {
268                                 if (!isCellFilled(cellLi, co)) {
269                                         // System.out
270                                         // .println("GridModel.setMemosForAllCells() currentValuesMask:"
271                                         // + Integer.toHexString(currentValuesMask));
272                                         cellInfos[9 * cellLi + co] &= ~currentValuesMask;
273                                 }
274                         }
275                 }
276
277                 // parcourir la colonne de cette cellule
278                 // - pour chaque colonne, cribler les memos
279                 // -- pour chaque cellule, cribler les memos
280                 {
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);
286                                 }
287                         }
288                         for (int li = 0; li < 9; li++) {
289                                 if (!isCellFilled(li, cellCo)) {
290                                         System.out
291                                                         .println("GridModel.setMemosForAllCells() currentValuesMask:"
292                                                                         + Integer.toHexString(currentValuesMask));
293                                         cellInfos[9 * li + cellCo] &= ~currentValuesMask;
294                                 }
295                         }
296                 }
297                 fireGridChanged(new GridChangedEvent(this, cellLi, cellCo, cellInfos[9
298                                 * cellLi + cellCo]));
299         }
300         
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]);
304                 }
305                 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
306                                 + co]));
307         }
308
309         private short getMemoFlag(byte value) {
310                 switch (value) {
311                 case 1:
312                         return FLAG_CELL_MEMO_1;
313                 case 2:
314                         return FLAG_CELL_MEMO_2;
315                 case 3:
316                         return FLAG_CELL_MEMO_3;
317                 case 4:
318                         return FLAG_CELL_MEMO_4;
319                 case 5:
320                         return FLAG_CELL_MEMO_5;
321                 case 6:
322                         return FLAG_CELL_MEMO_6;
323                 case 7:
324                         return FLAG_CELL_MEMO_7;
325                 case 8:
326                         return FLAG_CELL_MEMO_8;
327                 case 9:
328                         return FLAG_CELL_MEMO_9;
329                 default:
330                         throw new IllegalArgumentException("GridModel.getMemoFlag() value "
331                                         + value + " is illegal");
332                 }
333         }
334
335         public byte getNbOfPossibleValues(int li, int co) {
336                 short infos = cellInfos[9 * li + co];
337                 byte nb = 0;
338                 for (byte i = 1; i <= 9; i++) {
339                         nb += ((infos & getMemoFlag(i)) != 0) ? 1 : 0;
340                 }
341                 return nb;
342         }
343
344         private void setCellMemo(int li, int co, byte value) {
345                 cellInfos[9 * li + co] |= getMemoFlag(value);
346         }
347
348         public void clearCellMemos(int li, int co) {
349                 cellInfos[9 * li + co] &= ~MASK_CELL_MEMOS;
350         }
351
352         public boolean isCellValueSet(int li, int co, Byte value) {
353                 return (cellInfos[9 * li + co] & value.byteValue()) != 0;
354         }
355
356         public boolean isCellFilled(int li, int co) {
357                 return (cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0;
358         }
359
360         /**
361          * For use for initial values in the grid, and when doing a custom grid, for
362          * instance...
363          * 
364          * @param li
365          * @param co
366          */
367         public void setCellReadOnly(int li, int co) {
368                 cellInfos[9 * li + co] |= FLAG_CELL_READ_ONLY;
369         }
370
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
382                                 + co]));
383         }
384
385         public void fireGridChanged(GridChangedEvent event) {
386
387                 for (GridListener listener : listeners) {
388                         listener.gridChanged(event);
389                 }
390         }
391
392         public boolean isCellMemoSet(int li, int co, byte value) {
393                 return (cellInfos[9 * li + co] & getMemoFlag(value)) != 0;
394         }
395
396         public boolean isCellReadOnly(int li, int co) {
397                 // System.out.println("GridView.isCellReadOnly() cellInfos[9 * li + co]:"+cellInfos[9
398                 // * li + co]);
399                 return !isCustomGridModeON && (cellInfos[9 * li + co] & FLAG_CELL_READ_ONLY) != 0;
400         }
401
402         public byte getValueAt(int li, int co) {
403                 byte value = (byte) (cellInfos[9 * li + co] & MASK_CELL_VALUES);
404                 return value;
405         }
406
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]);
413                 }
414                 return ints;
415         }
416
417         public void requestNewGrid() {
418                 newGrid();
419         }
420
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;
426                                 }
427                         }
428                 }
429                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
430         }
431
432         public void clearAllUserMemos() {
433                 for (int li = 0; li < 9; li++) {
434                         for (int co = 0; co < 9; co++) {
435                                 clearCellMemos(li, co);
436                         }
437                 }
438                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
439         }
440         
441         public void enterCustomGridMode() {
442                 isCustomGridModeON = true;
443                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
444         }
445         
446         public GridValidity getGridValidity() {
447                 if (!isCustomGridModeON) {
448                         return GridValidity.VALID;
449                 }
450                 return checkGridValidity();
451         }
452         
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);
459                                 } else {
460                                         cellInfos[9 * li + co] &= ~FLAG_CELL_READ_ONLY;
461                                 }
462                         }
463                 }
464                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
465         }
466         
467         public boolean getCustomGridMode() {
468                 return isCustomGridModeON;
469         }
470         
471         public static class GridValidity {
472                 public static final GridValidity VALID = new GridValidity(true, null, null, null, null);
473                 
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;
479                 
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;
487                 }
488                 
489                 public boolean isGridValid() {
490                         return isValid;
491                 }
492                 
493                 public Integer getFirstErrorLine() {
494                         return firstErrorLine;
495                 }
496                 
497                 public Integer getFirstErrorColumn() {
498                         return firstErrorColumn;
499                 }
500                 
501                 public Integer getFirstErrorSquareX() {
502                         return firstErrorSquareX;
503                 }
504                 
505                 public Integer getFirstErrorSquareY() {
506                         return firstErrorSquareY;
507                 }
508                 
509         }
510
511         private GridValidity checkGridValidity() {
512                 boolean isValid = true;
513                 Integer lineWithError = null;
514                 Integer columnWithError = null;
515                 Integer squareWithErrorX = null;
516                 Integer squareWithErrorY = null;
517                 
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) {
525                                         isValid = false;
526                                         lineWithError = Integer.valueOf(li);
527                                         break;
528                                 }
529                                 numbers[value] = value;
530                         }
531                 }
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) {
539                                         isValid = false;
540                                         columnWithError = Integer.valueOf(co);
541                                         break;
542                                 }
543                                 numbers[value] = value;
544                         }
545                 }
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++) {
552                                                 int co = X + x;
553                                                 int li = Y + y;
554                                                 byte value = getValueAt(li, co);
555                                                 if (numbers[value] != 0) {
556                                                         isValid = false;
557                                                         System.out.println("GridModel.checkGridValidity() is not valid SQUARE");
558                                                         squareWithErrorX = Integer.valueOf(X);
559                                                         squareWithErrorY = Integer.valueOf(Y);
560                                                         break;
561                                                 }
562                                                 numbers[value] = value;
563                                         }
564                                 }
565                         }
566                 }
567                 return new GridValidity(isValid, lineWithError, columnWithError,
568                                 squareWithErrorX, squareWithErrorY);
569         }
570         
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)
575                                         return true;
576                         }
577                 }
578                 return false;
579         }
580         
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))
585                                         continue;
586                                 if ((cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0)
587                                         return true;
588                         }
589                 }
590                 return false;
591         }
592         
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)) {
597                                         continue;
598                                 } else {
599                                         return true;
600                                 }
601                         }
602                 }
603                 return false;
604         }
605
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];
610                 }
611                 return ints;
612         }
613         
614 }