init commit (imported from personal account)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
53..7....
|
||||
6..195...
|
||||
.98....6.
|
||||
8...6...3
|
||||
4..8.3..1
|
||||
7...2...6
|
||||
.6....28.
|
||||
...419..5
|
||||
....8..79
|
||||
@@ -0,0 +1,9 @@
|
||||
.9...2.7.
|
||||
.....3..6
|
||||
..196..8.
|
||||
4......1.
|
||||
.....6...
|
||||
.3.75...8
|
||||
...2.....
|
||||
.7.89...5
|
||||
..3...7..
|
||||
@@ -0,0 +1,9 @@
|
||||
8.14.....
|
||||
.4.83...6
|
||||
6..5921.4
|
||||
27.1.9538
|
||||
...6..4.9
|
||||
59.3...7.
|
||||
.2.....65
|
||||
36.95..1.
|
||||
........3
|
||||
+330
@@ -0,0 +1,330 @@
|
||||
package src;
|
||||
|
||||
/**
|
||||
* A Cell represents a single cell in the Sudoku grid.
|
||||
* This helper class is used to store the row, column, value, and possible values for a cell.
|
||||
* The possible values are stored in an ArrayList of Integers, and are sorted in ascending order.
|
||||
*
|
||||
* The Cell class also contains a method to add a possible value to the cell, and a method to get the possible values.
|
||||
*/
|
||||
public class Cell {
|
||||
private int row;
|
||||
private int col;
|
||||
private int box;
|
||||
private int value;
|
||||
private List possibleValues;
|
||||
|
||||
/**
|
||||
* Create a new Cell with the given row and column.
|
||||
*
|
||||
* The value of the cell is initially -2, and the possible values are initially empty.
|
||||
*
|
||||
* @param row
|
||||
* @param col
|
||||
*/
|
||||
public Cell(int row, int col) {
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
setBox();
|
||||
possibleValues = new List();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Cell with the given row, column, and value.
|
||||
*
|
||||
* The possible values are initially empty.
|
||||
*
|
||||
* @param row
|
||||
* @param col
|
||||
* @param value
|
||||
*/
|
||||
public Cell(int row, int col, int value) {
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
this.value = value;
|
||||
setBox();
|
||||
possibleValues = new List();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Cell with the given row, column, and possible values.
|
||||
*
|
||||
* The value of the cell is initially -2.
|
||||
*
|
||||
* @param row
|
||||
* @param col
|
||||
* @param possibleValues
|
||||
*/
|
||||
public Cell(int row, int col, int[] possibleValues) {
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
setBox();
|
||||
this.possibleValues = new List(possibleValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a cell.
|
||||
*
|
||||
* The value must be between 1 and 9, inclusive.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public void setValue(int value) {
|
||||
possibleValues.clear();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a cell.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the box a cell is in.
|
||||
*
|
||||
* @return box number
|
||||
*/
|
||||
public int getBox() {
|
||||
return box;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to the list of possible values.
|
||||
*
|
||||
* If the value is already possible for the cell, or if the value is not a valid value for the cell, then the value is not added.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public void addPossibleValue(int value) {
|
||||
if(possibleValues.contains(value)) {
|
||||
return;
|
||||
} else if(value < 1 || value > 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
possibleValues.add(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the list of possible values for the cell.
|
||||
*
|
||||
* @param possibleValues
|
||||
*/
|
||||
public void setPossibleValues(int[] possibleValues) {
|
||||
this.possibleValues = new List(possibleValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a value from the list of possible values.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public void removePossibleValue(int value) {
|
||||
if(!possibleValues.contains(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
possibleValues.remove(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of possible values for the cell.
|
||||
*
|
||||
* This list is a copy of the cell's possible values, and is sorted in ascending order.
|
||||
*
|
||||
* @return list of possible values for the cell
|
||||
*/
|
||||
public int[] getPossibleValues() {
|
||||
return possibleValues.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the cell's row and column, set the box number for the cell.
|
||||
*/
|
||||
private void setBox() {
|
||||
int boxRow = row / 3;
|
||||
int boxCol = col / 3;
|
||||
box = boxRow * 3 + boxCol;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to store the possible values for a cell in a sorted list.
|
||||
* The list is sorted in ascending order, and the values are stored in a linked list.
|
||||
*
|
||||
* The List class also contains a method to add a value to the list, and a method to get the list of values as an array.
|
||||
*/
|
||||
private class List {
|
||||
|
||||
private Value head;
|
||||
private Value tail;
|
||||
private int size;
|
||||
private int max;
|
||||
private int min;
|
||||
|
||||
/**
|
||||
* Create a new empty List.
|
||||
*
|
||||
* By default, the head and tail are null, the size is 0, and the max and min are 0.
|
||||
*/
|
||||
public List() {
|
||||
head = null;
|
||||
tail = null;
|
||||
size = 0;
|
||||
max = 0;
|
||||
min = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new List with the given initial value.
|
||||
*
|
||||
* By default, the head and tail are the same, the size is 1, and the max and min are the initial value.
|
||||
* @param initValue
|
||||
*/
|
||||
public List(int initValue) {
|
||||
head = new Value(initValue);
|
||||
tail = head;
|
||||
size = 1;
|
||||
max = initValue;
|
||||
min = initValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new List with the given initial values.
|
||||
*
|
||||
* @param initValues
|
||||
*/
|
||||
public List(int[] initValues) {
|
||||
for(int i = 0; i < initValues.length; i++) add(initValues[i]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new value to the list.
|
||||
*
|
||||
* By default, the value will be inserted at it's sorted place in the list.
|
||||
*
|
||||
* @param newValue
|
||||
*/
|
||||
public void add(int newValue) {
|
||||
if(size == 0) {
|
||||
head = new Value(newValue);
|
||||
tail = head;
|
||||
size = 1;
|
||||
max = newValue;
|
||||
min = newValue;
|
||||
} else {
|
||||
if(newValue > max) {
|
||||
max = newValue;
|
||||
tail.next = new Value(newValue);
|
||||
tail = tail.next;
|
||||
} else if(newValue < min) {
|
||||
min = newValue;
|
||||
Value tmp = new Value(newValue);
|
||||
tmp.setNext(head);
|
||||
head = tmp;
|
||||
} else {
|
||||
Value current = head;
|
||||
while(current.next != null && current.next.value < newValue) {
|
||||
current = current.next;
|
||||
}
|
||||
Value tmp = new Value(newValue);
|
||||
tmp.setNext(current.next);
|
||||
current.setNext(tmp);
|
||||
}
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a value from the list.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public void remove(int value) {
|
||||
if(size == 0) {
|
||||
System.out.println("List is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(head.value == value) {
|
||||
head = head.next;
|
||||
size--;
|
||||
return;
|
||||
}
|
||||
|
||||
Value current = head;
|
||||
while(current.next != null) {
|
||||
if(current.next.value == value) {
|
||||
current.setNext(current.next.next);
|
||||
size--;
|
||||
return;
|
||||
}
|
||||
current = current.next;
|
||||
}
|
||||
|
||||
System.out.println("Value " + value + " is not in the list.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of values as an array.
|
||||
*
|
||||
* @return array of values in the list
|
||||
*/
|
||||
public int[] toArray() {
|
||||
int[] array = new int[size];
|
||||
Value current = head;
|
||||
for(int i = 0; i < size; i++) {
|
||||
array[i] = current.value;
|
||||
current = current.next;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the list contains the given value.
|
||||
*
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public boolean contains(int value) {
|
||||
Value current = head;
|
||||
while(current != null) {
|
||||
if(current.value == value) return true;
|
||||
current = current.next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the list of all values.
|
||||
*/
|
||||
public void clear() {
|
||||
head = null;
|
||||
tail = null;
|
||||
size = 0;
|
||||
max = 0;
|
||||
min = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value class is a Node for the List class.
|
||||
*/
|
||||
private class Value {
|
||||
|
||||
private int value;
|
||||
private Value next;
|
||||
|
||||
public Value(int value) {
|
||||
this.value = value;
|
||||
next = null;
|
||||
}
|
||||
|
||||
public void setNext(Value next) {
|
||||
this.next = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
package src;
|
||||
|
||||
import java.util.Scanner;
|
||||
import java.io.File;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JComboBox;
|
||||
|
||||
/**
|
||||
* The Nav class represents the Navigation bar at the top of the JFrame window.
|
||||
*
|
||||
* The Nav class is responsible for basic File IO operations. The most basic
|
||||
* functionality is the following:
|
||||
* - creating a new, completely empty .sdku file for a user to create
|
||||
* their own puzzle with;
|
||||
* - open an existing .sdku file in the user's file system (a standard
|
||||
* or default location is TBD);
|
||||
* - save the currently open .sdku file with any changes the user has made.
|
||||
*
|
||||
* In the future, the Nav class would ideally incorporate additional elements,
|
||||
* such as the difficulty of the current problem, providing hints, displaying
|
||||
* the length of time since a puzzle was started, etc.
|
||||
*/
|
||||
public class Nav extends JPanel {
|
||||
private Settings s;
|
||||
private File f;
|
||||
private Cell[][] grid;
|
||||
|
||||
/**
|
||||
* Create a new Nav in a command-line interface. This is intended for
|
||||
* debugging rather than practical use.
|
||||
*
|
||||
* Additionally, even for debugging, this should be used since default
|
||||
* file I/O behavior is dictated by the values in the Settings object.
|
||||
*/
|
||||
public Nav() {
|
||||
System.out.println("Welcome to the Sudoku Solver!");
|
||||
|
||||
Scanner in = new Scanner(System.in);
|
||||
System.out.print(
|
||||
"Enter the name of the input file (do not " +
|
||||
"include the file extension): "
|
||||
);
|
||||
String inputFilename = in.nextLine();
|
||||
in.close();
|
||||
|
||||
open(inputFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Nav object for use in the command-line. This is intended
|
||||
* for debugging rather than practical use.
|
||||
*
|
||||
* @param Settings
|
||||
*/
|
||||
public Nav(Settings s) {
|
||||
this.s = s;
|
||||
System.out.println("Welcome to the Sudoku Solver!");
|
||||
|
||||
Scanner in = new Scanner(System.in);
|
||||
System.out.print(
|
||||
"Enter the name of the input file (do not " +
|
||||
"include the file extension): "
|
||||
);
|
||||
String inputFilename = in.nextLine();
|
||||
in.close();
|
||||
|
||||
open(inputFilename);
|
||||
}
|
||||
|
||||
private void new() {
|
||||
int default = s.getNewFileProperties();
|
||||
|
||||
// See Settings.getNewFileProperties for possible values and
|
||||
// the expected behavior of each value.
|
||||
switch(default) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
default:
|
||||
}
|
||||
// TODO: Create a new .sdku file in the default directory.
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an input filename, open the file and populate the grid.
|
||||
*
|
||||
* @param String
|
||||
*/
|
||||
private void open(String filename) {
|
||||
File f = new File(/*"spring24/sudoku/" + */filename + ".sdku");
|
||||
if(f == null || !f.exists() || f.isDirectory() || !f.canRead()) {
|
||||
System.out.println(
|
||||
"The file " + filename +
|
||||
".sdku does not exist or cannot be read."
|
||||
);
|
||||
|
||||
System.out.println("The exact path to the file is: " +
|
||||
f.getAbsolutePath()
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
createGrid(f);
|
||||
}
|
||||
|
||||
private void save() {
|
||||
// TODO: Get the current Cell[][] from the Board JPanel
|
||||
// TODO: Write to a file.
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently loaded Cell[][] grid.
|
||||
*
|
||||
* @return the grid
|
||||
*/
|
||||
public Cell[][] getLoadedGrid() {
|
||||
return grid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the loaded grid with the current version with any changes the
|
||||
* user has made.
|
||||
*
|
||||
* For future auto-save functionality, this could simply be called after
|
||||
* each change the user makes on the Sudoku board.
|
||||
*
|
||||
* In the future, an optimization could be made where a different data
|
||||
* structure is used to pass only the changes the player has made within
|
||||
* the last x minutes, for eg. {row, col, value, noteValues[]}. Given
|
||||
* the size of a standard Sudoku board however, this may be an irrelevant
|
||||
* and unnecessary optimization.
|
||||
*
|
||||
* @param Cell[][]
|
||||
*/
|
||||
public void updateLoadedGrid(Cell[][] grid) {
|
||||
this.grid = grid;
|
||||
}
|
||||
|
||||
/**
|
||||
* To be called when a File is opened or created. This will populate the
|
||||
* Cell[][] grid with values read in from the file.
|
||||
*
|
||||
* @param File
|
||||
*/
|
||||
private void createGrid(File f) {
|
||||
try (Scanner in = new Scanner(f)) {
|
||||
Cell[][] grid = new Cell[9][9];
|
||||
for(int i = 0; i < 9; i++) {
|
||||
String line = in.nextLine();
|
||||
for(int j = 0; j < 9; j++) {
|
||||
if(line.charAt(j) == '.') {
|
||||
grid[i][j] = new Cell(i, j, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
int value = Character.getNumericValue(line.charAt(j));
|
||||
grid[i][j] = new Cell(i, j, value);
|
||||
}
|
||||
}
|
||||
|
||||
this.grid = grid;
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println("An error occurred while reading the file " +
|
||||
filename + ".sdku."
|
||||
);
|
||||
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package src;
|
||||
import java.util.Scanner;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* A class solely to store user settings relating to default behaviors,
|
||||
* difficulty, filepaths, and more.
|
||||
*
|
||||
* These user settings are stored in a {TBD} file located at {TBD}.
|
||||
*/
|
||||
public class Settings {
|
||||
private int newFileProperties;
|
||||
private String defaultDirectory;
|
||||
|
||||
public Settings() {
|
||||
// TODO: Read in a file from the master default directory and populate
|
||||
// settings, effectively toggles. If the file does not exist,
|
||||
// populate it with the following values (possibly in .json).
|
||||
newFileProperties = 0;
|
||||
defaultDirectory = "~/AppData/Local/sudoku/";
|
||||
}
|
||||
|
||||
/**
|
||||
* New File Properties is a configurable setting, where the following
|
||||
* values are allowable:
|
||||
* 0 - Load the last opened .sdku file, default behavior
|
||||
* 1 - Create a new, blank .sdku file
|
||||
* 2 - Create a new, random .sdku file
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getNewFileProperties() {
|
||||
return newFileProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Default Directory is a configurable setting, where the user can
|
||||
* specify a default directory to store .sdku files. This is useful for
|
||||
* eliminating the need for user input for file operations.
|
||||
*
|
||||
* By default, the value will be "~/.config/sudoku/" on MacOS and Linux.
|
||||
* On Windows, the value will be "~/AppData/Local/sudoku/".
|
||||
*
|
||||
* NOTE: This does not change the location that the settings file may
|
||||
* be located in. This must be located at {TBD}.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getDefaultDirectory() {
|
||||
return defaultDirectory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
package src;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Scanner;
|
||||
|
||||
|
||||
/**
|
||||
* A Sudoku puzzle is a 9x9 grid of numbers, where each row, column, and 3x3 subgrid contains the numbers 1-9 exactly once.
|
||||
*
|
||||
* The goal of this challenge is to write a program that can solve Sudoku puzzles.
|
||||
*
|
||||
* Given an input .sdku file (a plain-text file containing the initial state of a Sudoku puzzle), find the solution to the puzzle and write the solution to a new .sdku file.
|
||||
*
|
||||
* The input file will contain 9 lines, each containing 9 characters. Each character will be a digit (1-9) or a period (.), representing an empty cell.
|
||||
*
|
||||
* The output file should contain the same 9x9 grid, with the empty cells filled in with the correct numbers.
|
||||
*
|
||||
* For example, given the following input file:
|
||||
*
|
||||
* 53..7....
|
||||
* 6..195...
|
||||
* .98....6.
|
||||
* 8...6...3
|
||||
* 4..8.3..1
|
||||
* 7...2...6
|
||||
* .6....28.
|
||||
* ...419..5
|
||||
* ....8..79
|
||||
*
|
||||
* The output file should be:
|
||||
*
|
||||
* 534678912
|
||||
* 672195348
|
||||
* 198342567
|
||||
* 859761423
|
||||
* 426853791
|
||||
* 713924856
|
||||
* 961537284
|
||||
* 287419635
|
||||
* 345286179
|
||||
*
|
||||
* This challenge is broken up into parts. In Programming Club, we will split into teams of 3-4 people to work on each method.
|
||||
*/
|
||||
public class SudokuChecker {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Cell[][] grid = readPuzzle(inputFilename);
|
||||
solve(grid);
|
||||
System.out.println("The solution to the Sudoku puzzle is:");
|
||||
for(int i = 0; i < 9; i++) {
|
||||
for(int j = 0; j < 9; j++) {
|
||||
System.out.print(grid[i][j].getValue());
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
//app.saveSolution(outputFilename, grid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an input filename, read the Sudoku puzzle from the file and return it as a 9x9 grid numbers, with no value for spaces in the file.
|
||||
*
|
||||
* @param filename
|
||||
* @return
|
||||
*/
|
||||
public Cell[][] readPuzzle(String filename) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an output filename and a 9x9 grid of numbers, write the Sudoku puzzle to the file.
|
||||
*
|
||||
* @param grid
|
||||
* @return
|
||||
*/
|
||||
public void saveSolution(String filename, Cell[][] grid) {
|
||||
System.out.println("TODO: Write sudoku puzzle solution to " + filename + ".sdku");
|
||||
// TODO: Account for possible invalid solution from isSolved and handle an error.
|
||||
// TODO: Write the solution to the file.
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two arrays of numbers, return the intersection of the two arrays.
|
||||
*
|
||||
* @param a
|
||||
* @param b
|
||||
* @return
|
||||
*/
|
||||
private ArrayList<Integer> intersection(ArrayList<Integer> a, ArrayList<Integer> b) {
|
||||
ArrayList<Integer> intersection = new ArrayList<Integer>();
|
||||
for(int i = 0; i < a.size(); i++) {
|
||||
if(b.contains(a.get(i))) {
|
||||
intersection.add(a.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine what numbers are available to be placed in the given cell of the grid.
|
||||
* This is effectively an intersection of the numbers available in the row, column, and box of the cell.
|
||||
*
|
||||
* @param grid
|
||||
* @param row
|
||||
* @param col
|
||||
* @return an array of numbers that are available to be placed in the given cell
|
||||
*/
|
||||
private void getAvailableNumbers(Cell[][] grid, int row, int col) {
|
||||
ArrayList<Integer> intersection = intersection(getRowRemainingNumbers(grid, row), getColRemainingNumbers(grid, col));
|
||||
intersection = intersection(intersection, getBoxRemainingNumbers(grid, row, col));
|
||||
|
||||
if(intersection.size() == 0) {
|
||||
return;
|
||||
} else if(intersection.size() == 1) {
|
||||
int value = intersection.get(0);
|
||||
|
||||
grid[row][col].setValue(value);
|
||||
updatePossibleValues(grid, row, col);
|
||||
return;
|
||||
} else {
|
||||
// Join the arrays and find the intersection of the three arrays.
|
||||
ArrayList<Integer> availableNumbers = new ArrayList<Integer>();
|
||||
for(int i = 0; i < intersection.size(); i++) {
|
||||
availableNumbers.add(intersection.get(i));
|
||||
}
|
||||
|
||||
grid[row][col].setPossibleValues(arrayListToArray(availableNumbers));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the possible values for cells in the same row, column, and box as the given cell.
|
||||
* This should always be called once a cell's value has been set, to remove that value from the possible values of other cells.
|
||||
*
|
||||
* @param grid
|
||||
* @param row
|
||||
* @param col
|
||||
*/
|
||||
private void updatePossibleValues(Cell[][] grid, int row, int col) {
|
||||
int value = grid[row][col].getValue();
|
||||
|
||||
for(int i = 0; i < 9; i++) {
|
||||
if(grid[row][i].getValue() == 0) {
|
||||
grid[row][i].removePossibleValue(value);
|
||||
if(grid[row][i].getPossibleValues().length == 1) {
|
||||
grid[row][i].setValue(grid[row][i].getPossibleValues()[0]);
|
||||
updatePossibleValues(grid, row, i);
|
||||
}
|
||||
}
|
||||
if(grid[i][col].getValue() == 0) {
|
||||
grid[i][col].removePossibleValue(value);
|
||||
if(grid[i][col].getPossibleValues().length == 1) {
|
||||
grid[i][col].setValue(grid[i][col].getPossibleValues()[0]);
|
||||
updatePossibleValues(grid, i, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int boxRow = row / 3;
|
||||
int boxCol = col / 3;
|
||||
for(int i = 0; i < 3; i++) {
|
||||
for(int j = 0; j < 3; j++) {
|
||||
if(grid[boxRow * 3 + i][boxCol * 3 + j].getValue() == 0) {
|
||||
grid[boxRow * 3 + i][boxCol * 3 + j].removePossibleValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Solve the Sudoku puzzle.
|
||||
*
|
||||
* @param grid
|
||||
*/
|
||||
public void solve(Cell[][] grid) {
|
||||
// Continue until a valid solution is reached.
|
||||
while(!isValidSolution(grid)) {
|
||||
for(int i = 0; i < 9; i++) {
|
||||
for(int j = 0; j < 9; j++) {
|
||||
if(grid[i][j].getValue() == 0) {
|
||||
getAvailableNumbers(grid, i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
/**
|
||||
* Get any number between 1 and 9 that is not in the row.
|
||||
*
|
||||
* @param grid
|
||||
* @param row
|
||||
* @return
|
||||
*/
|
||||
private ArrayList<Integer> getRowRemainingNumbers(Cell[][] grid, int row) {
|
||||
ArrayList<Integer> remainingNumbers = new ArrayList<Integer>();
|
||||
for(int i = 1; i <= 9; i++) {
|
||||
boolean found = false;
|
||||
for(int j = 0; j < 9; j++) {
|
||||
if(grid[row][j].getValue() == i) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
remainingNumbers.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return remainingNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any number between 1 and 9 that is not in the column.
|
||||
*
|
||||
* @param grid
|
||||
* @param col
|
||||
* @return
|
||||
*/
|
||||
private ArrayList<Integer> getColRemainingNumbers(Cell[][] grid, int col) {
|
||||
ArrayList<Integer> remainingNumbers = new ArrayList<Integer>();
|
||||
for(int i = 1; i <= 9; i++) {
|
||||
boolean found = false;
|
||||
for(int j = 0; j < 9; j++) {
|
||||
if(grid[j][col].getValue() == i) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
remainingNumbers.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return remainingNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get any number between 1 and 9 that is not in the box.
|
||||
*
|
||||
* A box is a 3x3 subgrid of the 9x9 grid.
|
||||
*
|
||||
* @param grid
|
||||
* @param box
|
||||
* @return
|
||||
*/
|
||||
private ArrayList<Integer> getBoxRemainingNumbers(Cell[][] grid, int row, int col) {
|
||||
ArrayList<Integer> remainingNumbers = new ArrayList<Integer>();
|
||||
int boxRow = row / 3;
|
||||
int boxCol = col / 3;
|
||||
for(int i = 1; i <= 9; i++) {
|
||||
boolean found = false;
|
||||
for(int j = 0; j < 3; j++) {
|
||||
for(int k = 0; k < 3; k++) {
|
||||
if(grid[boxRow * 3 + j][boxCol * 3 + k].getValue() == i) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
remainingNumbers.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return remainingNumbers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of numbers, return an array of the numbers.
|
||||
*
|
||||
* A helper method to keep the code using arrays instead of lists whenever possible.
|
||||
*
|
||||
* @param list
|
||||
* @return
|
||||
*/
|
||||
private int[] arrayListToArray(ArrayList<Integer> list) {
|
||||
int[] array = new int[list.size()];
|
||||
for(int i = 0; i < list.size(); i++) {
|
||||
array[i] = list.get(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a 9x9 grid of numbers, return true if the grid is a valid Sudoku puzzle solution, and false otherwise.
|
||||
*
|
||||
* A valid Sudoku puzzle is one where each row, column, and 3x3 subgrid contains the numbers 1-9 exactly once.
|
||||
*
|
||||
* @param grid a 9x9 grid of numbers
|
||||
* @return true if the grid is a valid Sudoku puzzle, and false otherwise
|
||||
*/
|
||||
private boolean isValidSolution(Cell[][] grid) {
|
||||
// Check that every cell has a value between 1 and 9.
|
||||
for(int i = 0; i < 9; i++) {
|
||||
for(int j = 0; j < 9; j++) {
|
||||
if(grid[i][j].getValue() < 1 || grid[i][j].getValue() > 9) {
|
||||
//System.out.println("Cell at row " + i + " and column " + j + " has an invalid value of " + grid[i][j].getValue() + ".");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that every row contains the numbers 1-9 exactly once.
|
||||
for(int i = 0; i < 9; i++) {
|
||||
int[] row = new int[9];
|
||||
for(int j = 0; j < 9; j++) {
|
||||
row[j] = grid[i][j].getValue();
|
||||
}
|
||||
if(!isValidSet(row)) {
|
||||
System.out.println("Row " + i + " is invalid.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that every column contains the numbers 1-9 exactly once.
|
||||
for(int i = 0; i < 9; i++) {
|
||||
int[] col = new int[9];
|
||||
for(int j = 0; j < 9; j++) {
|
||||
col[j] = grid[j][i].getValue();
|
||||
}
|
||||
if(!isValidSet(col)) {
|
||||
System.out.println("Column " + i + " is invalid.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that every 3x3 subgrid contains the numbers 1-9 exactly once.
|
||||
for(int i = 0; i < 3; i++) {
|
||||
for(int j = 0; j < 3; j++) {
|
||||
int[] box = new int[9];
|
||||
for(int k = 0; k < 3; k++) {
|
||||
for(int l = 0; l < 3; l++) {
|
||||
box[k * 3 + l] = grid[i * 3 + k][j * 3 + l].getValue();
|
||||
}
|
||||
}
|
||||
if(!isValidSet(box)) {
|
||||
System.out.println("Box at row " + i + " and column " + j + " is invalid.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of 9 numbers, return true if the array contains the numbers 1-9 exactly once, and false otherwise.
|
||||
*
|
||||
* @param set an array of 9 numbers
|
||||
* @return true if the array contains the numbers 1-9 exactly once, and false otherwise
|
||||
*/
|
||||
private boolean isValidSet(int[] set) {
|
||||
boolean[] found = new boolean[9];
|
||||
for(int i = 0; i < 9; i++) {
|
||||
if(set[i] < 1 || set[i] > 9) {
|
||||
return false;
|
||||
} else if(found[set[i] - 1]) {
|
||||
return false;
|
||||
} else {
|
||||
found[set[i] - 1] = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
javac *.java
|
||||
java Sudoku
|
||||
rm *.class
|
||||
Reference in New Issue
Block a user