import java.util.*;
/*
* Encapsulates a Sudoku grid to be solved.
* CS108 Stanford.
*/
public class Sudoku {
// Provided grid data for main/testing
// The instance variable strategy is up to you.
// Provided easy 1 6 grid
// (can paste this text into the GUI too)
public static final int[][] easyGrid = Sudoku.stringsToGrid(
"1 6 4 0 0 0 0 0 2",
"2 0 0 4 0 3 9 1 0",
"0 0 5 0 8 0 4 0 7",
"0 9 0 0 0 6 5 0 0",
"5 0 0 1 0 2 0 0 8",
"0 0 8 9 0 0 0 3 0",
"8 0 9 0 4 0 2 0 0",
"0 7 3 5 0 9 0 0 1",
"4 0 0 0 0 0 6 7 9");
// Provided medium 5 3 grid
public static final int[][] mediumGrid = Sudoku.stringsToGrid(
"530070000",
"600195000",
"098000060",
"800060003",
"400803001",
"700020006",
"060000280",
"000419005",
"000080079");
// Provided hard 3 7 grid
// 1 solution this way, 6 solutions if the 7 is changed to 0
public static final int[][] hardGrid = Sudoku.stringsToGrid(
"3 7 0 0 0 0 0 8 0",
"0 0 1 0 9 3 0 0 0",
"0 4 0 7 8 0 0 0 3",
"0 9 3 8 0 0 0 1 2",
"0 0 0 0 4 0 0 0 0",
"5 2 0 0 0 6 7 9 0",
"6 0 0 0 2 1 0 4 0",
"0 0 0 5 3 0 9 0 0",
"0 3 0 0 0 0 0 5 1");
public static final int SIZE = 9; // size of the whole 9x9 puzzle
public static final int PART = 3; // size of each 3x3 part
public static final int MAX_SOLUTIONS = 100;
// Provided various static utility methods to
// convert data formats to int[][] grid.
/**
* Returns a 2-d grid parsed from strings, one string per row.
* The "..." is a Java 5 feature that essentially
* makes "rows" a String[] array.
* (provided utility)
* @param rows array of row strings
* @return grid
*/
public static int[][] stringsToGrid(String... rows) {
int[][] result = new int[rows.length][];
for (int row = 0; row<rows.length; row++) {
result[row] = stringToInts(rows[row]);
}
return result;
}
/**
* Given a single string containing 81 numbers, returns a 9x9 grid.
* Skips all the non-numbers in the text.
* (provided utility)
* @param text string of 81 numbers
* @return grid
*/
public static int[][] textToGrid(String text) {
int[] nums = stringToInts(text);
if (nums.length != SIZE*SIZE) {
throw new RuntimeException("Needed 81 numbers, but got:" + nums.length);
}
int[][] result = new int[SIZE][SIZE];
int count = 0;
for (int row = 0; row<SIZE; row++) {
for (int col=0; col<SIZE; col++) {
result[row][col] = nums[count];
count++;
}
}
return result;
}
/**
* Given a string containing digits, like "1 23 4",
* returns an int[] of those digits {1 2 3 4}.
* (provided utility)
* @param string string containing ints
* @return array of ints
*/
public static int[] stringToInts(String string) {
int[] a = new int[string.length()];
int found = 0;
for (int i=0; i<string.length(); i++) {
if (Character.isDigit(string.charAt(i))) {
a[found] = Integer.parseInt(string.substring(i, i+1));
found++;
}
}
int[] result = new int[found];
System.arraycopy(a, 0, result, 0, found);
return result;
}
// Provided -- the deliverable main().
// You can edit to do easier cases, but turn in
// solving hardGrid.
public static void main(String[] args) {
Sudoku sudoku;
sudoku = new Sudoku(easyGrid);
System.out.println(sudoku); // print the raw problem
int count = sudoku.solve();
System.out.println("solutions:" + count);
System.out.println("elapsed:" + sudoku.getElapsed() + "ms");
System.out.println(sudoku.getSolutionText());
}
/**
* Sets up based on the given ints.
*/
private static final Set<Integer> generalSet = new HashSet<Integer>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
private int[][] grid;
private int[][] solution;
private Set<Integer>[] refSquares;
private Set<Integer>[] refRows;
private Set<Integer>[] refColumns;
private List<Spot> spots;
private boolean solutionFound;
private long elapsedTime;
public Sudoku(int[][] ints) {
grid = copyGrid(ints);
solution = copyGrid(ints);
solutionFound = false;
elapsedTime = 0;
generateRefRows();
generateRefColumns();
generateRefSquares();
generateSpots();
}
/**
* Solves the puzzle, invoking the underlying recursive search.
*
* wrapper function
*/
public int solve() {
elapsedTime = System.currentTimeMillis();
int solutions = recursiveSolve(new LinkedList<Spot>(spots));
elapsedTime = System.currentTimeMillis() - elapsedTime;
return solutions;
}
public String getSolutionText() {
StringBuffer buffer = new StringBuffer();
buffer.append(Arrays.toString(solution[0]));
for(int i = 1; i < solution.length; i++)
buffer.append("\n" + Arrays.toString(solution[i]));
String solutionText = new String(buffer);
// use a regular expression to strip the commas and brackets
return solutionText.replaceAll(",|\\[|\\]", "");
}
public long getElapsed() {
return elapsedTime;
}
// -------------------- Private ------------------- //
// generates a copy of the passed two-dimensional array
private int[][] copyGrid(int[][] arr) {
if(arr.length == 0) return null;
int[][] copy = new int[arr.length][arr[0].length];
for(int i = 0; i < arr.length; i++)
System.arraycopy(arr[i], 0, copy[i], 0, arr[i].length);
return copy;
}
// generates the spots for the particular grid
private void generateSpots() {
spots = new LinkedList<Spot>();
for(int row = 0; row < SIZE; row++)
for(int col = 0; col < SIZE; col++)
if(grid[row][col] == 0)
spots.add(new Spot(row, col));
Collections.sort(spots); // get the spots into the right order for the solve() method
}
// recursive helper method for the solve() method
private int recursiveSolve(LinkedList<Spot> workingSpots) {
// base case, we found a solution
if(workingSpots.isEmpty()) {
if(!solutionFound) {
solution = copyGrid(grid);
solutionFound = true;
}
return 1;
}
int numSolutions = 0;
Spot currentSpot = workingSpots.removeFirst();
Set<Integer> spotValues = currentSpot.possibleValues();
if(!spotValues.isEmpty()) {
// iterate through all of the possible values
Iterator<Integer> itr = spotValues.iterator();
while(itr.hasNext()) {
currentSpot.setValue(itr.next());
numSolutions += recursiveSolve(workingSpots);
currentSpot.reset(); // undo so we can test a different case
}
}
// put the spot back
workingSpots.addFirst(currentSpot);
return numSolutions;
}
// The following methods are only called during the instantiation of a Sudoku object.
private void generateRefRows() {
refRows = (Set<Integer>[]) new Set[SIZE];
for(int row = 0; row < SIZE; row++) {
refRows[row] = new HashSet<Integer>();
for(int col = 0; col < SIZE; col++)
if(grid[row][col] != 0 && !refRows[row].add(grid[row][col]))
throw new RuntimeException("Grid integrity exception generating reference rows");
}
}
private void generateRefColumns() {
refColumns = (Set<Integer>[]) new Set[SIZE];
for(int col = 0; col < SIZE; col++) {
refColumns[col] = new HashSet<Integer>();
for(int row = 0; row < SIZE; row++)
if(grid[row][col] != 0 && !refColumns[col].add(grid[row][col]))
throw new RuntimeException("Grid integrity exception generating reference columns");
}
}
private void generateRefSquares() {
int rowStart = 0;
int colStart = 0;
refSquares = (Set<Integer>[]) new Set[PART * PART];
for(int i = 0; i < PART * PART; i++) {
refSquares[i] = new HashSet<Integer>();
for(int row = rowStart; row < rowStart + PART; row++)
for(int col = colStart; col < colStart + PART; col++)
if(grid[row][col] != 0 && !refSquares[i].add(grid[row][col]))
throw new RuntimeException("Grid integrity exception generating reference squares");
rowStart += PART;
if(rowStart/PART % PART == 0) {
rowStart = 0;
colStart +=PART;
}
}
}
// --------------- Inner spot class --------------- //
private class Spot implements Comparable<Sudoku.Spot>{
private int row;
private int col;
private int square;
// constructor
public Spot(int row, int col) {
this.row = row;
this.col = col;
computeSquare();
}
// returns the spot's row
public int getRow() {
return row;
}
// returns the spot's column
public int getColumn() {
return col;
}
// returns the square the spot resides in
public int getSquare() {
return square;
}
// returns the spot's value
public int getValue() {
return grid[row][col];
}
// sets the spot's value
public void setValue(int value) {
refSquares[square].add(value);
refColumns[col].add(value);
refRows[row].add(value);
grid[row][col] = value;
}
// resets the spot to an empty state, re-adding the contained value
// to the appropriate row, column and square
public void reset() {
if(isEmpty()) return;
refSquares[square].remove(getValue());
refColumns[col].remove(getValue());
refRows[row].remove(getValue());
grid[row][col] = 0;
}
// returns if the current spot is empty
public boolean isEmpty() {
return (grid[row][col] == 0);
}
// returns a set containing all of the possible values
public Set<Integer> possibleValues() {
HashSet<Integer> possibleValues = new HashSet<Integer>(generalSet);
possibleValues.removeAll(refSquares[square]);
possibleValues.removeAll(refRows[row]);
possibleValues.removeAll(refColumns[col]);
return possibleValues;
}
public int compareTo(Sudoku.Spot spot) {
return possibleValues().size() - spot.possibleValues().size();
}
// --------- Private -------- //
// computes the square the spot resides in
private void computeSquare() {
int modifier = 2;
if(row < PART) modifier = 0;
else if(row < PART * 2) modifier = 1;
square = (col / PART) * PART + modifier;
}
}
}