View Javadoc
1   package io.github.skenvy;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   import javax.swing.text.Utilities;
6   
7   import io.github.skenvy.CellCollection;
8   import io.github.skenvy.Utility;
9   
10  
11  
12  /**
13   * A Sudoku Grid ~ the whole "board" with N rows, columns, and boxes.
14   */
15  public class SudokuGrid {
16  
17    /**
18     * A list of the rows ~ from top to bottom.
19     */
20    private final List<CellCollection> rows;
21  
22    /**
23     * A list of the columns ~ from left to right.
24     */
25    private final List<CellCollection> columns;
26  
27    /**
28     * A list of the boxes [or blocks|regions] ~
29     * from top left across and then down to bottom right.
30     */
31    private final List<CellCollection> boxes;
32  
33    /**
34     * A 2d array of the cells, first indexing by row, then by column.
35     */
36    private final Cell[][][][] cells;
37  
38    /**
39     * Initialise an empty sudoku grid for a standard Sudoku board.
40     */
41    public SudokuGrid() throws SudokuCellGridInvalidGridShapeException, Cell.SudokuCellInvalidInitialValueException {
42      this.cells = createCellGrid(new int[3][3][3][3]);
43      this.rows = null;
44      this.columns = null;
45      this.boxes = null;
46    }
47  
48    /**
49     * Initialise an empty sudoku grid for a custom sized Sudoku board.
50     */
51    public SudokuGrid(int boardSize) throws SudokuCellGridInvalidGridShapeException, Cell.SudokuCellInvalidInitialValueException {
52      this.cells = createCellGrid(new int[3][3][3][3]);
53      int collectionSize = (boardSize * boardSize);
54      this.rows = null;
55      this.columns = null;
56      this.boxes = null;
57    }
58  
59    /**
60     * Initialise a grid from one already established.
61     */
62    public SudokuGrid(SudokuGrid initialGrid, boolean exactReplica) throws SudokuCellGridInvalidGridShapeException, Cell.SudokuCellInvalidInitialValueException {
63      this.cells = createCellGrid(new int[3][3][3][3]);
64      this.rows = initialGrid.getRows();
65      this.columns = initialGrid.getColumns();
66      this.boxes = initialGrid.getBoxes();
67    }
68  
69    private Cell[][][][] createCellGrid(int[][] valuesGrid) throws SudokuCellGridInvalidGridShapeException, Cell.SudokuCellInvalidInitialValueException {
70      if (valuesGrid.length != valuesGrid[0].length) {
71        throw new SudokuCellGridInvalidGridShapeException(valuesGrid.length, valuesGrid[0].length);
72      } else if (!Utility.isIntegerSquared(valuesGrid.length)) {
73        throw new SudokuCellGridInvalidGridShapeException(valuesGrid.length);
74      }
75      int boardSize = Utility.integerSquareRoot(valuesGrid.length);
76      int[][][][] newValuesGrid = new int[boardSize][boardSize][boardSize][boardSize];
77      for (int boardRow = 0; boardRow < boardSize; boardRow++) { // iterate the boardRow ~ "000111222"
78        for (int boardColumn = 0; boardColumn < boardSize; boardColumn++) { // iterate the boardColumn ~ "000111222"
79          for (int boxInternalRow = 0; boxInternalRow < boardSize; boxInternalRow++) { // iterate the boxInternalRow ~ "012012012"
80            for (int boxInternalColumn = 0; boxInternalColumn < boardSize; boxInternalColumn++) { // iterate the boxInternalColumn ~ "012012012
81              newValuesGrid[boardRow][boardColumn][boxInternalRow][boxInternalColumn] = valuesGrid[boardSize * boardRow + boxInternalRow][boardSize * boardColumn + boxInternalColumn];
82            }
83          }
84        }
85      }
86      return createCellGrid(newValuesGrid);
87    }
88  
89    private Cell[][][][] createCellGrid(int[][][][] valuesGrid) throws SudokuCellGridInvalidGridShapeException, Cell.SudokuCellInvalidInitialValueException {
90      if ((valuesGrid.length != valuesGrid[0].length) || (valuesGrid.length != valuesGrid[0][0].length) || (valuesGrid.length != valuesGrid[0][0][0].length)) {
91        throw new SudokuCellGridInvalidGridShapeException(valuesGrid.length, valuesGrid[0].length, valuesGrid[0][0].length, valuesGrid[0][0][0].length);
92      }
93      int boardSize = valuesGrid.length;
94      Cell[][][][] tempCells = new Cell[boardSize][boardSize][boardSize][boardSize];
95      List<CellCollection> rows = initialiseEmptyCellCollections(boardSize);
96      List<CellCollection> columns = initialiseEmptyCellCollections(boardSize);
97      List<CellCollection> boxes = initialiseEmptyCellCollections(boardSize);
98      for (int boardRow = 0; boardRow < boardSize; boardRow++) { // iterate the boardRow ~ "000111222"
99        for (int boardColumn = 0; boardColumn < boardSize; boardColumn++) { // iterate the boardColumn ~ "000111222"
100         for (int boxInternalRow = 0; boxInternalRow < boardSize; boxInternalRow++) { // iterate the boxInternalRow ~ "012012012"
101           for (int boxInternalColumn = 0; boxInternalColumn < boardSize; boxInternalColumn++) { // iterate the boxInternalColumn ~ "012012012
102             // We must first create the cell with a reference to its row, column, and box
103             int rowIndex = boardSize * boardRow + boxInternalRow;
104             int colIndex = boardSize * boardColumn + boxInternalColumn;
105             int boxIndex = boardSize * boardRow + boardColumn;
106             tempCells[boardRow][boardColumn][boxInternalRow][boxInternalColumn] = new Cell(valuesGrid[boardRow][boardColumn][boxInternalRow][boxInternalColumn], boardSize, rows.get(rowIndex), columns.get(colIndex), boxes.get(boxIndex));
107             // then add the created cell to that same row, column, and box.
108             rows.get(rowIndex).addCellToCollection(tempCells[boardRow][boardColumn][boxInternalRow][boxInternalColumn]);
109             columns.get(colIndex).addCellToCollection(tempCells[boardRow][boardColumn][boxInternalRow][boxInternalColumn]);
110             boxes.get(boxIndex).addCellToCollection(tempCells[boardRow][boardColumn][boxInternalRow][boxInternalColumn]);
111           }
112         }
113       }
114     }
115     return tempCells;
116   }
117 
118   List<CellCollection> initialiseEmptyCellCollections(int boardSize) {
119     List<CellCollection> tempCollections = new ArrayList<CellCollection>((boardSize * boardSize));
120     for (int k = 0; k < (boardSize * boardSize); k++) {
121       tempCollections.add(new CellCollection(boardSize));
122     }
123     return tempCollections;
124   }
125 
126   public List<CellCollection> getRows() {
127     return this.rows;
128   }
129 
130   public List<CellCollection> getColumns() {
131     return this.columns;
132   }
133 
134   public List<CellCollection> getBoxes() {
135     return this.boxes;
136   }
137 
138   /**
139    * Used for cell related exceptions.
140    */
141   public class SudokuCellGridException extends Exception {
142 
143     public SudokuCellGridException(String string) {
144       super(string);
145     }
146 
147   }
148 
149   public class SudokuCellGridInvalidGridShapeException extends Exception {
150 
151     public SudokuCellGridInvalidGridShapeException(int macroRows, int macroCols, int microRows, int microCols) {
152       super(String.format("The shape of the input grid was not a valid square, received nested array lengths of %d, %d, %d, %d", macroRows, macroCols, microRows, microCols));
153     }
154 
155     public SudokuCellGridInvalidGridShapeException(int rows, int cols) {
156       super(String.format("The shape of the input grid was not a valid square, received nested array lengths of %d, %d", rows, cols));
157     }
158 
159     public SudokuCellGridInvalidGridShapeException(int rows) {
160       super(String.format("The shape of the input grid was not a valid square, received non square value for collections count, received %d", rows));
161     }
162 
163   }
164 
165 }