Working parser, file chooser and sample files

This commit is contained in:
Imbus 2023-12-10 15:54:20 +01:00
parent 175545d3d5
commit 521b3fb05b
6 changed files with 163 additions and 4 deletions

View file

@ -0,0 +1,9 @@
0 0 9 0 7 1 3 0 0
0 0 1 0 0 0 0 0 0
6 0 0 0 9 0 0 4 7
5 0 0 9 0 4 0 0 0
1 0 4 0 0 0 2 0 9
0 0 0 1 0 8 0 0 4
7 3 0 0 1 0 0 0 2
0 0 0 0 0 0 5 0 0
0 0 8 2 4 0 6 0 0

View file

@ -54,7 +54,23 @@ public class SudokuController {
} }
}); });
view.addCellClickListener(new CellActionListener()); view.addFileButtonListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// Open a file, view handles the parsing internally via SudokuParser
int[][] newBoard = view.openFile();
// If the file was parsed successfully
if (newBoard != null) {
// Set the model
model.setBoard(newBoard);
// Update the view
view.updateView(model.getBoard());
}
}
});
view.addCellActionListener(new CellActionListener());
} }
/** Start the GUI */ /** Start the GUI */

View file

@ -4,6 +4,8 @@ import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.*; import java.awt.event.*;
import sudoku.SudokuParser;
/** /**
* SolverView is a GUI for the SudokuSolver interface * SolverView is a GUI for the SudokuSolver interface
*/ */
@ -17,6 +19,8 @@ public class SudokuView extends JFrame {
private JButton resetButton; private JButton resetButton;
/** Button for random */ /** Button for random */
private JButton randomButton; private JButton randomButton;
/** Button for picking a Sudoku-file */
private JButton fileButton;
/** Constructor */ /** Constructor */
public SudokuView() { public SudokuView() {
@ -52,11 +56,13 @@ public class SudokuView extends JFrame {
solveButton = new JButton("Solve"); solveButton = new JButton("Solve");
resetButton = new JButton("Reset"); resetButton = new JButton("Reset");
randomButton = new JButton("Randomize"); randomButton = new JButton("Randomize");
fileButton = new JButton("Open file");
JPanel buttonPanel = new JPanel(); JPanel buttonPanel = new JPanel();
buttonPanel.add(solveButton); buttonPanel.add(solveButton);
buttonPanel.add(resetButton); buttonPanel.add(resetButton);
buttonPanel.add(randomButton); buttonPanel.add(randomButton);
buttonPanel.add(fileButton);
add(buttonPanel, BorderLayout.SOUTH); add(buttonPanel, BorderLayout.SOUTH);
} }
@ -105,6 +111,15 @@ public class SudokuView extends JFrame {
randomButton.addActionListener(listener); randomButton.addActionListener(listener);
} }
/**
* Method to add ActionListener to file button
*
* @param listener the ActionListener to add
*/
public void addFileButtonListener(ActionListener listener) {
fileButton.addActionListener(listener);
}
/** /**
* Method to add ActionListener to individual cells in the grid * Method to add ActionListener to individual cells in the grid
* <p> * <p>
@ -114,7 +129,7 @@ public class SudokuView extends JFrame {
* *
* @param listener the ActionListener to add * @param listener the ActionListener to add
*/ */
public void addCellClickListener(ActionListener listener) { public void addCellActionListener(ActionListener listener) {
for (int row = 0; row < 9; row++) { for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) { for (int col = 0; col < 9; col++) {
grid[row][col].addActionListener(listener); grid[row][col].addActionListener(listener);
@ -183,4 +198,35 @@ public class SudokuView extends JFrame {
public void showErrorMessage(String message) { public void showErrorMessage(String message) {
JOptionPane.showMessageDialog(this, message); JOptionPane.showMessageDialog(this, message);
} }
/**
* Method to open a file picker dialog
*
* @return 2D array of integers representing the Sudoku board
*/
public int[][] openFile() {
// Create a file chooser and set all related options
JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new java.io.File("."));
fileChooser.setDialogTitle("Select a Sudoku file");
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
fileChooser.setAcceptAllFileFilterUsed(false);
// Show the file chooser and return if the user cancels
int returnValue = fileChooser.showOpenDialog(this);
if (returnValue != JFileChooser.APPROVE_OPTION) {
return null;
}
// Get the path
String filepath = fileChooser.getSelectedFile().getAbsolutePath();
// Try to parse it
try {
return SudokuParser.parseSudoku(filepath);
} catch (Exception e) {
showErrorMessage(e.getMessage());
return null;
}
}
} }

View file

@ -47,8 +47,6 @@ public class Solver implements SudokuSolver {
*/ */
private boolean solve(int row, int col) { private boolean solve(int row, int col) {
if (++tries >= 10000) { if (++tries >= 10000) {
if (tries == 10000)
System.out.println(String.format("Likely unsolvable. Tries: %d", tries));
return false; return false;
} }

View file

@ -0,0 +1,73 @@
package sudoku;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.FileNotFoundException;
/** Helpers class for parsing Sudoku files */
public class SudokuParser {
private static final int BOARD_SIZE = 9;
// * Empty private constructor */
private SudokuParser() {
// Empty
};
/**
* Parses a Sudoku file and returns a 2D array of integers
*
* @param filePath Path to the Sudoku file
* @return 2D array of integers representing the Sudoku board
* @throws IOException If an IO error occurs
* @throws FileNotFoundException When the file cannot be found
* @throws NumberFormatException When the file contains invalid characters
* @throws IllegalArgumentException When the file contains an invalid number of
* rows or columns
*/
public static int[][] parseSudoku(String filePath)
throws IOException, FileNotFoundException, NumberFormatException, IllegalArgumentException {
int[][] sudokuBoard = new int[BOARD_SIZE][BOARD_SIZE];
// In practice we could just split the entire file into a single string and then
// parse it into an array of integers, which is then partitioned into a 2D
// array.
// However, this is how the assignment is specified, so we will do it this way.
// Try to read the file with a BufferedReader
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
int row = 0;
// While there are lines to read and we haven't reached the end of the board
while ((line = reader.readLine()) != null && row < BOARD_SIZE) {
// Split it into an array of strings
String[] values = line.trim().split("\\s+");
// Check that the number of columns is correct
if (values.length != BOARD_SIZE) {
throw new IllegalArgumentException("Invalid number of columns in the Sudoku file.");
}
// Parse the strings into integers and add them to the board
for (int col = 0; col < BOARD_SIZE; col++) {
sudokuBoard[row][col] = Integer.parseInt(values[col]); // Throws NumberFormatException
}
row++;
}
if (row != BOARD_SIZE) {
throw new IllegalArgumentException("Invalid number of rows in the Sudoku file.");
}
} catch (FileNotFoundException e) {
throw new FileNotFoundException("The Sudoku file could not be found.");
} catch (IOException e) {
throw new IOException("An error occurred while reading the Sudoku file.");
} catch (NumberFormatException e) {
throw new NumberFormatException("The Sudoku file contains invalid characters.");
}
return sudokuBoard;
}
}

View file

@ -0,0 +1,17 @@
package sudoku;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class SudokuParserTest {
@Test
void constructorTest() {
int[][] board;
try {
board = SudokuParser.parseSudoku("sample_sudokus/demo_from_lab.txt");
} catch (Exception e) {
board = null;
}
assertNotNull(board);
}
}