/** * This <code>Board</code> object can be used to simulate a SuDoKu board. * It uses a 3x3 grid of 3x3 grids (<code>int[3][3][3][3]</code>) to keep track * of the values. The <code>Board</code> class contains 3 preset grids of * varying difficulty for quick implementation, the ability to add your own * user-defined grid, and can return reasons (as a <code>String</code>) for * invalid input into the <code>Board</code>. * * @author Thomas Zaki * @version 2.0 */ public class Board { private int[][][][] grid; private String err; private int row, rowIndexA, rowIndexB, col, colIndexA, colIndexB, box, boxIndexA, boxIndexB; /** * Auto-fills the <code>Board</code> with an easy setup */ public static final int EASY = 0; /** * Auto-fills the <code>Board</code> with an intermediate setup */ public static final int MEDIUM = 1; /** * Auto-fills the <code>Board</code> with a hard setup */ public static final int HARD = 2; /** * Creates a new <code>Board</code> with all values set to 0 */ public Board() { //presets values to 0 grid = new int[3][3][3][3]; err = new String(); }//end Constructor /** * Creates a new <code>Board</code> with a specified difficulty level * * @param difficulty the difficulty level of the <code>Board</code> */ public Board(int difficulty) { err = new String(); /* {{{{1,0,1}, {0,1,0}, {1,0,1}}, {{0,2,0}, {2,0,2}, {0,2,0}}, {{3,0,3}, {0,3,0}, {3,0,3}}}, {{{0,4,0}, {4,0,4}, {0,4,0}}, {{5,0,5}, {0,5,0}, {5,0,5}}, {{0,6,0}, {6,0,6}, {0,6,0}}}, {{{7,0,7}, {0,0,0}, {7,0,7}}, {{0,8,0}, {8,0,8}, {0,8,0}}, {{9,0,9}, {0,9,0}, {9,0,9}}}}; will produce: +#######+#######+#######+ 1stRow 2ndRow 3rdRow # 1 1 # 2 # 3 3 # note that {{1,0,1}, {0,1,0}, {1,0,1}} # 1 # 2 2 # 3 # makes: # 1 1 # 2 # 3 3 # +#######+ +#######+#######+#######+ # 1 1 # # 4 # 5 5 # 6 # # 1 # # 4 4 # 5 # 6 6 # # 1 1 # # 4 # 5 5 # 6 # +#######+ +#######+#######+#######+ so each one represents a 3x3 grid and each # 7 7 # 8 # 9 9 # internal array is a row in that grid # 7 # 8 8 # 9 # # 7 7 # 8 # 9 9 # +#######+#######+#######+ */ switch (difficulty) { case 0: int[][][][] g1 = {{{{0,1,9}, {0,0,0}, {8,2,0}}, {{0,6,0}, {0,0,0}, {9,7,4}}, {{5,4,0}, {0,0,0}, {0,3,6}}}, {{{0,0,1}, {0,0,0}, {0,0,2}}, {{5,0,3}, {0,0,0}, {7,0,1}}, {{8,0,0}, {0,0,0}, {6,0,0}}}, {{{7,5,0}, {0,0,0}, {0,8,3}}, {{1,3,8}, {0,0,0}, {0,4,0}}, {{0,9,2}, {0,0,0}, {7,1,0}}}}; grid = (int[][][][]) g1.clone(); break; case 1: int[][][][] g2 = {{{{0,0,0}, {5,0,0}, {0,4,0}}, {{2,7,9}, {0,0,0}, {0,3,0}}, {{0,0,0}, {0,0,7}, {0,2,0}}}, {{{4,0,0}, {0,6,8}, {7,0,0}}, {{0,5,0}, {0,0,0}, {0,6,0}}, {{0,0,8}, {3,5,0}, {0,0,9}}}, {{{0,5,0}, {3,0,0}, {0,0,0}}, {{0,4,0}, {0,0,0}, {8,1,5}}, {{0,9,0}, {0,0,2}, {0,0,0}}}}; grid = (int[][][][]) g2.clone(); break; case 2: int[][][][] g3 = {{{{0,0,0}, {0,2,0}, {3,9,0}}, {{0,1,0}, {6,0,0}, {0,0,7}}, {{4,0,0}, {0,0,5}, {0,0,1}}}, {{{5,0,0}, {0,6,0}, {0,3,0}}, {{0,6,0}, {0,0,0}, {0,8,0}}, {{0,3,0}, {0,8,0}, {0,0,7}}}, {{{9,0,0}, {4,0,0}, {0,0,2}}, {{2,0,0}, {0,0,5}, {0,3,0}}, {{0,5,6}, {0,2,0}, {0,0,0}}}}; grid = (int[][][][]) g3.clone(); break; default: break; }//end switch difficulty }//end constructor public Board(int[][][][] g) { err = new String(); grid = (int[][][][]) g.clone(); }//end constructor /** * Returns whether the <code>Board</code> is full or not * * @return true if the <code>Board</code> is full, otherwise false */ public boolean fullBoard() { boolean result = true; for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) for(int k = 0; k < 3; k++) for(int l = 0; l < 3; l++) if(grid[i][k][j][l] == 0) //looks for zero's result = false; return result; } /** * Sets the values used to identify the current row, column, and box (or sub-grid) * * @param r the row * @param c the column * @return false if an error occurs, otherwise true */ private boolean setRowAndColAndBox(int r, int c) { boolean result = true; //directly assigns row and column values row = r; col = c; //assigns the row index values switch(row) { case 1: rowIndexA = 0; rowIndexB = 0; break; case 2: rowIndexA = 0; rowIndexB = 1; break; case 3: rowIndexA = 0; rowIndexB = 2; break; case 4: rowIndexA = 1; rowIndexB = 0; break; case 5: rowIndexA = 1; rowIndexB = 1; break; case 6: rowIndexA = 1; rowIndexB = 2; break; case 7: rowIndexA = 2; rowIndexB = 0; break; case 8: rowIndexA = 2; rowIndexB = 1; break; case 9: rowIndexA = 2; rowIndexB = 2; break; default: result = false; err += "\n(*) Column '" + col + "' does not exist.\n"; }//end switch row //assigns the column index values switch(col) { case 1: colIndexA = 0; colIndexB = 0; break; case 2: colIndexA = 0; colIndexB = 1; break; case 3: colIndexA = 0; colIndexB = 2; break; case 4: colIndexA = 1; colIndexB = 0; break; case 5: colIndexA = 1; colIndexB = 1; break; case 6: colIndexA = 1; colIndexB = 2; break; case 7: colIndexA = 2; colIndexB = 0; break; case 8: colIndexA = 2; colIndexB = 1; break; case 9: colIndexA = 2; colIndexB = 2; break; default: result = false; err += "\n(*) Row '" + row + "' does not exist."; }//end switch column //assigns the vale of box, based on row/column values if((row == 1 || row == 2 || row == 3) && (col == 1 || col == 2 || col == 3)) box = 1; else if((row == 1 || row == 2 || row == 3) && (col == 4 || col == 5 || col == 6)) box = 2; else if((row == 1 || row == 2 || row == 3) && (col == 7 || col == 8 || col == 9)) box = 3; else if((row == 4 || row == 5 || row == 6) && (col == 0 || col == 1 || col == 2)) box = 4; else if((row == 4 || row == 5 || row == 6) && (col == 4 || col == 5 || col == 6)) box = 5; else if((row == 4 || row == 5 || row == 6) && (col == 7 || col == 8 || col == 9)) box = 6; else if((row == 7 || row == 8 || row == 9) && (col == 1 || col == 2 || col == 3)) box = 7; else if((row == 7 || row == 8 || row == 9) && (col == 4 || col == 5 || col == 6)) box = 8; else if((row == 7 || row == 8 || row == 9) && (col == 7 || col == 8 || col == 9)) box = 9; //assigns the box index values switch(box) { case 1: boxIndexA = 0; boxIndexB = 0; break; case 2: boxIndexA = 0; boxIndexB = 1; break; case 3: boxIndexA = 0; boxIndexB = 2; break; case 4: boxIndexA = 1; boxIndexB = 0; break; case 5: boxIndexA = 1; boxIndexB = 1; break; case 6: boxIndexA = 1; boxIndexB = 2; break; case 7: boxIndexA = 2; boxIndexB = 0; break; case 8: boxIndexA = 2; boxIndexB = 1; break; case 9: boxIndexA = 2; boxIndexB = 2; break; default: result = false; }//end switch box return result; }//end setRowAndColAndBox /** * Checks a row for potential repeats * * @param value the value to find repeats of * @return false if a repeat exists, otherwise true */ private boolean checkRow(int value) { boolean result = true; //checks the values in a specified row // (a row is defined by row indexes, or making i and k constants) for(int j = 0; j < 3 && result; j++) for(int l = 0; l < 3 && result; l++) if(grid[rowIndexA][j][rowIndexB][l] == value) result = false; if(!result) err += "\n(*) The value entered already exists in row " + row + ". "; return result; }//end checkRow /** * Checks a column for potential repeats * * @param value the value to find repeats of * @return false if a repeat exists, otherwise true */ private boolean checkCol(int value) { boolean result = true; //checks the values in a specified column // (a column is defined by column indexes, or making j and l constants) for(int i = 0; i < 3 && result; i++) for(int k = 0; k < 3 && result; k++) if(grid[i][colIndexA][k][colIndexB] == value) result = false; if(!result) err += "\n(*) The value entered already exists in column " + col + ". "; return result; }//end checkCol /** * Checks a box for potential repeats * * @param value the value to find repeats of * @return false if a repeat exists, otherwise true */ private boolean checkBox(int value) { boolean result = true; //checks the values in a specified box // (a box is defined by box indexes, or making i and k constants) for(int j = 0; j < 3 && result; j++) for(int l = 0; l < 3 && result; l++) if(grid[boxIndexA][boxIndexB][j][l] == value) result = false; if(!result) err += "\n(*) The value entered already exists in box " + box + ". "; return result; }//end checkBox /** * Checks if the current space is already occupied * * @return false if the space is occupied, otherwise true */ private boolean isOccupied() { boolean result = false; //checks for values other than zero in every space if(grid[rowIndexA][colIndexA][rowIndexB][colIndexB] != 0) result = true; if(result) err += "\n(*) The space selected (" + row + ", " + col + ") is already occupied. "; return result; }//end isOccupied /** * Checks whether the placement of a value in a specified position is valid or not * * @param value the value being checked * @param row the new current row to be checked * @param col the new current column to be checked * @return false if an error occurs, otherwise true */ private boolean checkPlacement(int value, int row, int col) { boolean result = true; //tests for valid input for value // (row and column are tested in respective check methods) if(value < 1 || value > 9) { result = false; err += "\n(*) The value '" + value + "' is out of range [1-9]. "; }//end if if(result) { //sets current row, column, and box setRowAndColAndBox(row, col); //performs tests (individually, so that all errors are printed) if(isOccupied()) result = false; if(!checkRow(value)) result = false; if(!checkCol(value)) result = false; if(!checkBox(value)) result = false; }//end if return result; }//end checkPlacement /** * Adds a number to a specified location on the <code>Board</code> * * @param value the value to be added * @param row the row to add the value in * @param col the column to add the value in * @return false if an error occurs, otherwise true */ public boolean addNum(int value, int row, int col) { //resets err err = ""; //checks placement of the value boolean result = checkPlacement(value, row, col); //if the placement is valid, place it there if(result) grid[rowIndexA][colIndexA][rowIndexB][colIndexB] = value; return result; }//end addNum /** * Gets the current error <code>String</code> * * @return the error <code>String</code> generated by calling <code>addNum()</code> */ public String getErr() { return err; }//end getErr /** * Returns the <code>Board</code> as a <code>String</code> */ public String toString() { String result = new String("\n+#######+#######+#######+\n"); for(int i = 0; i < 3; i++) { result += "# "; for(int j = 0; j < 3; j++) { for(int k = 0; k < 3; k++) { for(int l = 0; l < 3; l++) { if(grid[i][k][j][l] != 0) result += grid[i][k][j][l] + " "; //The trick to understanding the above statement is to // just not think about it and accept the fact that // it works... else result += " "; }//end for 'l' result += "# "; }//end for 'k' result += "\n# "; }//end for 'j' //cuts off the extra '#' result = result.substring(0, result.length() - 2) + "+#######+#######+#######+\n"; }//end for 'i' return result; }//end toString }//end Board class