OSDN Git Service

Updated po files. Removed some System.out.println logs.
[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                                         // System.out
186                                         // .println("GridModel.setMemosForAllCells() currentValuesMask:"
187                                         // + Integer.toHexString(currentValuesMask));
188                                         cellInfos[9 * li + co] &= ~currentValuesMask;
189                                 }
190                         }
191                 }
192
193                 // parcourir toutes les colonnes
194                 // - pour chaque colonne, cribler les memos
195                 // -- pour chaque cellule, cribler les memos
196                 for (int co = 0; co < 9; co++) { // each column
197                         short currentValuesMask = 0;
198                         for (int li = 0; li < 9; li++) {
199                                 if (isCellFilled(li, co)) {
200                                         byte value = getValueAt(li, co);
201                                         currentValuesMask |= getMemoFlag(value);
202                                 }
203                         }
204                         for (int li = 0; li < 9; li++) {
205                                 if (!isCellFilled(li, co)) {
206                                         System.out
207                                                         .println("GridModel.setMemosForAllCells() currentValuesMask:"
208                                                                         + Integer.toHexString(currentValuesMask));
209                                         cellInfos[9 * li + co] &= ~currentValuesMask;
210                                 }
211                         }
212                 }
213                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
214         }
215
216         public void setMemosForThisCell(int cellLi, int cellCo) {
217                 if (isCellFilled(cellLi, cellCo))
218                         return;
219                 cellInfos[9 * cellLi + cellCo] = MASK_CELL_MEMOS;
220
221                 
222                 // parcourir le carre courant
223                 // - pour chaque carre, cribler les memos
224                 // -- pour chaque cellule, cribler les memos
225                 //
226                 { // left pos of a square
227
228                         int X = 3 * (cellCo / 3);
229                         int Y = 3 * (cellLi / 3);
230                         System.out.println("GridModel.setMemosForThisCell() cellLi:"+cellLi+" cellCo:"+cellCo+" X:"+X+" Y:"+Y);
231                         short currentValuesMask = 0;
232                         for (int x = 0; x < 3; x++) {
233                                 for (int y = 0; y < 3; y++) {
234                                         int co = X + x;
235                                         int li = Y + y;
236                                         if (isCellFilled(li, co)) {
237                                                 byte value = getValueAt(li, co);
238                                                 currentValuesMask |= getMemoFlag(value);
239                                         }
240                                 }
241                         }
242                         cellInfos[9 * cellLi + cellCo] &= ~currentValuesMask;
243                 }
244
245                 // parcourir la ligne de cette cellule
246                 // - pour chaque ligne, cribler les memos
247                 // -- pour chaque cellule, cribler les memos
248                 //
249                 {
250                         short currentValuesMask = 0;
251                         for (int co = 0; co < 9; co++) {
252                                 if (isCellFilled(cellLi, co)) {
253                                         byte value = getValueAt(cellLi, co);
254                                         currentValuesMask |= getMemoFlag(value);
255                                 }
256                         }
257                         for (int co = 0; co < 9; co++) {
258                                 if (!isCellFilled(cellLi, co)) {
259                                         // System.out
260                                         // .println("GridModel.setMemosForAllCells() currentValuesMask:"
261                                         // + Integer.toHexString(currentValuesMask));
262                                         cellInfos[9 * cellLi + co] &= ~currentValuesMask;
263                                 }
264                         }
265                 }
266
267                 // parcourir la colonne de cette cellule
268                 // - pour chaque colonne, cribler les memos
269                 // -- pour chaque cellule, cribler les memos
270                 {
271                         short currentValuesMask = 0;
272                         for (int li = 0; li < 9; li++) {
273                                 if (isCellFilled(li, cellCo)) {
274                                         byte value = getValueAt(li, cellCo);
275                                         currentValuesMask |= getMemoFlag(value);
276                                 }
277                         }
278                         for (int li = 0; li < 9; li++) {
279                                 if (!isCellFilled(li, cellCo)) {
280                                         System.out
281                                                         .println("GridModel.setMemosForAllCells() currentValuesMask:"
282                                                                         + Integer.toHexString(currentValuesMask));
283                                         cellInfos[9 * li + cellCo] &= ~currentValuesMask;
284                                 }
285                         }
286                 }
287                 fireGridChanged(new GridChangedEvent(this, cellLi, cellCo, cellInfos[9
288                                 * cellLi + cellCo]));
289         }
290         
291         public void setCellMemos(int li, int co, byte[] values) {
292                 for (int i = 0; i < values.length; i++) {
293                         setCellMemo(li, co, values[i]);
294                 }
295                 fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
296                                 + co]));
297         }
298
299         private short getMemoFlag(byte value) {
300                 switch (value) {
301                 case 1:
302                         return FLAG_CELL_MEMO_1;
303                 case 2:
304                         return FLAG_CELL_MEMO_2;
305                 case 3:
306                         return FLAG_CELL_MEMO_3;
307                 case 4:
308                         return FLAG_CELL_MEMO_4;
309                 case 5:
310                         return FLAG_CELL_MEMO_5;
311                 case 6:
312                         return FLAG_CELL_MEMO_6;
313                 case 7:
314                         return FLAG_CELL_MEMO_7;
315                 case 8:
316                         return FLAG_CELL_MEMO_8;
317                 case 9:
318                         return FLAG_CELL_MEMO_9;
319                 default:
320                         throw new IllegalArgumentException("GridModel.getMemoFlag() value "
321                                         + value + " is illegal");
322                 }
323         }
324
325         public byte getNbOfPossibleValues(int li, int co) {
326                 short infos = cellInfos[9 * li + co];
327                 byte nb = 0;
328                 for (byte i = 1; i <= 9; i++) {
329                         nb += ((infos & getMemoFlag(i)) != 0) ? 1 : 0;
330                 }
331                 return nb;
332         }
333
334         private void setCellMemo(int li, int co, byte value) {
335                 cellInfos[9 * li + co] |= getMemoFlag(value);
336         }
337
338         public void clearCellMemos(int li, int co) {
339                 cellInfos[9 * li + co] &= ~MASK_CELL_MEMOS;
340         }
341
342         public boolean isCellValueSet(int li, int co, Byte value) {
343                 return (cellInfos[9 * li + co] & value.byteValue()) != 0;
344         }
345
346         public boolean isCellFilled(int li, int co) {
347                 return (cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0;
348         }
349
350         /**
351          * For use for initial values in the grid, and when doing a custom grid, for
352          * instance...
353          * 
354          * @param li
355          * @param co
356          */
357         public void setCellReadOnly(int li, int co) {
358                 cellInfos[9 * li + co] |= FLAG_CELL_READ_ONLY;
359         }
360
361         public void setCellValue(int li, int co, int value, boolean silent) {
362                 cellInfos[9 * li + co] &= ~MASK_CELL_VALUES;
363                 cellInfos[9 * li + co] |= value;
364                 clearCellMemos(li, co);
365                 
366                 if (silent == false) {
367                         fireGridChanged(new GridChangedEvent(this, li, co, cellInfos[9 * li
368                                 + co]));
369                 }
370                 
371                 if (!silent && isGridFull()) {
372                         GridValidity validity = checkGridValidity();
373                         if (validity.isGridValid()) {
374                                 setGridComplete();
375                         }
376                         fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
377                 }
378         }
379
380         public void fireGridChanged(GridChangedEvent event) {
381
382                 for (GridListener listener : listeners) {
383                         listener.gridChanged(event);
384                 }
385         }
386
387         public boolean isCellMemoSet(int li, int co, byte value) {
388                 return (cellInfos[9 * li + co] & getMemoFlag(value)) != 0;
389         }
390
391         public boolean isCellReadOnly(int li, int co) {
392                 return !isCustomGridModeON && (cellInfos[9 * li + co] & FLAG_CELL_READ_ONLY) != 0;
393         }
394
395         public boolean isGridFull() {
396                 for (int li=0; li<9; li++) {
397                         for (int co=0; co<9; co++) {
398                                 if (!isCellFilled(li, co)) {
399                                         return false;
400                                 }
401                         }
402                 }
403                 return true;
404         }
405         
406         public boolean isGridComplete() {
407                 return (cellInfos[0] & FLAG_GRID_COMPLETE) != 0;
408         }
409
410         public void setGridResolved() {
411                 cellInfos[0] |= FLAG_GRID_COMPLETE;
412                 fireGridResolved();
413         }
414         
415         private void fireGridResolved() {
416                 for (GridListener listener : listeners) {
417                         listener.gridResolved();
418                 }
419         }
420         
421         public void setGridComplete() {
422                 cellInfos[0] |= FLAG_GRID_COMPLETE;
423                 fireGridComplete();
424         }
425
426         private void fireGridComplete() {
427                 for (GridListener listener : listeners) {
428                         listener.gridComplete();
429                 }
430         }
431         
432         public byte getValueAt(int li, int co) {
433                 byte value = (byte) (cellInfos[9 * li + co] & MASK_CELL_VALUES);
434                 return value;
435         }
436
437         public int[] cloneCellInfosAsInts() {
438                 int[] ints = new int[cellInfos.length];
439                 for (int i=0; i<cellInfos.length; i++) {
440                         ints[i] = cellInfos[i];
441 //                      System.out.println("GridModel.cloneCellInfosAsInts() cellInfos["+i+"]:"+cellInfos[i]);
442 //                      System.out.println("GridModel.cloneCellInfosAsInts() ints["+i+"]:"+ints[i]);
443                 }
444                 return ints;
445         }
446
447         public void requestNewGrid() {
448                 newGrid();
449         }
450
451         public void clearAllUserMoves() {
452                 for (int li = 0; li < 9; li++) {
453                         for (int co = 0; co < 9; co++) {
454                                 if (!isCellReadOnly(li, co)) {
455                                         cellInfos[9 * li + co] &= ~MASK_CELL_VALUES;
456                                 }
457                         }
458                 }
459                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
460         }
461
462         public void clearAllUserMemos() {
463                 for (int li = 0; li < 9; li++) {
464                         for (int co = 0; co < 9; co++) {
465                                 clearCellMemos(li, co);
466                         }
467                 }
468                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
469         }
470         
471         public void enterCustomGridMode() {
472                 isCustomGridModeON = true;
473                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
474         }
475         
476         public GridValidity getGridValidity() {
477                 if (!isCustomGridModeON && !isGridFull()) {
478                         return GridValidity.VALID;
479                 }
480                 return checkGridValidity();
481         }
482         
483         public void exitCustomGridMode() {
484                 isCustomGridModeON = false;
485                 for (int li = 0; li < 9; li++) {
486                         for (int co = 0; co < 9; co++) {
487                                 if (isCellFilled(li, co)) {
488                                         setCellReadOnly(li, co);
489                                 } else {
490                                         cellInfos[9 * li + co] &= ~FLAG_CELL_READ_ONLY;
491                                 }
492                         }
493                 }
494                 fireGridChanged(new GridChangedEvent(this, 0, 0, (short)0));
495         }
496         
497         public boolean getCustomGridMode() {
498                 return isCustomGridModeON;
499         }
500         
501         public static class GridValidity {
502                 public static final GridValidity VALID = new GridValidity(true, null, null, null, null);
503                 
504                 private boolean isValid = true;
505                 private Integer firstErrorLine = null;
506                 private Integer firstErrorColumn = null;
507                 private Integer firstErrorSquareX = null;
508                 private Integer firstErrorSquareY = null;
509                 
510                 private GridValidity(boolean isValid, Integer lineWithError, Integer columnWithError,
511                                 Integer squareWithErrorX, Integer squareWithErrorY) {
512                         this.isValid = isValid;
513                         this.firstErrorLine = lineWithError;
514                         this.firstErrorColumn = columnWithError;
515                         this.firstErrorSquareX = squareWithErrorX;
516                         this.firstErrorSquareY = squareWithErrorY;
517                 }
518                 
519                 private static GridValidity valueOf(boolean isValid, Integer lineWithError, Integer columnWithError,
520                                 Integer squareWithErrorX, Integer squareWithErrorY) {
521                         if (isValid) {
522                                 return VALID;
523                         } else {
524                                 return new GridValidity(isValid, lineWithError, columnWithError,
525                                                 squareWithErrorX, squareWithErrorY);
526                         }
527                         
528                 }
529                 
530                 public boolean isGridValid() {
531                         return isValid;
532                 }
533                                 
534                 public Integer getFirstErrorLine() {
535                         return firstErrorLine;
536                 }
537                 
538                 public Integer getFirstErrorColumn() {
539                         return firstErrorColumn;
540                 }
541                 
542                 public Integer getFirstErrorSquareX() {
543                         return firstErrorSquareX;
544                 }
545                 
546                 public Integer getFirstErrorSquareY() {
547                         return firstErrorSquareY;
548                 }
549                 
550         }
551
552         private GridValidity checkGridValidity() {
553                 boolean isValid = true;
554                 Integer lineWithError = null;
555                 Integer columnWithError = null;
556                 Integer squareWithErrorX = null;
557                 Integer squareWithErrorY = null;
558                 
559                 // Check validity of all lines
560                 for (int li = 0; isValid && li < 9; li++) {
561                         byte[] numbers = new byte[10];
562                         for (int co = 0; co < 9; co++) {
563                                 byte value = getValueAt(li, co);
564                                 if (numbers[value] != 0) {
565                                         isValid = false;
566                                         lineWithError = Integer.valueOf(li);
567                                         break;
568                                 }
569                                 numbers[value] = value;
570                         }
571                 }
572                 // Check validity of all columns
573                 for (int co = 0; isValid && co < 9; co++) {
574                         byte[] numbers = new byte[10];
575                         for (int li = 0; li < 9; li++) {
576                                 byte value = getValueAt(li, co);
577                                 if (numbers[value] != 0) {
578                                         isValid = false;
579                                         columnWithError = Integer.valueOf(co);
580                                         break;
581                                 }
582                                 numbers[value] = value;
583                         }
584                 }
585                 // Check validity of all squares
586                 for (int X = 0; isValid && X < 9; X += 3) { // left pos of a square
587                         for (int Y = 0; isValid && Y < 9; Y += 3) { // top pos of a square
588                                 int[] numbers = new int[10];
589                                 for (int x = 0; isValid && x < 3; x++) {
590                                         for (int y = 0; isValid && y < 3; y++) {
591                                                 int co = X + x;
592                                                 int li = Y + y;
593                                                 byte value = getValueAt(li, co);
594                                                 if (numbers[value] != 0) {
595                                                         isValid = false;
596                                                         squareWithErrorX = Integer.valueOf(X);
597                                                         squareWithErrorY = Integer.valueOf(Y);
598                                                         break;
599                                                 }
600                                                 numbers[value] = value;
601                                         }
602                                 }
603                         }
604                 }
605                 return GridValidity.valueOf(isValid, lineWithError, columnWithError,
606                                 squareWithErrorX, squareWithErrorY);
607         }
608         
609         public boolean areSomeMemosSet() {
610                 for (int li = 0; li < 9; li++) {
611                         for (int co = 0; co < 9; co++) {
612                                 if ((cellInfos[9 * li + co] & MASK_CELL_MEMOS) != 0)
613                                         return true;
614                         }
615                 }
616                 return false;
617         }
618         
619         public boolean areSomeCellsFilled() {
620                 for (int li = 0; li < 9; li++) {
621                         for (int co = 0; co < 9; co++) {
622                                 if (isCellReadOnly(li, co))
623                                         continue;
624                                 if ((cellInfos[9 * li + co] & MASK_CELL_VALUES) != 0)
625                                         return true;
626                         }
627                 }
628                 return false;
629         }
630         
631         public boolean areSomeCellsEmpty() {
632                 for (int li = 0; li < 9; li++) {
633                         for (int co = 0; co < 9; co++) {
634                                 if (isCellFilled(li, co)) {
635                                         continue;
636                                 } else {
637                                         return true;
638                                 }
639                         }
640                 }
641                 return false;
642         }
643
644         public int[] asIntArray() {
645                 int[] ints = new int[cellInfos.length];
646                 for (int i=0; i<cellInfos.length; i++) {
647                         ints[i] = cellInfos[i];
648                 }
649                 return ints;
650         }
651         
652 }