documentation and formatting to 80 character width.

This commit is contained in:
Josh Ashton
2024-03-21 14:50:40 -06:00
parent ac1464a9fb
commit 74346a761c
7 changed files with 176 additions and 113 deletions
View File
View File
+12 -5
View File
@@ -8,7 +8,8 @@ import java.awt.BorderLayout;
import java.awt.Dimension;
/**
* Runner class for the Sudoku App, managing the GUI and coordinating internal logic.
* Runner class for the Sudoku App, managing the GUI and coordinating
* internal logic.
*/
public class App extends JFrame {
// GUI fields
@@ -33,7 +34,8 @@ public class App extends JFrame {
* Basic JFrame settings and setup for the GUI.
*/
private void initSetup() {
// TODO: These are just some default values, these should use the values from Settings.
// TODO: These are just some default values, these should use the
// values from Settings.
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setPreferredSize(new Dimension(750, 550));
@@ -42,9 +44,13 @@ public class App extends JFrame {
setLocationRelativeTo(null);
try {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
UIManager.setLookAndFeel(
UIManager.getCrossPlatformLookAndFeelClassName()
);
} catch (Exception e) {
System.out.println("Error with cross platform look/feel in frameSetup().");
System.out.println(
"Error with cross platform look/feel in frameSetup()."
);
}
}
@@ -76,7 +82,8 @@ public class App extends JFrame {
public static void main(String[] args) {
if(args.length == 0)
new App().setVisible(true);
else if(args[0].equals("-c") || args[0].equals("--cli"))
else if(args[0].equals("-c") ||
args[0].equals("--cli"))
cli();
else
new App().setVisible(true);
+29 -11
View File
@@ -33,7 +33,7 @@ public class Nav extends JPanel {
* 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
* Additionally, even for debugging, this should not be used since default
* file I/O behavior is dictated by the values in the Settings object.
*/
public Nav() {
@@ -61,6 +61,8 @@ public class Nav extends JPanel {
super();
this.s = s;
// If the program is being run in the command-line, prompt the user
// for the name of the input file.
if(cli) {
System.out.println("Welcome to the Sudoku Solver!");
@@ -73,10 +75,10 @@ public class Nav extends JPanel {
in.close();
openFile(inputFilename);
} else {
// TODO: Create the GUI for the Nav bar.
createGUI();
}
// Otherwise, create the GUI for the Nav bar.
} else createGUI();
}
/**
@@ -92,6 +94,8 @@ public class Nav extends JPanel {
JButton solve = new JButton("Solve");
solve.setFont(s.getFont()); // TODO: Make custom Button class.
// Add the ComboBox for the file options, the elapsed time label, and
// the solve button to the Nav Panel.
add(createFileOptions());
add(elapsedTime);
add(solve);
@@ -103,12 +107,14 @@ public class Nav extends JPanel {
* @return ComboBox<String>
*/
private ComboBox<String> createFileOptions() {
// Create a new ComboBox with the default styling.
ComboBox<String> fileOptions = new ComboBox<>(s);
fileOptions.addItem("New");
fileOptions.addItem("Open");
fileOptions.addItem("Save");
fileOptions.addItem("Exit");
// Add an ActionListener to the ComboBox to handle the user's selection.
fileOptions.addActionListener(e -> {
String selected = (String) fileOptions.getSelectedItem();
switch(selected) {
@@ -154,19 +160,19 @@ public class Nav extends JPanel {
* Open an existing .sdku file in the user's file system, using a GUI.
*/
private void openFile() {
// No error handling is needed here, since the Settings class ensures
// that the default directory is always a valid directory.
File defaultDirectory = new File(s.getDefaultDirectory());
if(defaultDirectory != null || !defaultDirectory.isDirectory()) {
defaultDirectory.mkdirs();
}
FileChooser fc = new FileChooser(defaultDirectory);
// Custom FileChooser class for style and to ensure the user can only
// select .sdku files.
FileChooser fc = new FileChooser(defaultDirectory, s);
int result = fc.showOpenDialog(this);
// If the user selects a file, open it.
if(result == FileChooser.APPROVE_OPTION) {
f = fc.getSelectedFile();
createGrid(f);
} else {
System.out.println("No file was selected.");
}
}
@@ -179,6 +185,7 @@ public class Nav extends JPanel {
*/
private void openFile(String filename) {
File f = new File("resources/" + filename + ".sdku");
// If the file does not exist, print an error message and return.
if(f == null || !f.exists() || f.isDirectory() || !f.canRead()) {
System.out.println(
"The file " + filename +
@@ -192,6 +199,7 @@ public class Nav extends JPanel {
return;
}
// Otherwise, open the file and populate the grid.
System.out.println("Opening the file " + filename + ".sdku.");
createGrid(f);
}
@@ -241,21 +249,31 @@ public class Nav extends JPanel {
*/
private void createGrid(File f) {
try (Scanner in = new Scanner(f)) {
// Create a new 9x9 grid of Cells.
Cell[][] grid = new Cell[9][9];
// Read in the file line by line.
for(int i = 0; i < 9; i++) {
// Read in the line and create a new Cell for each character.
String line = in.nextLine();
for(int j = 0; j < 9; j++) {
// If the character is a period, the cell is empty.
if(line.charAt(j) == '.') {
grid[i][j] = new Cell(i, j, 0);
continue;
}
// Otherwise, the character is a digit.
int value = Character.getNumericValue(line.charAt(j));
grid[i][j] = new Cell(i, j, value);
}
}
this.grid = grid;
// If the file is not found, print an error message and return.
} catch (FileNotFoundException e) {
String filename = f.getName();
System.err.println("An error occurred while reading the file " +
+64 -38
View File
@@ -1,35 +1,40 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Scanner;
/**
* The SudokuChecker class is responsible for checking the validity of a given Sudoku puzzle solution.
* The SudokuChecker class is responsible for calculating the solution to
* a given Sudoku puzzle.
*
* The SudokuChecker class is also responsible for solving a given Sudoku puzzle.
* TODO: The Sudoku algorithm used is efficient and effective for solving
* easy and medium puzzles, but it is not optimized for hard puzzles. It
* is recommended to use a different algorithm for hard puzzles, likely
* a tree & back-track approach.
*/
public class SudokuChecker {
private Cell[][] grid;
/**
* Create a new SudokuChecker object, initializing the grid to the given 9x9 grid of numbers.
* This should either check each cell as the user inputs a value, or be used to check the validity of a
* puzzle when the user requests it.
* Create a new SudokuChecker object, initializing the grid to the given
* 9x9 grid of numbers. This should either check each cell as the user
* inputs a value, or be used to check the validity of a puzzle when the
* user requests it.
*
* TODO: Currently, this class is not used in the GUI. It is only used in
* the command-line interface.
*
* TODO: Currently, this class is not used in the GUI. It is only used in the command-line interface.
* @param grid
*/
public SudokuChecker(Cell[][] grid) {
this.grid = grid;
solve();
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();
}
}
/**
* Get the solution to the Sudoku puzzle.
*
* @return Cell[][]
*/
public Cell[][] getSolution() {
return grid;
}
/**
@@ -39,7 +44,10 @@ public class SudokuChecker {
* @param b
* @return
*/
private ArrayList<Integer> intersection(ArrayList<Integer> a, ArrayList<Integer> b) {
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))) {
@@ -51,16 +59,25 @@ public class SudokuChecker {
}
/**
* 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.
* 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 row
* @param col
* @return an array of numbers that are available to be placed in the given cell
* @return an array of numbers that are available to be placed in the
* given cell
*/
private void getAvailableNumbers(int row, int col) {
ArrayList<Integer> intersection = intersection(getRowRemainingNumbers(row), getColRemainingNumbers(col));
intersection = intersection(intersection, getBoxRemainingNumbers(row, col));
ArrayList<Integer> intersection = intersection(
getRowRemainingNumbers(row),
getColRemainingNumbers(col)
);
intersection = intersection(
intersection,
getBoxRemainingNumbers(row, col)
);
if(intersection.size() == 0) {
return;
@@ -77,13 +94,16 @@ public class SudokuChecker {
availableNumbers.add(intersection.get(i));
}
grid[row][col].setPossibleValues(arrayListToArray(availableNumbers));
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.
* 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 row
* @param col
@@ -112,9 +132,9 @@ public class SudokuChecker {
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);
}
if(grid[boxRow * 3 + i][boxCol * 3 + j].getValue() == 0)
grid[boxRow * 3 + i][boxCol * 3 + j].
removePossibleValue(value);
}
}
}
@@ -218,7 +238,8 @@ public class SudokuChecker {
/**
* 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.
* A helper method to keep the code using arrays instead of lists
* whenever possible.
*
* @param list
* @return
@@ -232,9 +253,11 @@ public class SudokuChecker {
}
/**
* Given a 9x9 grid of numbers, return true if the grid is a valid Sudoku puzzle solution, and false otherwise.
* 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.
* A valid Sudoku puzzle is one where each row, column, and 3x3 subgrid
* contains the numbers 1-9 exactly once.
*
* @return true if the grid is a valid Sudoku puzzle, and false otherwise
*/
@@ -242,10 +265,8 @@ public class SudokuChecker {
// 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() + ".");
if(grid[i][j].getValue() < 1 || grid[i][j].getValue() > 9)
return false;
}
}
}
@@ -283,7 +304,10 @@ public class SudokuChecker {
}
}
if(!isValidSet(box)) {
System.out.println("Box at row " + i + " and column " + j + " is invalid.");
System.out.println(
"Box at row " + i +
" and column " + j + " is invalid."
);
return false;
}
}
@@ -293,10 +317,12 @@ public class SudokuChecker {
}
/**
* Given an array of 9 numbers, return true if the array contains the numbers 1-9 exactly once, and false otherwise.
* 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
* @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];
+2 -17
View File
@@ -11,22 +11,6 @@ import javax.swing.JFileChooser;
public class FileChooser extends JFileChooser {
private Settings s;
/**
* Create a new FileChooser with the default directory set to the user's
* Sudoku directory and no special styling.
*
* @param defaultDirectory
*/
public FileChooser(File defaultDirectory) {
super(defaultDirectory);
for (File f: defaultDirectory.listFiles()) {
ensureFileIsVisible(f);
}
style();
setupSettings();
}
/**
* Create a new FileChooser with the default directory set to the user's
* Sudoku directory.
@@ -70,7 +54,8 @@ public class FileChooser extends JFileChooser {
public boolean accept(File f) {
String name = f.getName().toLowerCase();
if(name.length() > 5) {
return name.substring(name.length() - 5).equals(".sdku") || !f.isDirectory();
return name.substring(name.length() - 5).equals(".sdku")
|| !f.isDirectory();
}
return false;
}
+65 -38
View File
@@ -1,6 +1,5 @@
package gui;
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
@@ -12,13 +11,30 @@ import java.awt.FontFormatException;
* 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}.
* These user settings are stored in a settings file that must be stored
* either in "~/.config/sudoku/settings.json" or
* "%APPDATA%/Local/Sudoku/settings.json" depending on the user's operating
* system.
*
* (Currently) Configurable settings include:
* - Default Directory
* - New File Properties
* - Font
*
*/
public class Settings {
private final String os;
private int newFileProperties;
// The directory where the settings file is located.
private String appDirectory;
// The default directory for .sdku files.
private String defaultDirectory;
// The properties for creating a new .sdku file.
private int newFileProperties;
// The font to be used throughout the application.
private Font font;
/**
@@ -33,11 +49,13 @@ public class Settings {
public Settings() {
os = System.getProperty("os.name").toLowerCase();
if(os.startsWith("windows"))
defaultDirectory = System.getProperty("user.home") + "\\AppData\\Local\\Sudoku\\";
appDirectory = System.getProperty("user.home") +
"\\AppData\\Local\\Sudoku\\";
else
defaultDirectory = System.getProperty("user.home") + "/.config/sudoku/";
appDirectory = System.getProperty("user.home") +
"/.config/sudoku/";
File dir = new File(defaultDirectory);
File dir = new File(appDirectory);
if (!dir.exists() || !dir.isDirectory()) {
dir.mkdirs();
@@ -46,6 +64,31 @@ public class Settings {
readSettings();
}
/**
* 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 default directory is either "~/.config/sudoku/puzzles"
* or "%APPDATA%/Local/Sudoku/Puzzles", depending on the user's operating
* system.
*
* @return String
*/
public String getDefaultDirectory() {
return defaultDirectory;
}
/**
* Set the default directory and write it to the settings file.
*
* @param defaultDirectory
*/
public void setDefaultDirectory(String defaultDirectory) {
this.defaultDirectory = defaultDirectory;
updateSettingsFile();
}
/**
* New File Properties is a configurable setting, where the following
* values are allowable:
@@ -69,32 +112,6 @@ public class Settings {
updateSettingsFile();
}
/**
* 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.
*
* NOTE: This does not change the location that the settings file may
* be located in. At this time, the settings file is always located in
* either "~/.config/sudoku/" or "%APPDATA%/Local/Sudoku/", depending
* on the user's operating system.
*
* @return String
*/
public String getDefaultDirectory() {
return defaultDirectory;
}
/**
* Set the default directory and write it to the settings file.
*
* @param defaultDirectory
*/
public void setDefaultDirectory(String defaultDirectory) {
this.defaultDirectory = defaultDirectory;
updateSettingsFile();
}
/**
* The Font is a configurable setting, where the user can specify a
* font to be used throughout the application.
@@ -134,6 +151,7 @@ public class Settings {
* settings file.
*/
private void defaultSettings() {
defaultDirectory = appDirectory + "puzzles";
newFileProperties = 0;
// Load the font from the system directory.
@@ -154,8 +172,8 @@ public class Settings {
* to populate the Settings file.
*/
private void readSettings() {
File settingsFile = new File(defaultDirectory + "/settings");
if (!settingsFile.exists()) {
File settingsFile = new File(appDirectory + "/settings.json");
if (!settingsFile.exists() && !settingsFile.isDirectory()) {
defaultSettings();
return;
}
@@ -172,7 +190,9 @@ public class Settings {
// Load the font from the system directory.
String fontFilepath;
if(os.startsWith("windows"))
fontFilepath = System.getProperty("user.home") + "\\AppData\\Local\\Microsoft\\Windows\\Fonts\\" + fontName + ".ttf";
fontFilepath = System.getProperty("user.home") +
"\\AppData\\Local\\Microsoft\\Windows\\Fonts\\" +
fontName + ".ttf";
else if(os.startsWith("mac"))
fontFilepath = "/Library/Fonts/" + fontName + ".ttf";
else
@@ -180,12 +200,19 @@ public class Settings {
// Register the font with the GraphicsEnvironment.
try {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, new File(fontFilepath)));
GraphicsEnvironment ge = GraphicsEnvironment.
getLocalGraphicsEnvironment();
ge.registerFont(Font.createFont(
Font.TRUETYPE_FONT,
new File(fontFilepath)
));
return 0;
} catch(IOException | FontFormatException e) {
System.out.println("Filepath of font not found: " + fontFilepath + " does not exist.");
System.out.println("Filepath of font not found: " +
fontFilepath + " does not exist.");
return 1;
}
}