View Javadoc
1   package io.github.skenvy;
2   
3   import java.util.ArrayList;
4   import java.util.Arrays;
5   
6   public class Sudoku {
7   
8     /**
9      * The individual cell width/length of a single inner grid. For a normal
10     * sudoku board, this is 3. A grid/row/column has "boardSize^2" many cells,
11     * and the overall board has "(boardSize^2)^2" ~ "boardSize^4" many cells.
12     */
13    private final int boardSize;
14  
15    /**
16     * The known and determined values that populate the grid. Is initialised
17     * with the already known cell values, and is then populated with those
18     * determined through solving.
19     */
20    public int[][][][] board;
21  
22    /**
23     * Whether a cell is filled or not.
24     */
25    public boolean[][][][] cellFilled;
26  
27    //Stores what numbers can go in what squares.
28    boolean[][][][][] placeCanContain;
29  
30    //whether a small grid contains a number
31    boolean[/*#*/][][] numberPresentG;
32  
33    //whether a row/column contains a number
34    boolean[/*R|C*/][/*#*/][/*RC-#*/] numberPresentRowCol;
35  
36    //Records if a square that is entered is in error.
37    public boolean[][][][] errorInSquare;
38  
39    //
40    ArrayList<String> digest = new ArrayList<String>();
41  
42    //
43    ArrayList<String> operations = new ArrayList<String>();
44  
45    //Default is to make the board with the standard 3*3*3*3
46    public Sudoku() {
47      this.boardSize = 3;
48      initialiseBoardVariables();
49    }
50  
51    //Specify some size k to make a k*k*k*k size board!
52    public Sudoku(int size) {
53      this.boardSize = size;
54      initialiseBoardVariables();
55    }
56  
57    //You set up a new instance using an already setup board
58    public Sudoku(int[][][][] boardIn) throws InvlalidSudokuCongiguration {
59      this.boardSize = boardIn.length;
60      if (boardIn.length == boardIn[0].length & boardIn.length == boardIn[0][0].length & boardIn.length == boardIn[0][0][0].length) {
61        initialiseBoardVariables();
62        this.board = boardIn;
63      } else {
64        throw new InvlalidSudokuCongiguration("Invalid board lengths in incoming array, not a proper matrix of grids, each a matrix of cells");
65      }
66    }
67  
68    /**
69     * Used for sudoku related exceptions.
70     */
71    public final class InvlalidSudokuCongiguration extends Exception {
72  
73      public InvlalidSudokuCongiguration(String string) {
74        super(string);
75      }
76  
77    }
78  
79    //Used after assigning the boardSize to appropriately size all member variables.
80    private void initialiseBoardVariables() {
81      board = new int[boardSize][boardSize][boardSize][boardSize];
82      cellFilled = new boolean[boardSize][boardSize][boardSize][boardSize];
83      placeCanContain = new boolean[boardSize][boardSize][boardSize][boardSize][(boardSize * boardSize)];
84      numberPresentG = new boolean[(boardSize * boardSize)][boardSize][boardSize];
85      numberPresentRowCol = new boolean[2][(boardSize * boardSize)][(boardSize * boardSize)];
86      errorInSquare = new boolean[boardSize][boardSize][boardSize][boardSize];
87    }
88  
89    public static void main(String[] args) {
90      final Sudoku puzzle = new Sudoku(2);
91      String[] boardContents = new String[4];
92      //Zero Refill "S_0_0_0_0_0_0_0_0_0_E"
93      ////XY                 000102101112202122
94      //boardContents[0] = "S_0_0_0_0_5_0_6_0_0_E"; /*0,X,0,Y*/
95      //boardContents[1] = "S_9_0_0_0_0_0_4_5_0_E"; /*0,X,1,Y*/
96      //boardContents[2] = "S_0_2_7_1_0_0_0_0_0_E"; /*0,X,2,Y*/
97      //boardContents[3] = "S_2_9_0_0_0_0_0_0_0_E"; /*1,X,0,Y*/
98      //boardContents[4] = "S_1_0_3_0_8_0_2_0_5_E"; /*1,X,1,Y*/
99      //boardContents[5] = "S_0_0_0_0_0_0_0_4_6_E"; /*1,X,2,Y*/
100     //boardContents[6] = "S_0_0_0_0_0_6_7_3_0_E"; /*2,X,0,Y*/
101     //boardContents[7] = "S_0_5_9_0_0_0_0_0_4_E"; /*2,X,1,Y*/
102     //boardContents[8] = "S_0_0_1_0_2_0_0_0_0_E"; /*2,X,2,Y*/
103     //XY                 000102101112202122
104     //boardContents[0] = "S_0_8_0_0_0_0_0_5_2_E"; /*0,X,0,Y*/
105     //boardContents[1] = "S_0_0_0_4_0_0_3_0_0_E"; /*0,X,1,Y*/
106     //boardContents[2] = "S_0_0_3_2_5_0_0_0_0_E"; /*0,X,2,Y*/
107     //boardContents[3] = "S_8_0_0_6_0_0_0_0_3_E"; /*1,X,0,Y*/
108     //boardContents[4] = "S_0_0_7_8_0_9_4_0_0_E"; /*1,X,1,Y*/
109     //boardContents[5] = "S_9_0_0_0_0_1_0_0_5_E"; /*1,X,2,Y*/
110     //boardContents[6] = "S_0_0_0_0_3_2_9_0_0_E"; /*2,X,0,Y*/
111     //boardContents[7] = "S_0_0_1_0_0_8_0_0_0_E"; /*2,X,1,Y*/
112     //boardContents[8] = "S_6_2_0_0_0_0_0_3_0_E"; /*2,X,2,Y*/
113     //XY                 000102101112202122
114     //boardContents[0] = "S_0_0_0_0_3_0_0_5_9_E"; /*0,X,0,Y*/
115     //boardContents[1] = "S_0_0_0_0_0_5_6_8_0_E"; /*0,X,1,Y*/
116     //boardContents[2] = "S_0_0_0_0_0_1_0_2_0_E"; /*0,X,2,Y*/
117     //boardContents[3] = "S_0_5_0_0_7_8_0_9_0_E"; /*1,X,0,Y*/
118     //boardContents[4] = "S_3_0_0_0_0_0_0_0_6_E"; /*1,X,1,Y*/
119     //boardContents[5] = "S_0_2_0_5_6_0_0_4_0_E"; /*1,X,2,Y*/
120     //boardContents[6] = "S_0_6_0_2_0_0_0_0_0_E"; /*2,X,0,Y*/
121     //boardContents[7] = "S_0_9_8_6_0_0_0_0_0_E"; /*2,X,1,Y*/
122     //boardContents[8] = "S_4_7_0_0_8_0_0_0_0_E"; /*2,X,2,Y*/
123 
124     boardContents[0] = "S_1_2_3_4_E"; /*0,X,0,Y*/
125     boardContents[1] = "S_4_3_2_1_E"; /*0,X,1,Y*/
126     boardContents[2] = "S_2_0_0_0_E"; /*0,X,2,Y*/
127     boardContents[3] = "S_0_1_0_0_E"; /*1,X,0,Y*/
128     puzzle.readBoardIn(boardContents);
129     puzzle.initialiseBoard();
130     puzzle.solveBoard();
131     puzzle.printBoardOut();
132     System.out.println("done");
133     puzzle.printDigest();
134     puzzle.printOperations();
135   }
136 
137   //Read in the stringy form of the board; i.e.
138   //    boardContents[0] = "S_1_2_3_4_E";
139   //    boardContents[1] = "S_4_3_2_1_E";
140   //    boardContents[2] = "S_2_0_0_0_E";
141   //    boardContents[3] = "S_0_1_0_0_E";
142   public void readBoardIn(String[] boardRows) {
143     int boardRow = 0;
144     int boardColumn = 0;
145     int gridRow = 0;
146     int gridColumn = 0;
147     int previousIndex = 0;
148     //Iterate the rows
149     for (int j0 = 0; j0 < (boardSize * boardSize); j0++) {
150       // i.e. "0 then 1 then 2 then 0 then 1 then 2 then 0 then 1 then 2"
151       gridRow = j0 % boardSize;
152       // i.e. "0 then 0 then 0 then 1 then 1 then 1 then 2 then 2 then 2" < thanks to java always rounding down.
153       boardRow = (j0 - gridRow) / boardSize;
154       //Keep track of index where we found the last delimiter
155       previousIndex = 0;
156       //Iterate the columns
157       for (int j1 = 0; j1 < (boardSize * boardSize); j1++) {
158         // i.e. "0 then 1 then 2 then 0 then 1 then 2 then 0 then 1 then 2"
159         gridColumn = j1 % boardSize;
160         // i.e. "0 then 0 then 0 then 1 then 1 then 1 then 2 then 2 then 2" < thanks to java always rounding down.
161         boardColumn = (j1 - gridColumn) / boardSize;
162         //Find the next value between two instances of the delimiter
163         int stringIntStartIndex = boardRows[j0].indexOf('_', previousIndex) + 1;
164         int stringIntEndIndex   = boardRows[j0].indexOf('_', stringIntStartIndex);
165         String stringInt = boardRows[j0].substring(stringIntStartIndex, stringIntEndIndex);
166         int parsedInt = Integer.parseInt(stringInt);
167         //Assign the value to the location on the board and record a message
168         board[boardRow][boardColumn][gridRow][gridColumn] = parsedInt;
169         previousIndex = stringIntEndIndex;
170         if (parsedInt != 0) {
171           operations.add("Read Board In: Place " + boardRow + ", " + boardColumn + ", " + gridRow + ", " + gridColumn + " contains " + stringInt);
172         }
173       }
174     }
175   }
176 
177   //The corollary to "readBoardIn"
178   public void printBoardOut() {
179     int boardRow = 0;
180     int boardColumn = 0;
181     int gridRow = 0;
182     int gridColumn = 0;
183     String mess;
184     for (int j0 = 0; j0 < (boardSize * boardSize); j0++) {
185       mess = "Row " + (j0) + ": S_";
186       gridRow = j0 % boardSize;
187       boardRow = (j0 - gridRow) / boardSize;
188       for (int j1 = 0; j1 < (boardSize * boardSize); j1++) {
189         gridColumn = j1 % boardSize;
190         boardColumn = (j1 - gridColumn) / boardSize;
191         mess = mess.concat("" + board[boardRow][boardColumn][gridRow][gridColumn] + "_");
192       }
193       mess = mess.concat("E");
194       textFieldAdd(mess);
195     }
196   }
197 
198   //Checks board is valid and generates list of possibilities
199   public boolean initialiseBoard() { 
200     digest.add("Digest Start");
201     operations.add("Operations Start");
202     if (!boardCheckValid()) {
203       textFieldAdd("Board Check: Failure");
204       return false;
205     }
206     textFieldAdd("Board Check: Pass");
207     initialisePlaceCanContain(false);
208     return true;
209   }
210 
211   public void initialisePlaceFilled() {
212     for (int k0 = 0; k0 < boardSize; k0++) { //grid row iterate
213       for (int k1 = 0; k1 < boardSize; k1++) { //grid column iterate
214         for (int k2 = 0; k2 < boardSize; k2++) { //small grid row iterate
215           for (int k3 = 0; k3 < boardSize; k3++) { //small grid column iterate
216             if ((board[k0][k1][k2][k3] >= 1) && (board[k0][k1][k2][k3] <= (boardSize * boardSize))) {
217               cellFilled[k0][k1][k2][k3] = true;
218               operations.add("Place " + k0 + ", " + k1 + ", " + k2 + ", " + k3 + " filled with " + board[k0][k1][k2][k3]);
219             } else {
220               operations.add("Place " + k0 + ", " + k1 + ", " + k2 + ", " + k3 + " is empty");
221               cellFilled[k0][k1][k2][k3] = false;
222             }
223           }
224         }
225       }
226     }
227   }
228 
229   public void initialisePlaceCanContain(boolean clear) {
230     if (clear) {
231       for (int k0 = 0; k0 < boardSize; k0++) {
232         for (int k1 = 0; k1 < boardSize; k1++) {
233           for (int k2 = 0; k2 < boardSize; k2++) {
234             for (int k3 = 0; k3 < boardSize; k3++) {
235               for (int k4 = 0; k4 < (boardSize * boardSize); k4++) {
236                 placeCanContain[k0][k1][k2][k3][k4] = true;
237               }
238             }
239           }
240         }
241       }
242     } else {
243       initialisePlaceCanContain(true);
244       initialiseNumberPresentGridRowCol(false);
245       updatePlaceCanContain();
246     }
247   }
248 
249   public void updatePlaceCanContain() {
250     for (int k0 = 0; k0 < boardSize; k0++) {
251       for (int k1 = 0; k1 < boardSize; k1++) {
252         for (int k2 = 0; k2 < boardSize; k2++) {
253           for (int k3 = 0; k3 < boardSize; k3++) {
254             if (!cellFilled[k0][k1][k2][k3]) {
255               for (int k4 = 0; k4 < (boardSize * boardSize); k4++) {
256                 if (numberPresentRowCol[0][k4][k2 + k0 * board.length] || numberPresentRowCol[1][k4][k3 + k1 * board.length] || numberPresentG[k4][k0][k1]) {
257                   placeCanContain[k0][k1][k2][k3][k4] = false;
258                 }
259               }
260             }
261           }
262         }
263       }
264     }
265   }
266 
267   public void initialiseNumberPresentGridRowCol(boolean clear) {
268     if (clear) {
269       for (int k0 = 0; k0 < (boardSize * boardSize); k0++) {
270         for (int k1 = 0; k1 < boardSize; k1++) {
271           for (int k2 = 0; k2 < boardSize; k2++) {
272             numberPresentG[k0][k1][k2] = false;
273           }
274         }
275       }
276       for (int k0 = 0; k0 < 2; k0++) {
277         for (int k1 = 0; k1 < (boardSize * boardSize); k1++) {
278           for (int k2 = 0; k2 < (boardSize * boardSize); k2++) {
279             numberPresentRowCol[k0][k1][k2] = false;
280           }
281         }
282       }
283     } else {
284       updateNumberPresentGridRowCol();
285     }
286   }
287 
288   public void updateNumberPresentGridRowCol() {
289     for (int k0 = 0; k0 < boardSize; k0++) {
290       for (int k1 = 0; k1 < boardSize; k1++) {
291         for (int k2 = 0; k2 < boardSize; k2++) {
292           for (int k3 = 0; k3 < boardSize; k3++) {
293             if (cellFilled[k0][k1][k2][k3]) {
294               numberPresentRowCol[0][board[k0][k1][k2][k3] - 1][k2 + k0 * board.length] = true;
295               numberPresentRowCol[1][board[k0][k1][k2][k3] - 1][k3 + k1 * board.length] = true;
296               numberPresentG[board[k0][k1][k2][k3] - 1][k0][k1] = true;
297             }
298           }
299         }
300       }
301     }
302   }
303 
304   public boolean boardCheckValid() {
305     initialisePlaceFilled();
306     initialiseNumberPresentGridRowCol(true);
307     for (int k0 = 0; k0 < boardSize; k0++) { //grid row iterate
308       for (int k1 = 0; k1 < boardSize; k1++) { //grid column iterate
309         for (int k2 = 0; k2 < boardSize; k2++) { //small grid row iterate
310           for (int k3 = 0; k3 < boardSize; k3++) { //small grid column iterate
311             if (cellFilled[k0][k1][k2][k3]) {
312               if (numberPresentRowCol[0][board[k0][k1][k2][k3] - 1][k2 + k0 * boardSize]) {
313                 textFieldAdd("Error in row " + (k2 + k0 * boardSize));
314                 return false;
315               } else if (numberPresentRowCol[1][board[k0][k1][k2][k3] - 1][k3 + k1 * boardSize]) {
316                 textFieldAdd("Error in column " + (k3 + k1 * boardSize));
317                 return false;
318               } else if (numberPresentG[board[k0][k1][k2][k3] - 1][k0][k1]) {
319                 textFieldAdd("Error in grid (R,C): (" + k0 + ", " + k1 + ")");
320                 return false;
321               } else {
322                 numberPresentRowCol[0][board[k0][k1][k2][k3] - 1][k2 + k0 * boardSize] = true;
323                 numberPresentRowCol[1][board[k0][k1][k2][k3] - 1][k3 + k1 * boardSize] = true;
324                 numberPresentG[board[k0][k1][k2][k3] - 1][k0][k1] = true;
325               }
326             }
327           }
328         }
329       }
330     }
331     return true;
332   }
333 
334   public void boardCheckErrors() {
335     int[/*GridRowCol*/][/*#-GridRowCol*/][/*#*/] appearsTimes = new int[3][(boardSize * boardSize)][(boardSize * boardSize)];
336     for (int j0 = 0; j0 < 3; j0++) {
337       for (int j1 = 0; j1 < (boardSize * boardSize); j1++) {
338         for (int j2 = 0; j2 < (boardSize * boardSize); j2++) {
339           appearsTimes[j0][j1][j2] = 0;
340         }
341       }
342     }
343     for (int j0 = 0; j0 < boardSize; j0++) {
344       for (int j1 = 0; j1 < boardSize; j1++) {
345         for (int k0 = 0; k0 < boardSize; k0++) {
346           for (int k1 = 0; k1 < boardSize; k1++) {
347             if (cellFilled[j0][j1][k0][k1]) {
348               if (0 < board[j0][j1][k0][k1] && board[j0][j1][k0][k1] <= (boardSize * boardSize)) {
349                 appearsTimes[0][j0 * boardSize + j1][board[j0][j1][k0][k1] - 1]++;
350                 appearsTimes[1][j0 * boardSize + k0][board[j0][j1][k0][k1] - 1]++;
351                 appearsTimes[2][j1 * boardSize + k1][board[j0][j1][k0][k1] - 1]++;
352               }
353             }
354           }
355         }
356       }
357     }
358     for (int j0 = 0; j0 < boardSize; j0++) {
359       for (int j1 = 0; j1 < boardSize; j1++) {
360         for (int k0 = 0; k0 < boardSize; k0++) {
361           for (int k1 = 0; k1 < boardSize; k1++) {
362             if (0 < board[j0][j1][k0][k1] && board[j0][j1][k0][k1] <= (boardSize * boardSize)) {
363               if (appearsTimes[0][j0 * 3 + j1][board[j0][j1][k0][k1] - 1] > 1 || appearsTimes[1][j0 * 3 + k0][board[j0][j1][k0][k1] - 1] > 1 || appearsTimes[2][j1 * 3 + k1][board[j0][j1][k0][k1] - 1] > 1) {
364                 errorInSquare[j0][j1][k0][k1] = true;
365               } else {
366                 errorInSquare[j0][j1][k0][k1] = false;
367               }
368             }
369           }
370         }
371       }
372     }
373   }
374 
375   public boolean errorExists() {
376     boardCheckErrors();
377     boolean check = false;
378     for (int k0 = 0; k0 < boardSize; k0++) { //grid row iterate
379       for (int k1 = 0; k1 < boardSize; k1++) { //grid column iterate
380         for (int k2 = 0; k2 < boardSize; k2++) { //small grid row iterate
381           for (int k3 = 0; k3 < boardSize; k3++) { //small grid column iterate
382             if (errorInSquare[k0][k1][k2][k3]) {
383               check = true;
384             }
385           }
386         }
387       }
388     }
389     return check;
390   }
391 
392   public boolean boardCheckComplete() {
393     for (int k0 = 0; k0 < boardSize; k0++) { //grid row iterate
394       for (int k1 = 0; k1 < boardSize; k1++) { //grid column iterate
395         for (int k2 = 0; k2 < boardSize; k2++) { //small grid row iterate
396           for (int k3 = 0; k3 < boardSize; k3++) { //small grid column iterate
397             if (!cellFilled[k0][k1][k2][k3]) {
398               return false;
399             } 
400           }
401         }
402       }
403     }
404     return true;
405   }
406 
407   public int boardCheckPlacesFilled() {
408     int count = 0;
409     for (int k0 = 0; k0 < boardSize; k0++) { //grid row iterate
410       for (int k1 = 0; k1 < boardSize; k1++) { //grid column iterate
411         for (int k2 = 0; k2 < boardSize; k2++) { //small grid row iterate
412           for (int k3 = 0; k3 < boardSize; k3++) { //small grid column iterate
413             if (cellFilled[k0][k1][k2][k3]) {
414               count++;
415             } 
416           }
417         }
418       }
419     }
420     return count;
421   }
422 
423   public void printDigest() {
424     for (int k = 0; k < digest.size(); k++) {
425       System.out.println(digest.get(k));
426     }
427   }
428 
429   public String[] digestAsArray() {
430     String[] out = new String[digest.size()];
431     for (int k = 0; k < digest.size(); k++) {
432       out[k] = digest.get(k);
433     }
434     return out;
435   }
436 
437   public void printOperations() {
438     for (int k = 0; k < operations.size(); k++) {
439       System.out.println(operations.get(k));
440     }
441   }
442 
443   public String[] operationsAsArray() {
444     String[] out = new String[operations.size()];
445     for (int k = 0; k < operations.size(); k++) {
446       out[k] = operations.get(k);
447     }
448     return out;
449   }
450 
451   public void textFieldAdd(String a) {
452     digest.add(a);
453     operations.add(a);
454   }
455 
456   public void solveBoard() {
457     if (!solveBoardEasy()) {
458       if (!solveBoardModerate()) {
459         printBoardOut();
460         if (!solveBoardHard()) {
461           textFieldAdd("Couldn't solve this board with existing techniques.");
462         }
463       }
464     }
465   }
466 
467   public boolean solveBoardEasy() {
468     if (boardCheckComplete()) {
469       textFieldAdd("Board already solved");
470       return true;
471     }
472     int unsolvedSquares = (boardSize * boardSize) * (boardSize * boardSize) - boardCheckPlacesFilled();
473     int countSoleCandidates = 0;
474     int countUniqueCandidates = 0;
475     for (int q = 0; q < unsolvedSquares; q++) {
476       countSoleCandidates += iterateSoleCandidate();
477       if (countSoleCandidates == unsolvedSquares) {
478         break;
479       } else {
480         unsolvedSquares -= countSoleCandidates;
481       }
482       countUniqueCandidates += iterateUniqueCandidate();
483       if (countUniqueCandidates == unsolvedSquares) {
484         break;
485       } else {
486         unsolvedSquares -= countUniqueCandidates;
487       }
488       if ((countSoleCandidates == 0) && (countUniqueCandidates == 0)) {
489         break;
490       }
491     }
492     if (boardCheckComplete()) {
493       textFieldAdd("Board was easy!");
494       textFieldAdd("Board solved in " + countSoleCandidates + " solo candidate steps");
495       textFieldAdd("Board solved in " + countUniqueCandidates + " unique candidate steps");
496       return true;
497     }
498     textFieldAdd("Board was not easy!");
499     textFieldAdd("Board Unsolved; Performed " + countSoleCandidates + " solo candidate steps");
500     textFieldAdd("Board Unsolved; Performed " + countUniqueCandidates + " unique candidate steps");
501     return false;
502   }
503 
504   public boolean solveBoardModerate() {
505     if (boardCheckComplete()) {
506       return true;
507     }
508     int unsolvedSquares = (boardSize * boardSize) * (boardSize * boardSize) - boardCheckPlacesFilled();
509     int countSoleCandidates = 0;
510     int countUniqueCandidates = 0;
511     int countReduceRowColByGrid = 0;
512     int countReduceGridByRowCol = 0;
513     for (int q = 0; q < unsolvedSquares; q++) {
514       countSoleCandidates += iterateSoleCandidate();
515       if (countSoleCandidates == unsolvedSquares) {
516         break;
517       } else {
518         unsolvedSquares -= countSoleCandidates;
519       }
520       countUniqueCandidates += iterateUniqueCandidate();
521       if (countUniqueCandidates == unsolvedSquares) {
522         break;
523       } else {
524         unsolvedSquares -= countUniqueCandidates;
525       }
526       countReduceRowColByGrid += iterateReductionOfCanContainInRowColByGrid();
527       countReduceGridByRowCol += iterateReductionOfCanContainInGridByRowCol();
528     }
529     if (boardCheckComplete()) {
530       textFieldAdd("Board was moderate!");
531       textFieldAdd("Board solved with " + countReduceRowColByGrid + " row/column reductions by grid steps");
532       textFieldAdd("Board solved with " + countReduceGridByRowCol + " grid reductions by grid steps");
533       textFieldAdd("Board solved in a further " + countSoleCandidates + " solo candidate steps");
534       textFieldAdd("Board solved in a further " + countUniqueCandidates + " unique candidate steps");
535       return true;
536     }
537     textFieldAdd("Board was not moderate!");
538     textFieldAdd("Board Unsolved; Performed " + countReduceRowColByGrid + " row/column reductions by grid steps");
539     textFieldAdd("Board Unsolved; Performed " + countReduceGridByRowCol + " grid reductions by grid steps");
540     textFieldAdd("Board Unsolved; Performed a further " + countSoleCandidates + " solo candidate steps");
541     textFieldAdd("Board Unsolved; Performed a further " + countUniqueCandidates + " unique candidate steps");
542     return false;
543   }
544 
545   public boolean solveBoardHard() {
546     if (boardCheckComplete()) {
547       return true;
548     }
549     int unsolvedSquares = (boardSize * boardSize) * (boardSize * boardSize) - boardCheckPlacesFilled();
550     int countSoleCandidates = 0;
551     int countUniqueCandidates = 0;
552     int countReduceRowColByGrid = 0;
553     int countReduceGridByRowCol = 0;
554     int countReduceSubVis = 0;
555     int countReduceSubHid = 0;
556     for (int q = 0; q < unsolvedSquares; q++) {
557       countSoleCandidates += iterateSoleCandidate();
558       if (countSoleCandidates == unsolvedSquares) {
559         break;
560       } else {
561         unsolvedSquares -= countSoleCandidates;
562       }
563       countUniqueCandidates += iterateUniqueCandidate();
564       if (countUniqueCandidates == unsolvedSquares) {
565         break;
566       } else {
567         unsolvedSquares -= countUniqueCandidates;
568       }
569       countReduceRowColByGrid += iterateReductionOfCanContainInRowColByGrid();
570       countReduceGridByRowCol += iterateReductionOfCanContainInGridByRowCol();
571       countReduceSubVis += iterateReductionOfCanContainSubsetVisible();
572       countReduceSubHid += iterateReductionOfCanContainSubsetHidden();
573     }
574     if (boardCheckComplete()) {
575       textFieldAdd("Board was hard!");
576       textFieldAdd("Board solved with " + countReduceSubVis + " subset reductions by visible subsets steps");
577       textFieldAdd("Board solved with " + countReduceSubHid + " subset reductions by hidden subsets steps");
578       textFieldAdd("Board solved in a further " + countReduceRowColByGrid + " row/column reductions by grid steps");
579       textFieldAdd("Board solved in a further " + countReduceGridByRowCol + " grid reductions by grid steps");
580       textFieldAdd("Board solved in a further " + countSoleCandidates + " solo candidate steps");
581       textFieldAdd("Board solved in a further " + countUniqueCandidates + " unique candidate steps");
582       return true;
583     }
584     textFieldAdd("Board was not hard!");
585     textFieldAdd("Board Unsolved; Performed " + countReduceSubVis + " subset reductions by visible subsets steps");
586     textFieldAdd("Board Unsolved; Performed " + countReduceSubHid + " subset reductions by hidden subsets steps");
587     textFieldAdd("Board Unsolved; Performed a further " + countReduceRowColByGrid + " row/column reductions by grid steps");
588     textFieldAdd("Board Unsolved; Performed a further " + countReduceGridByRowCol + " grid reductions by grid steps");
589     textFieldAdd("Board Unsolved; Performed a further " + countSoleCandidates + " solo candidate steps");
590     textFieldAdd("Board Unsolved; Performed a further " + countUniqueCandidates + " unique candidate steps");
591     return false;
592   }
593 
594   public int iterateSoleCandidate() {
595     //System.out.println("Called: IterateSinglePossibility");
596     int count = 0;
597     while (checkSoleCandidate()) {
598       //System.out.println("Looped: IterateSinglePossibility");
599       count++;
600     }
601     return count;
602   }
603 
604   public boolean checkSoleCandidate() {
605     int candidates = 0;
606     int candidate = 0;
607     for (int k0 = 0; k0 < boardSize; k0++) {
608       for (int k1 = 0; k1 < boardSize; k1++) {
609         for (int k2 = 0; k2 < boardSize; k2++) {
610           for (int k3 = 0; k3 < boardSize; k3++) {
611             if (!cellFilled[k0][k1][k2][k3]) {
612               candidates = 0;
613               for (int k4 = 0; k4 < (boardSize * boardSize); k4++) {
614                 if (placeCanContain[k0][k1][k2][k3][k4]) {
615                   candidate = k4;
616                   candidates += 1;
617                 }
618               }
619               if (candidates == 1) {
620                 board[k0][k1][k2][k3] = candidate + 1;
621                 cellFilled[k0][k1][k2][k3] = true;
622                 deliminatePotential(k0, k1, k2, k3, candidate);
623                 return true;
624               }
625             }
626           }
627         }
628       }
629     }
630     return false;
631   }
632 
633   public int iterateUniqueCandidate() {
634     //System.out.println("Called: IterateSinglePossibility");
635     int count = 0;
636     while (checkUniqueCandidate()) {
637       //System.out.println("Looped: IterateSinglePossibility");
638       count++;
639     }
640     return count;
641   }
642 
643   public boolean checkUniqueCandidate() {
644     int gridRowColX = 0;
645     int gridRowColY = 0;
646     int grcX = 0;
647     int grcY = 0;
648     int candidates = 0;
649     int candR0 = 0;
650     int candC0 = 0;
651     int candR1 = 0;
652     int candC1 = 0;
653     for (int val = 0; val < (boardSize * boardSize); val++) {
654       for (int outerGridRowCol = 0; outerGridRowCol < (boardSize * boardSize); outerGridRowCol++) { //Which G,R,C
655         gridRowColY = outerGridRowCol % boardSize;
656         gridRowColX = (outerGridRowCol - gridRowColY) / boardSize;
657         //Grid Check
658         if (!numberPresentG[val][gridRowColX][gridRowColY]) {
659           candidates = 0;
660           candR0 = gridRowColX;
661           candC0 = gridRowColY;
662           candR1 = 0;
663           candC1 = 0;
664           for (int innerGridRowCol = 0; innerGridRowCol < (boardSize * boardSize); innerGridRowCol++) { //Check elements of G,R,C
665             grcY = innerGridRowCol % boardSize;
666             grcX = (innerGridRowCol - grcY) / boardSize;
667             if (!cellFilled[gridRowColX][gridRowColY][grcX][grcY] && placeCanContain[gridRowColX][gridRowColY][grcX][grcY][val]) {
668               candR1 = grcX;
669               candC1 = grcY;
670               candidates += 1;
671             }
672           }
673           if (candidates == 1) {
674             board[candR0][candC0][candR1][candC1] = val + 1;
675             cellFilled[candR0][candC0][candR1][candC1] = true;
676             deliminatePotential(candR0, candC0, candR1, candC1, val);
677             //System.out.println("grid returns " + val + " in " + candR0 + ", " + candC0 + ", " + candR1 + ", " + candC1);
678             return true;
679           }
680         }
681         //Row Check
682         if (!numberPresentRowCol[0][val][outerGridRowCol]) {
683           candidates = 0;
684           candR0 = gridRowColX;
685           candC0 = 0;
686           candR1 = gridRowColY;
687           candC1 = 0;
688           for (int innerGridRowCol = 0; innerGridRowCol < (boardSize * boardSize); innerGridRowCol++) { //Check elements of G,R,C
689             grcY = innerGridRowCol % boardSize;
690             grcX = (innerGridRowCol - grcY) / boardSize;
691             if (!cellFilled[gridRowColX][grcX][gridRowColY][grcY] && placeCanContain[gridRowColX][grcX][gridRowColY][grcY][val]) {
692               candC0 = grcX;
693               candC1 = grcY;
694               candidates += 1;
695             }
696           }
697           if (candidates == 1) {
698             board[candR0][candC0][candR1][candC1] = val + 1;
699             cellFilled[candR0][candC0][candR1][candC1] = true;
700             deliminatePotential(candR0, candC0, candR1, candC1, val);
701             //System.out.println("row returns");
702             return true;
703           }
704         }
705         //Column Check
706         if (!numberPresentRowCol[1][val][outerGridRowCol]) {
707           candidates = 0;
708           candR0 = 0;
709           candC0 = gridRowColX;
710           candR1 = 0;
711           candC1 = gridRowColY;
712           for (int innerGridRowCol = 0; innerGridRowCol < (boardSize * boardSize); innerGridRowCol++) { //Check elements of G,R,C
713             grcY = innerGridRowCol % boardSize;
714             grcX = (innerGridRowCol - grcY) / boardSize;
715             if (!cellFilled[grcX][gridRowColX][grcY][gridRowColY] && placeCanContain[grcX][gridRowColX][grcY][gridRowColY][val]) {
716               candR0 = grcX;
717               candR1 = grcY;
718               candidates += 1;
719             }
720           }
721           if (candidates == 1) {
722             board[candR0][candC0][candR1][candC1] = val + 1;
723             cellFilled[candR0][candC0][candR1][candC1] = true;
724             deliminatePotential(candR0, candC0, candR1, candC1, val);
725             //System.out.println("column returns");
726             return true;
727           }
728         }
729       }
730     }
731     return false;
732   }
733 
734   public int iterateReductionOfCanContainInRowColByGrid() {
735     int count = 0;
736     while (reductionOfCanContainInRowColByGrid()) {
737       count++;
738     }
739     return count;
740   }
741 
742   public boolean reductionOfCanContainInRowColByGrid() { //Block and column / Row Interaction
743     int rowVal = 0;
744     boolean[] rows = new boolean[boardSize];
745     int rowsVal = 0;
746     int colVal = 0;
747     boolean[] cols = new boolean[boardSize];
748     int colsVal = 0;
749     int deliminatedSquares = 0;
750     for (int jq = 0; jq < boardSize; jq++) {
751       rows[jq] = false;
752       cols[jq] = false;
753     }
754     for (int j0 = 0; j0 < boardSize; j0++) {
755       for (int j1 = 0; j1 < boardSize; j1++) {
756         for (int val = 0; val < (boardSize * boardSize); val++) {
757           if (!numberPresentG[val][j0][j1]) {
758             for (int jq = 0; jq < boardSize; jq++) {
759               rows[jq] = false;
760               cols[jq] = false;
761             } //System.out.println("Stuck");
762             for (int k0 = 0; k0 < boardSize; k0++) {
763               for (int k1 = 0; k1 < boardSize; k1++) {
764                 if (!cellFilled[j0][j1][k0][k1] && placeCanContain[j0][j1][k0][k1][val]) {
765                   rows[k0] = true;
766                   cols[k1] = true;
767                 }
768               }
769             }
770             rowsVal = 0;
771             colsVal = 0;
772             for (int jq = 0; jq < boardSize; jq++) {
773               if (rows[jq]) {
774                 rowsVal++;
775                 rowVal = jq;
776               }
777               if (cols[jq]) {
778                 colsVal++;
779                 colVal = jq;
780               }
781             }
782             if (rowsVal == 1) {
783               for (int jthC = 0; jthC < (boardSize * boardSize); jthC++) {
784                 int jthC1 = jthC % boardSize;
785                 int jthC0 = (jthC - jthC1) / boardSize;
786                 if (j1 != jthC0) {
787                   if (!cellFilled[j0][jthC0][rowVal][jthC1] && placeCanContain[j0][jthC0][rowVal][jthC1][val]) {
788                     deliminatedSquares++;
789                     placeCanContain[j0][jthC0][rowVal][jthC1][val] = false;
790                   }
791                 }
792               }
793               if (deliminatedSquares > 0) {
794                 return true;
795               }
796             }
797             if (colsVal == 1) {
798               for (int jthR = 0; jthR < (boardSize * boardSize); jthR++) {
799                 int jthR1 = jthR % boardSize;
800                 int jthR0 = (jthR - jthR1) / boardSize;
801                 if (j0 != jthR0) {
802                   if (!cellFilled[jthR0][j1][jthR1][colVal] && placeCanContain[jthR0][j1][jthR1][colVal][val]) {
803                     deliminatedSquares++;
804                     placeCanContain[jthR0][j1][jthR1][colVal][val] = false;
805                   }
806                 }
807               }
808               if (deliminatedSquares > 0) {
809                 return true;
810               }
811             }
812           }
813         }
814       }
815     }
816     return false;
817   }
818 
819   public int iterateReductionOfCanContainInGridByRowCol() {
820     int count = 0;
821     while (reductionOfCanContainInGridByRowCol()) {
822       count++;
823     }
824     return count;
825   }
826 
827   public boolean reductionOfCanContainInGridByRowCol() { //Block / Block Interaction
828     int deliminations = 0;
829     int candidates = 0;
830     for (int val = 0; val < (boardSize * boardSize); val++) {
831       for (int jthCheckRow = 0; jthCheckRow < (boardSize * boardSize); jthCheckRow++) {
832         if (!numberPresentRowCol[0][val][jthCheckRow]) {
833           int jthCheckRow1 = jthCheckRow % boardSize;
834           int jthCheckRow0 = (jthCheckRow - jthCheckRow1) / boardSize;
835           for (int jthIgnore = 0; jthIgnore < boardSize; jthIgnore++) {
836             candidates = 0;
837             for (int jthSelect = 0; jthSelect < boardSize; jthSelect++) {
838               if (jthSelect != jthIgnore) {
839                 for (int jthRotate = 0; jthRotate < boardSize; jthRotate++) {
840                   if (!cellFilled[jthCheckRow0][jthSelect][jthCheckRow1][jthRotate] && placeCanContain[jthCheckRow0][jthSelect][jthCheckRow1][jthRotate][val]) {
841                     candidates++;
842                   }
843                 }
844               }
845             }
846             if (candidates == 0) {
847               //In Grid (jthCheckRow0, jthIgnore)
848               for (int jthRotateR = 0; jthRotateR < boardSize; jthRotateR++) {
849                 for (int jthRotateC = 0; jthRotateC < boardSize; jthRotateC++) {
850                   if (jthRotateR != jthCheckRow1) {
851                     if (!cellFilled[jthCheckRow0][jthIgnore][jthRotateR][jthRotateC] && placeCanContain[jthCheckRow0][jthIgnore][jthRotateR][jthRotateC][val]) {
852                       placeCanContain[jthCheckRow0][jthIgnore][jthRotateR][jthRotateC][val] = false;
853                       deliminations++;
854                     }
855                   }
856                 }
857               }
858               if (deliminations > 0) {
859                 return true;
860               }
861             }
862           }
863         }
864       }
865       for (int jthCheckC = 0; jthCheckC < (boardSize * boardSize); jthCheckC++) {
866         if (!numberPresentRowCol[1][val][jthCheckC]) {
867           int jthCheckC1 = jthCheckC % boardSize;
868           int jthCheckC0 = (jthCheckC - jthCheckC1) / boardSize;
869           for (int jthIgnore = 0; jthIgnore < boardSize; jthIgnore++) {
870             candidates = 0;
871             for (int jthSelect = 0; jthSelect < boardSize; jthSelect++) {
872               if (jthSelect != jthIgnore) {
873                 for (int jthRotate = 0; jthRotate < boardSize; jthRotate++) {
874                   if (!cellFilled[jthSelect][jthCheckC0][jthRotate][jthCheckC1] && placeCanContain[jthSelect][jthCheckC0][jthRotate][jthCheckC1][val]) {
875                     candidates++;
876                   }
877                 }
878               }
879             }
880             if (candidates == 0) {
881               //In Grid (jthIgnore, jthCheckC0)
882               for (int jthRotateR = 0; jthRotateR < boardSize; jthRotateR++) {
883                 for (int jthRotateC = 0; jthRotateC < boardSize; jthRotateC++) {
884                   if (jthRotateC != jthCheckC1) {
885                     if (!cellFilled[jthIgnore][jthCheckC0][jthRotateR][jthRotateC] && placeCanContain[jthIgnore][jthCheckC0][jthRotateR][jthRotateC][val]) {
886                       placeCanContain[jthIgnore][jthCheckC0][jthRotateR][jthRotateC][val] = false;
887                       deliminations++;
888                     }
889                   }
890                 }
891               }
892               if (deliminations > 0) {
893                 return true;
894               }
895             }
896           }
897         }
898       }
899     }
900     return false;
901   }
902 
903   public int iterateReductionOfCanContainSubsetVisible() {
904     int count = 0;
905     while (reductionOfCanContainSubsetVisible()) {
906       count++;
907     }
908     return count;
909   }
910 
911 
912   public boolean reductionOfCanContainSubsetVisible() {
913     int minimumSubsetPresent = 0;
914     int numberOfTrueSubsets = 0;
915     int deliminations = 0;
916     int numberOfVisibleSubsets = 0;
917     int visibleSubsetIndex = 0;
918     int s00 = 0;
919     int s01 = 0;
920     int s10 = 0;
921     int s11 = 0;
922     for (int subsetSize = 2; subsetSize < (boardSize * boardSize) - 1; subsetSize++) {
923       int[] theSubset = new int[subsetSize];
924       boolean subsetFound = false;
925       int[][][] subsets = new int[boardSize][boardSize][subsetSize];
926       int[][] visibleSubsets = new int[(boardSize * boardSize)][subsetSize];
927       int[] visibleSubsetCount = new int[(boardSize * boardSize)];
928       int[] subsetFoundAt = new int[(boardSize * boardSize)];
929       boolean[][] subsetSizeTrue = new boolean[boardSize][boardSize];
930       for (int j0 = 0; j0 < boardSize; j0++) {
931         for (int j1 = 0; j1 < boardSize; j1++) {
932           subsetFound = false;
933           //Grid Subset Capture
934           for (int k0 = 0; k0 < boardSize; k0++) {
935             for (int k1 = 0; k1 < boardSize; k1++) {
936               subsetSizeTrue[k0][k1] = false;
937             }
938           }
939           for (int k0 = 0; k0 < boardSize; k0++) {
940             for (int k1 = 0; k1 < boardSize; k1++) {
941               minimumSubsetPresent = 0;
942               if (!cellFilled[j0][j1][k0][k1]) {
943                 for (int val = 0; val < (boardSize * boardSize); val++) {
944                   if (placeCanContain[j0][j1][k0][k1][val]) {
945                     if (minimumSubsetPresent < subsetSize) {
946                       subsets[k0][k1][minimumSubsetPresent] = val;
947                       minimumSubsetPresent++;
948                       if (minimumSubsetPresent == subsetSize) {
949                         subsetSizeTrue[k0][k1] = true;
950                       } else {
951                         subsetSizeTrue[k0][k1] = false;
952                       }
953                     } else {
954                       subsetSizeTrue[k0][k1] = false;
955                     }
956                   }
957                 }
958               }
959             }
960           } //(starts with subsetSizeTrue == true iff it only has the subset size many possibilities)
961           //Grid Subset Check
962           numberOfTrueSubsets = 0;
963           for (int k0 = 0; k0 < boardSize; k0++) {
964             for (int k1 = 0; k1 < boardSize; k1++) {
965               if (subsetSizeTrue[k0][k1]) {
966                 subsetFoundAt[numberOfTrueSubsets] = k0 * boardSize + k1;
967                 numberOfTrueSubsets++;
968               }
969               visibleSubsetCount[k0 * boardSize + k1] = 0;
970             }
971           }
972           if (numberOfTrueSubsets >= subsetSize) {
973             numberOfVisibleSubsets = 1;
974             visibleSubsetIndex = 1;
975             s01 = subsetFoundAt[0] % boardSize;
976             s00 = (subsetFoundAt[0] - s01) / boardSize;
977             visibleSubsets[0] = subsets[s00][s01];
978             visibleSubsetCount[0] = 1; 
979             outer: for (int q0 = 1; q0 < numberOfTrueSubsets; q0++) {
980               s01 = subsetFoundAt[q0] % boardSize;
981               s00 = (subsetFoundAt[q0] - s01) / boardSize;
982               inner: for (int q1 = 0; q1 < visibleSubsetIndex; q1++) {
983                 //System.out.println("q0 " + q0 + " and q1 " + q1);
984                 if (Arrays.equals(subsets[s00][s01], visibleSubsets[q1])) {
985                   //System.out.println(Arrays.toString(subsets[s00][s01]) + " equal to " + Arrays.toString(visibleSubsets[q1]) + " at visibility " + visibleSubsetIndex + " and q0 " + q0 + " and q1 " + q1);
986                   visibleSubsetCount[q1]++;
987                   break inner;
988                 } else if (q1 == (visibleSubsetIndex - 1)) {
989                   visibleSubsets[visibleSubsetIndex] = subsets[s00][s01];
990                   visibleSubsetCount[visibleSubsetIndex]++;
991                   visibleSubsetIndex++;
992                   break inner;
993                 }
994               }
995             }
996             for (int q = 0; q < visibleSubsetIndex; q++) {
997               if (visibleSubsetCount[q] == subsetSize) {
998                 subsetFound = true;
999                 theSubset = visibleSubsets[q];
1000                 break;
1001               }
1002             }
1003           }
1004           //Grid Subset Action
1005           if (subsetFound) {
1006             /*System.out.println(numberOfTrueSubsets + " " + visibleSubsetIndex);
1007             System.out.println(Arrays.toString(visibleSubsets[0]) + " " + visibleSubsetCount[0]);
1008             System.out.println(Arrays.toString(visibleSubsets[1]) + " " + visibleSubsetCount[1]);
1009             System.out.println(Arrays.toString(visibleSubsets[2]) + " " + visibleSubsetCount[2]);
1010             System.out.println(Arrays.toString(placeCanContain[0][1][1][2]));
1011             System.out.println(Arrays.toString(placeCanContain[0][1][2][0]));
1012             System.out.println(Arrays.toString(placeCanContain[0][1][2][1]));*/
1013             for (int k0 = 0; k0 < boardSize; k0++) {
1014               for (int k1 = 0; k1 < boardSize; k1++) {
1015                 if (!cellFilled[j0][j1][k0][k1]) {
1016                   if (Arrays.equals(subsets[k0][k1], theSubset) && subsetSizeTrue[k0][k1]) {
1017                     continue;
1018                   } else {
1019                     for (int subsetIter = 0; subsetIter < subsetSize; subsetIter++) {
1020                       if (placeCanContain[j0][j1][k0][k1][theSubset[subsetIter]]) {
1021                         placeCanContain[j0][j1][k0][k1][theSubset[subsetIter]] = false;
1022                         deliminations++;
1023                       }
1024                     }
1025                   }
1026                 }
1027               }
1028             }
1029             if (deliminations > 0) {
1030               System.out.println("Visible subset in grid " + (j0 * 3 + j1) + " of size " + subsetSize + ", " + Arrays.toString(theSubset));
1031               /*System.out.println(Arrays.toString(subsetSizeTrue[0]) + Arrays.toString(subsetSizeTrue[1]) + Arrays.toString(subsetSizeTrue[2]));
1032               System.out.println(Arrays.toString(placeCanContain[0][1][1][2]));
1033               System.out.println(Arrays.toString(placeCanContain[0][1][2][0]));
1034               System.out.println(Arrays.toString(placeCanContain[0][1][2][1]));*/
1035               return true;
1036             } else {
1037               subsetFound = false;
1038             }
1039           }
1040           //Row Subset Capture
1041           for (int k0 = 0; k0 < boardSize; k0++) {
1042             for (int k1 = 0; k1 < boardSize; k1++) {
1043               subsetSizeTrue[k0][k1] = false;
1044             }
1045           }
1046           for (int k0 = 0; k0 < boardSize; k0++) {
1047             for (int k1 = 0; k1 < boardSize; k1++) {
1048               minimumSubsetPresent = 0;
1049               if (!cellFilled[j0][k0][j1][k1]) {
1050                 for (int val = 0; val < (boardSize * boardSize); val++) {
1051                   if (placeCanContain[j0][k0][j1][k1][val]) {
1052                     if (minimumSubsetPresent < subsetSize) {
1053                       subsets[k0][k1][minimumSubsetPresent] = val;
1054                       minimumSubsetPresent++;
1055                       if (minimumSubsetPresent == subsetSize) {
1056                         subsetSizeTrue[k0][k1] = true;
1057                       } else {
1058                         subsetSizeTrue[k0][k1] = false;
1059                       }
1060                     } else {
1061                       subsetSizeTrue[k0][k1] = false;
1062                     }
1063                   }
1064                 }
1065               }
1066             }
1067           }
1068           //Row Subset Check
1069           numberOfTrueSubsets = 0;
1070           for (int k0 = 0; k0 < boardSize; k0++) {
1071             for (int k1 = 0; k1 < boardSize; k1++) {
1072               if (subsetSizeTrue[k0][k1]) {
1073                 subsetFoundAt[numberOfTrueSubsets] = k0 * boardSize + k1;
1074                 numberOfTrueSubsets++;
1075               }
1076               visibleSubsetCount[k0 * boardSize + k1] = 0;
1077             }
1078           }
1079           if (numberOfTrueSubsets >= subsetSize) {
1080             numberOfVisibleSubsets = 1;
1081             visibleSubsetIndex = 1;
1082             s01 = subsetFoundAt[0] % boardSize;
1083             s00 = (subsetFoundAt[0] - s01) / boardSize;
1084             visibleSubsets[0] = subsets[s00][s01];
1085             visibleSubsetCount[0] = 1;
1086             outer: for (int q0 = 1; q0 < numberOfTrueSubsets; q0++) {
1087               s01 = subsetFoundAt[q0] % boardSize;
1088               s00 = (subsetFoundAt[q0] - s01) / boardSize;
1089               inner: for (int q1 = 0; q1 < visibleSubsetIndex; q1++) {
1090                 if (Arrays.equals(subsets[s00][s01], visibleSubsets[q1])) {
1091                   visibleSubsetCount[q1]++;
1092                   break inner;
1093                 } else if (q1 == (visibleSubsetIndex - 1)) {
1094                   visibleSubsets[visibleSubsetIndex] = subsets[s00][s01];
1095                   visibleSubsetCount[visibleSubsetIndex]++;
1096                   visibleSubsetIndex++;
1097                   break inner;
1098                 }
1099               }
1100             }
1101             for (int q = 0; q < visibleSubsetIndex; q++) {
1102               if (visibleSubsetCount[q] == subsetSize) {
1103                 subsetFound = true;
1104                 theSubset = visibleSubsets[q];
1105                 break;
1106               }
1107             }
1108           }
1109           //Row Subset Action
1110           if (subsetFound) {
1111             for (int k0 = 0; k0 < boardSize; k0++) {
1112               for (int k1 = 0; k1 < boardSize; k1++) {
1113                 if (!cellFilled[j0][k0][j1][k1]) {
1114                   if (Arrays.equals(subsets[k0][k1], theSubset) && subsetSizeTrue[k0][k1]) {
1115                     continue;
1116                   } else {
1117                     for (int subsetIter = 0; subsetIter < subsetSize; subsetIter++) {
1118                       if (placeCanContain[j0][k0][j1][k1][theSubset[subsetIter]]) {
1119                         placeCanContain[j0][k0][j1][k1][theSubset[subsetIter]] = false;
1120                         deliminations++;
1121                       }
1122                     }
1123                   }
1124                 }
1125               }
1126             }
1127             if (deliminations > 0) {
1128               System.out.println("Visible subset in row " + (j0 * 3 + j1) + " of size " + subsetSize + ", " + Arrays.toString(theSubset));
1129               return true;
1130             } else {
1131               subsetFound = false;
1132             }
1133           }
1134           //Column Subset Capture
1135           for (int k0 = 0; k0 < boardSize; k0++) {
1136             for (int k1 = 0; k1 < boardSize; k1++) {
1137               subsetSizeTrue[k0][k1] = false;
1138             }
1139           }
1140           for (int k0 = 0; k0 < boardSize; k0++) {
1141             for (int k1 = 0; k1 < boardSize; k1++) {
1142               minimumSubsetPresent = 0;
1143               if (!cellFilled[k0][j0][k1][j1]) {
1144                 for (int val = 0; val < (boardSize * boardSize); val++) {
1145                   if (placeCanContain[k0][j0][k1][j1][val]) {
1146                     if (minimumSubsetPresent < subsetSize) {
1147                       subsets[k0][k1][minimumSubsetPresent] = val;
1148                       minimumSubsetPresent++;
1149                       if (minimumSubsetPresent == subsetSize) {
1150                         subsetSizeTrue[k0][k1] = true;
1151                       } else {
1152                         subsetSizeTrue[k0][k1] = false;
1153                       }
1154                     } else {
1155                       subsetSizeTrue[k0][k1] = false;
1156                     }
1157                   }
1158                 }
1159               }
1160             }
1161           }
1162           //Column Subset Check
1163           numberOfTrueSubsets = 0;
1164           for (int k0 = 0; k0 < boardSize; k0++) {
1165             for (int k1 = 0; k1 < boardSize; k1++) {
1166               if (subsetSizeTrue[k0][k1]) {
1167                 subsetFoundAt[numberOfTrueSubsets] = k0 * boardSize + k1;
1168                 numberOfTrueSubsets++;
1169               }
1170               visibleSubsetCount[k0 * boardSize + k1] = 0;
1171             }
1172           }
1173           if (numberOfTrueSubsets >= subsetSize) {
1174             numberOfVisibleSubsets = 1;
1175             visibleSubsetIndex = 1;
1176             s01 = subsetFoundAt[0] % boardSize;
1177             s00 = (subsetFoundAt[0] - s01) / boardSize;
1178             visibleSubsets[0] = subsets[s00][s01];
1179             visibleSubsetCount[0] = 1;
1180             outer: for (int q0 = 1; q0 < numberOfTrueSubsets; q0++) {
1181               s01 = subsetFoundAt[q0] % boardSize;
1182               s00 = (subsetFoundAt[q0] - s01) / boardSize;
1183               inner: for (int q1 = 0; q1 < visibleSubsetIndex; q1++) {
1184                 if (Arrays.equals(subsets[s00][s01], visibleSubsets[q1])) {
1185                   visibleSubsetCount[q1]++;
1186                   break inner;
1187                 } else if (q1 == (visibleSubsetIndex - 1)) {
1188                   visibleSubsets[visibleSubsetIndex] = subsets[s00][s01];
1189                   visibleSubsetCount[visibleSubsetIndex]++;
1190                   visibleSubsetIndex++;
1191                   break inner;
1192                 }
1193               }
1194             }
1195             for (int q = 0; q < visibleSubsetIndex; q++) {
1196               if (visibleSubsetCount[q] == subsetSize) {
1197                 subsetFound = true;
1198                 theSubset = visibleSubsets[q];
1199                 break;
1200               }
1201             }
1202           }
1203           //Column Subset Action
1204           if (subsetFound) {
1205             for (int k0 = 0; k0 < boardSize; k0++) {
1206               for (int k1 = 0; k1 < boardSize; k1++) {
1207                 if (!cellFilled[k0][j0][k1][j1]) {
1208                   if (Arrays.equals(subsets[k0][k1], theSubset) && subsetSizeTrue[k0][k1]) {
1209                     continue;
1210                   } else {
1211                     for (int subsetIter = 0; subsetIter < subsetSize; subsetIter++) {
1212                       if (placeCanContain[k0][j0][k1][j1][theSubset[subsetIter]]) {
1213                         placeCanContain[k0][j0][k1][j1][theSubset[subsetIter]] = false;
1214                         deliminations++;
1215                       }
1216                     }
1217                   }
1218                 }
1219               }
1220             }
1221             if (deliminations > 0) {
1222               System.out.println("Visible subset in column " + (j0 * 3 + j1) + " of size " + subsetSize + ", " + Arrays.toString(theSubset));
1223               return true;
1224             } else {
1225               subsetFound = false;
1226             }
1227           }
1228         }
1229       }
1230     }
1231     return false;
1232   }
1233 
1234   public int iterateReductionOfCanContainSubsetHidden() {
1235     int count = 0;
1236     while (reductionOfCanContainSubsetHidden()) {
1237       count++;
1238     }
1239     return count;
1240   }
1241 
1242   public boolean reductionOfCanContainSubsetHidden() { //if there are n values that can only be found in n squares those squares can contain no other values
1243     int[] valueIncidence = new int[(boardSize * boardSize)];
1244     int valuesIncidental = 0;
1245     int subsetSquares = 0;
1246     int s0 = 0;
1247     int s1 = 0;
1248     boolean[] hiddenValue = new boolean[(boardSize * boardSize)];
1249     boolean subsetCaptured;
1250     boolean subsetChecked;
1251     ArrayList<Integer> hiddenValues = new ArrayList<Integer>();
1252     ArrayList<Integer> hiddenSubsetSquares = new ArrayList<Integer>();
1253     PermutationComputation permVals = new PermutationComputation();
1254     PermutationComputation permSquares = new PermutationComputation();
1255     int deliminations = 0;
1256     for (int subsetSize = 2; subsetSize < (boardSize * boardSize) - 1; subsetSize++) {
1257       int[] theSubset = new int[subsetSize];
1258       int[] theSquares = new int[subsetSize];
1259       for (int j0 = 0; j0 < boardSize; j0++) {
1260         for (int j1 = 0; j1 < boardSize; j1++) {
1261           //Grid Capture
1262           subsetCaptured = false;
1263           subsetChecked = false;
1264           valuesIncidental = 0;
1265           hiddenValues.clear();
1266           for (int q = 0; q < (boardSize * boardSize); q++) {
1267             valueIncidence[q] = 0;
1268             hiddenValue[q] = false;
1269           }
1270           for (int k0 = 0; k0 < boardSize; k0++) {
1271             for (int k1 = 0; k1 < boardSize; k1++) {
1272               if (!cellFilled[j0][j1][k0][k1]) {
1273                 for (int q = 0; q < (boardSize * boardSize); q++) {
1274                   if (placeCanContain[j0][j1][k0][k1][q]) {
1275                     valueIncidence[q]++;
1276                   }
1277                 }
1278               }
1279             }
1280           }
1281           for (int q = 0; q < (boardSize * boardSize); q++) {
1282             if (valueIncidence[q] == subsetSize) {
1283               valuesIncidental++;
1284             }
1285           }
1286           if (valuesIncidental >= subsetSize) {
1287             subsetCaptured = true;
1288           }
1289           //Grid Check
1290           if (subsetCaptured) { //there are n or more values that appear only n times, now find the squares
1291             hiddenSubsetSquares.clear();
1292             subsetSquares = 0;
1293             for (int q = 0; q < (boardSize * boardSize); q++) {
1294               if (valueIncidence[q] == subsetSize) {
1295                 hiddenValue[q] = true;
1296                 hiddenValues.add(q);
1297               }
1298             }
1299             for (int k0 = 0; k0 < boardSize; k0++) {
1300               for (int k1 = 0; k1 < boardSize; k1++) {
1301                 if (!cellFilled[j0][j1][k0][k1]) {
1302                   valuesIncidental = 0;
1303                   for (int q = 0; q < (boardSize * boardSize); q++) {
1304                     if (placeCanContain[j0][j1][k0][k1][q] && hiddenValue[q]) {
1305                       valuesIncidental++;
1306                     }
1307                   }
1308                   if (valuesIncidental >= subsetSize) {
1309                     hiddenSubsetSquares.add(k0 * 3 + k1);
1310                     subsetSquares++;
1311                   }
1312                 }
1313               }
1314             }
1315             if (subsetSquares >= subsetSize) {
1316               boolean[][] subsets = new boolean[subsetSquares][hiddenValues.size()];
1317               boolean falseDeclared = true;
1318               for (int square = 0; square < subsetSquares; square++) {
1319                 for (int vals = 0; vals < hiddenValues.size(); vals++) {
1320                   s1 = hiddenSubsetSquares.get(square) % boardSize;
1321                   s0 = (hiddenSubsetSquares.get(square) - s1) / boardSize;
1322                   if (placeCanContain[j0][j1][s0][s1][hiddenValues.get(vals)]) {
1323                     subsets[square][vals] = true;
1324                   } else {
1325                     subsets[square][vals] = false;
1326                   }
1327                 }
1328               }
1329               permVals.clear();
1330               permVals.permutationCompute(subsetSize, hiddenValues.size());
1331               permSquares.clear();
1332               permSquares.permutationCompute(subsetSize, subsetSquares);
1333               outer: for (int square = 0; square < permSquares.size(); square++) {
1334                 for (int vals = 0; vals < permVals.size(); vals++) {
1335                   falseDeclared = false;
1336                   for (int b0 = 0; b0 < subsetSize; b0++) {
1337                     for (int b1 = 0; b1 < subsetSize; b1++) {
1338                       if (!subsets[permSquares.getget(square, b1)][permVals.getget(vals, b0)]) {
1339                         falseDeclared = true;
1340                       }
1341                     }
1342                   }
1343                   if (!falseDeclared) {
1344                     theSubset = permVals.get(vals);
1345                     theSquares = permSquares.get(square);
1346                     for (int h = 0; h < subsetSize; h++) {
1347                       theSubset[h] = hiddenValues.get(theSubset[h]);
1348                       theSquares[h] = hiddenSubsetSquares.get(theSquares[h]);
1349                     }
1350                     subsetChecked = true;
1351                     break outer;
1352                   }
1353                 }
1354               }
1355             }
1356           }
1357           //Grid Action
1358           if (subsetChecked) {
1359             ArrayList<Integer> subVoids = new ArrayList<Integer>();
1360             for (int h = 0; h < subsetSize; h++) {
1361               subVoids.add(theSubset[h]);
1362             }
1363             for (int h = 0; h < subsetSize; h++) {
1364               s1 = theSquares[h] % boardSize;
1365               s0 = (theSquares[h] - s1) / boardSize;
1366               for (int val = 0; val < (boardSize * boardSize); val++) {
1367                 if (placeCanContain[j0][j1][s0][s1][val] && !subVoids.contains(val)) {
1368                   placeCanContain[j0][j1][s0][s1][val] = false;
1369                   deliminations++;
1370                 }
1371               }
1372             }
1373             if (deliminations > 0) {
1374               System.out.println("Hidden subset in grid " + (j0 * 3 + j1) + " of size " + subsetSize + ", " + Arrays.toString(theSubset));
1375               return true;
1376             } else {
1377               subsetChecked = false;
1378             }
1379           }
1380           //Row Capture
1381           subsetCaptured = false;
1382           subsetChecked = false;
1383           valuesIncidental = 0;
1384           hiddenValues.clear();
1385           for (int q = 0; q < (boardSize * boardSize); q++) {
1386             valueIncidence[q] = 0;
1387             hiddenValue[q] = false;
1388           }
1389           for (int k0 = 0; k0 < boardSize; k0++) {
1390             for (int k1 = 0; k1 < boardSize; k1++) {
1391               if (!cellFilled[j0][k0][j1][k1]) {
1392                 for (int q = 0; q < (boardSize * boardSize); q++) {
1393                   if (placeCanContain[j0][k0][j1][k1][q]) {
1394                     valueIncidence[q]++;
1395                   }
1396                 }
1397               }
1398             }
1399           }
1400           for (int q = 0; q < (boardSize * boardSize); q++) {
1401             if (valueIncidence[q] == subsetSize) {
1402               valuesIncidental++;
1403             }
1404           }
1405           if (valuesIncidental >= subsetSize) {
1406             subsetCaptured = true;
1407           }
1408           //Row Check
1409           if (subsetCaptured) {
1410             hiddenSubsetSquares.clear();
1411             subsetSquares = 0;
1412             for (int q = 0; q < (boardSize * boardSize); q++) {
1413               if (valueIncidence[q] == subsetSize) {
1414                 hiddenValue[q] = true;
1415                 hiddenValues.add(q);
1416               }
1417             }
1418             for (int k0 = 0; k0 < boardSize; k0++) {
1419               for (int k1 = 0; k1 < boardSize; k1++) {
1420                 if (!cellFilled[j0][k0][j1][k1]) {
1421                   valuesIncidental = 0;
1422                   for (int q = 0; q < (boardSize * boardSize); q++) {
1423                     if (placeCanContain[j0][k0][j1][k1][q] && hiddenValue[q]) {
1424                       valuesIncidental++;
1425                     }
1426                   }
1427                   if (valuesIncidental >= subsetSize) {
1428                     hiddenSubsetSquares.add(k0 * 3 + k1);
1429                     subsetSquares++;
1430                   }
1431                 }
1432               }
1433             }
1434             if (subsetSquares >= subsetSize) {
1435               boolean[][] subsets = new boolean[subsetSquares][hiddenValues.size()];
1436               boolean falseDeclared = true;
1437               for (int square = 0; square < subsetSquares; square++) {
1438                 for (int vals = 0; vals < hiddenValues.size(); vals++) {
1439                   s1 = hiddenSubsetSquares.get(square) % boardSize;
1440                   s0 = (hiddenSubsetSquares.get(square) - s1) / boardSize;
1441                   if (placeCanContain[j0][s0][j1][s1][hiddenValues.get(vals)]) {
1442                     subsets[square][vals] = true;
1443                   } else {
1444                     subsets[square][vals] = false;
1445                   }
1446                 }
1447               }
1448               permVals.clear();
1449               permVals.permutationCompute(subsetSize, hiddenValues.size());
1450               permSquares.clear();
1451               permSquares.permutationCompute(subsetSize, subsetSquares);
1452               outer: for (int square = 0; square < permSquares.size(); square++) {
1453                 for (int vals = 0; vals < permVals.size(); vals++) {
1454                   falseDeclared = false;
1455                   for (int b0 = 0; b0 < subsetSize; b0++) {
1456                     for (int b1 = 0; b1 < subsetSize; b1++) {
1457                       if (!subsets[permSquares.getget(square, b1)][permVals.getget(vals, b0)]) {
1458                         falseDeclared = true;
1459                       }
1460                     }
1461                   }
1462                   if (!falseDeclared) {
1463                     theSubset = permVals.get(vals);
1464                     theSquares = permSquares.get(square);
1465                     for (int h = 0; h < subsetSize; h++) {
1466                       theSubset[h] = hiddenValues.get(theSubset[h]);
1467                       theSquares[h] = hiddenSubsetSquares.get(theSquares[h]);
1468                     }
1469                     subsetChecked = true;
1470                     break outer;
1471                   }
1472                 }
1473               }
1474             }
1475 
1476           }
1477           //Row Action
1478           if (subsetChecked) {
1479             ArrayList<Integer> subVoids = new ArrayList<Integer>();
1480             for (int h = 0; h < subsetSize; h++) {
1481               subVoids.add(theSubset[h]);
1482             }
1483             for (int h = 0; h < subsetSize; h++) {
1484               s1 = theSquares[h] % boardSize;
1485               s0 = (theSquares[h] - s1) / boardSize;
1486               for (int val = 0; val < (boardSize * boardSize); val++) {
1487                 if (placeCanContain[j0][s0][j1][s1][val] && !subVoids.contains(val)) {
1488                   placeCanContain[j0][s0][j1][s1][val] = false;
1489                   deliminations++;
1490                 }
1491               }
1492             }
1493             if (deliminations > 0) {
1494               System.out.println("Hidden subset in row " + (j0 * 3 + j1) + " of size " + subsetSize + ", " + Arrays.toString(theSubset));
1495               return true;
1496             } else {
1497               subsetChecked = false;
1498             }
1499           }
1500           //Col Capture
1501           subsetCaptured = false;
1502           subsetChecked = false;
1503           valuesIncidental = 0;
1504           hiddenValues.clear();
1505           for (int q = 0; q < (boardSize * boardSize); q++) {
1506             valueIncidence[q] = 0;
1507             hiddenValue[q] = false;
1508           }
1509           for (int k0 = 0; k0 < boardSize; k0++) {
1510             for (int k1 = 0; k1 < boardSize; k1++) {
1511               if (!cellFilled[k0][j0][k1][j1]) {
1512                 for (int q = 0; q < (boardSize * boardSize); q++) {
1513                   if (placeCanContain[k0][j0][k1][j1][q]) {
1514                     valueIncidence[q]++;
1515                   }
1516                 }
1517               }
1518             }
1519           }
1520           for (int q = 0; q < (boardSize * boardSize); q++) {
1521             if (valueIncidence[q] == subsetSize) {
1522               valuesIncidental++;
1523             }
1524           }
1525           if (valuesIncidental >= subsetSize) {
1526             subsetCaptured = true;
1527           }
1528           //Col Check
1529           if (subsetCaptured) {
1530             hiddenSubsetSquares.clear();
1531             subsetSquares = 0;
1532             for (int q = 0; q < (boardSize * boardSize); q++) {
1533               if (valueIncidence[q] == subsetSize) {
1534                 hiddenValue[q] = true;
1535                 hiddenValues.add(q);
1536               }
1537             }
1538             for (int k0 = 0; k0 < boardSize; k0++) {
1539               for (int k1 = 0; k1 < boardSize; k1++) {
1540                 if (!cellFilled[k0][j0][k1][j1]) {
1541                   valuesIncidental = 0;
1542                   for (int q = 0; q < (boardSize * boardSize); q++) {
1543                     if (placeCanContain[k0][j0][k1][j1][q] && hiddenValue[q]) {
1544                       valuesIncidental++;
1545                     }
1546                   }
1547                   if (valuesIncidental >= subsetSize) {
1548                     hiddenSubsetSquares.add(k0 * 3 + k1);
1549                     subsetSquares++;
1550                   }
1551                 }
1552               }
1553             }
1554             if (subsetSquares >= subsetSize) {
1555               boolean[][] subsets = new boolean[subsetSquares][hiddenValues.size()];
1556               boolean falseDeclared = true;
1557               for (int square = 0; square < subsetSquares; square++) {
1558                 for (int vals = 0; vals < hiddenValues.size(); vals++) {
1559                   s1 = hiddenSubsetSquares.get(square) % boardSize;
1560                   s0 = (hiddenSubsetSquares.get(square) - s1) / boardSize;
1561                   if (placeCanContain[s0][j0][s1][j1][hiddenValues.get(vals)]) {
1562                     subsets[square][vals] = true;
1563                   } else {
1564                     subsets[square][vals] = false;
1565                   }
1566                 }
1567               }
1568               permVals.clear();
1569               permVals.permutationCompute(subsetSize, hiddenValues.size());
1570               permSquares.clear();
1571               permSquares.permutationCompute(subsetSize, subsetSquares);
1572               outer: for (int square = 0; square < permSquares.size(); square++) {
1573                 for (int vals = 0; vals < permVals.size(); vals++) {
1574                   falseDeclared = false;
1575                   for (int b0 = 0; b0 < subsetSize; b0++) {
1576                     for (int b1 = 0; b1 < subsetSize; b1++) {
1577                       if (!subsets[permSquares.getget(square, b1)][permVals.getget(vals, b0)]) {
1578                         falseDeclared = true;
1579                       }
1580                     }
1581                   }
1582                   if (!falseDeclared) {
1583                     theSubset = permVals.get(vals);
1584                     theSquares = permSquares.get(square);
1585                     for (int h = 0; h < subsetSize; h++) {
1586                       theSubset[h] = hiddenValues.get(theSubset[h]);
1587                       theSquares[h] = hiddenSubsetSquares.get(theSquares[h]);
1588                     }
1589                     subsetChecked = true;
1590                     break outer;
1591                   }
1592                 }
1593               }
1594             }
1595           }
1596           //Col Action
1597           if (subsetChecked) {
1598             ArrayList<Integer> subVoids = new ArrayList<Integer>();
1599             for (int h = 0; h < subsetSize; h++) {
1600               subVoids.add(theSubset[h]);
1601             }
1602             for (int h = 0; h < subsetSize; h++) {
1603               s1 = theSquares[h] % boardSize;
1604               s0 = (theSquares[h] - s1) / boardSize;
1605               for (int val = 0; val < (boardSize * boardSize); val++) {
1606                 if (placeCanContain[s0][j0][s1][j1][val] && !subVoids.contains(val)) {
1607                   placeCanContain[s0][j0][s1][j1][val] = false;
1608                   deliminations++;
1609                 }
1610               }
1611             }
1612             if (deliminations > 0) {
1613               System.out.println("Hidden subset in column " + (j0 * 3 + j1) + " of size " + subsetSize + ", " + Arrays.toString(theSubset));
1614               return true;
1615             } else {
1616               subsetChecked = false;
1617             }
1618           }
1619         }
1620       }
1621     }
1622     return false;
1623   }
1624 
1625 
1626   public void deliminatePotential(int k0, int k1, int k2, int k3, int k4) {
1627     numberPresentRowCol[0][k4][k2 + k0 * board.length] = true;
1628     numberPresentRowCol[1][k4][k3 + k1 * board.length] = true;
1629     numberPresentG[k4][k0][k1] = true;
1630     for (int j0 = 0; j0 < boardSize; j0++) {
1631       for (int j1 = 0; j1 < boardSize; j1++) {
1632         placeCanContain[k0][j0][k2][j1][k4] = false; //row assertion
1633         placeCanContain[j0][k1][j1][k3][k4] = false; //column assertion
1634         placeCanContain[k0][k1][j0][j1][k4] = false; //grid assertion
1635       }
1636     }
1637   }
1638 
1639   public int[] subsetCollisionAt(int subsetSize, int numberOfTrueSubsets, int[] subsetFoundAt, int[][][] subsets) {
1640     int[] collisionsAt = new int[subsetSize];
1641     int numberOfPerfectCollisions = 0;
1642     subsetCollisionAtRolling(subsetSize, numberOfTrueSubsets, subsetFoundAt, subsets);
1643 
1644     return collisionsAt;
1645   }
1646 
1647   public void subsetCollisionAtRolling(int subsetSize, int numberOfTrueSubsets, int[] subsetFoundAt, int[][][] subsets) {
1648     int q1 = 0;
1649     int q0 = 0;
1650     for (int q = 0; q < numberOfTrueSubsets; q++) {
1651       subsetCollisionAtRolling(subsetSize, numberOfTrueSubsets - 1, subsetFoundAt, subsets);
1652       q1 = subsetFoundAt[q] % boardSize;
1653       q0 = (subsetFoundAt[q] - q1) / boardSize;
1654       //if (Arrays.equals(subsets[s0j0][s0j1], subsets[s1j0][s1j1])) {
1655       //    subsetPair1 = q0; subsetPair2 = q1; subsetFound = true;
1656       //    break;
1657       //}
1658     }
1659   }
1660 
1661   public boolean placeCanContainCheckDoesCollide(int j0, int j1, int j2, int j3, int k0, int k1, int k2, int k3, int collisions) {
1662     int counted = 0;
1663     for (int q = 0; q < (boardSize * boardSize); q++) {
1664       if (placeCanContain[j0][j1][j2][j3][q] && placeCanContain[k0][k1][k2][k3][q]) {
1665         counted++;
1666       }
1667     }
1668     if (counted >= collisions) {
1669       return true;
1670     }
1671     return false;
1672   }
1673 
1674   public boolean[] placeCanContainCheckCollision(int j0, int j1, int j2, int j3, int k0, int k1, int k2, int k3) {
1675     boolean[] collidingValues = new boolean[(boardSize * boardSize)];
1676     for (int q = 0; q < (boardSize * boardSize); q++) {
1677       if (placeCanContain[j0][j1][j2][j3][q] && placeCanContain[k0][k1][k2][k3][q]) {
1678         collidingValues[q] = true;
1679       } else {
1680         collidingValues[q] = false;
1681       }
1682     }
1683     return collidingValues;
1684   }
1685 
1686 }