/**
* 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