Ajout generation grille, amélioration UI, commentaires

This commit is contained in:
Vincent TEISSIER 2024-05-04 00:13:44 +02:00
parent f8e3c642b5
commit 8adb8b4547
8 changed files with 429 additions and 75 deletions

@ -17,4 +17,4 @@ public class Cell {
public void setValue(int value) {
this.value = value;
}
}
}

124
GenerateGrid.java Normal file

@ -0,0 +1,124 @@
import java.util.Random;
public class GenerateGrid {
private static final int GRID_SIZE = 9;
private static final int EMPTY_CELL = 0;
private static final int MINIMUM_CLUES = 25; // Modifier ce nombre en fonction du nombre de clues désiré
public static void main(String[] args) {
Grid grid = generatePuzzleGrid();
printGrid(grid);
}
public static Grid generatePuzzleGrid() {
return generateGrid();
}
public static Grid generateGrid() {
Grid grid = new Grid();
solveSudoku(grid);
removeNumbers(grid);
return grid;
}
private static boolean solveSudoku(Grid grid) {
return solveSudokuHelper(grid, 0, 0);
}
private static boolean solveSudokuHelper(Grid grid, int row, int col) {
if (row == GRID_SIZE) {
row = 0;
if (++col == GRID_SIZE) {
return true;
}
}
if (grid.getCell(row, col).getValue() != EMPTY_CELL) {
return solveSudokuHelper(grid, row + 1, col);
}
Random random = new Random();
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9};
shuffleArray(numbers, random);
for (int num : numbers) {
if (isValidMove(grid, row, col, num)) {
grid.getCell(row, col).setValue(num);
if (solveSudokuHelper(grid, row + 1, col)) {
return true;
}
grid.getCell(row, col).setValue(EMPTY_CELL);
}
}
return false;
}
private static boolean isValidMove(Grid grid, int row, int col, int num) {
for (int i = 0; i < GRID_SIZE; i++) {
if (grid.getCell(row, i).getValue() == num || grid.getCell(i, col).getValue() == num) {
return false;
}
}
int boxRow = row - row % 3;
int boxCol = col - col % 3;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (grid.getCell(boxRow + i, boxCol + j).getValue() == num) {
return false;
}
}
}
return true;
}
private static void shuffleArray(int[] array, Random random) {
for (int i = array.length - 1; i > 0; i--) {
int index = random.nextInt(i + 1);
int temp = array[index];
array[index] = array[i];
array[i] = temp;
}
}
public static void removeNumbers(Grid grid) {
Random random = new Random();
while (countClues(grid) > MINIMUM_CLUES) {
int row = random.nextInt(GRID_SIZE);
int col = random.nextInt(GRID_SIZE);
int value = grid.getCell(row, col).getValue();
grid.getCell(row, col).setValue(EMPTY_CELL);
Grid tempGrid = new Grid();
if (!solveSudoku(tempGrid)) {
grid.getCell(row, col).setValue(value);
}
}
}
private static int countClues(Grid grid) {
int count = 0;
for (int row = 0; row < GRID_SIZE; row++) {
for (int col = 0; col < GRID_SIZE; col++) {
if (grid.getCell(row, col).getValue() != EMPTY_CELL) {
count++;
}
}
}
return count;
}
public static void printGrid(Grid grid) {
for (int row = 0; row < GRID_SIZE; row++) {
for (int col = 0; col < GRID_SIZE; col++) {
System.out.print(grid.getCell(row, col).getValue() + " ");
}
System.out.println();
}
}
}

@ -7,9 +7,18 @@ public class Grid {
public Grid() {
cells = new Cell[9][9];
for (int ligne = 0; ligne < 9; ligne++) {
for (int column = 0; column < 9; column++) {
cells[ligne][column] = new Cell();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
cells[i][j] = new Cell();
}
}
}
public Grid(Grid grid) {
cells = new Cell[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
cells[i][j] = new Cell(grid.cells[i][j].getValue());
}
}
}
@ -35,24 +44,24 @@ public class Grid {
}
}
public Cell getCell(int ligne, int column) {
return cells[ligne][column];
public Cell getCell(int ligne, int col) {
return cells[ligne][col];
}
public void setCell(int ligne, int column, int value) {
cells[ligne][column].setValue(value);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int ligne = 0; ligne < 9; ligne++) {
for (int column = 0; column < 9; column++) {
int value = cells[ligne][column].getValue();
sb.append(value).append(" ");
public void copyFrom(Grid other) {
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
this.cells[row][col].setValue(other.cells[row][col].getValue());
}
sb.append("\n");
}
return sb.toString();
}
}
public void printGrid() {
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
System.out.print(cells[row][col].getValue() + " ");
}
System.out.println();
}
}
}

@ -1,5 +1,5 @@
# List of Java source files
SRCS := Cell.java Grid.java Sudoku.java SudokuButtonListener.java SudokuUI.java
SRCS := Cell.java GenerateGrid.java Grid.java Sudoku.java SudokuButtonListener.java SudokuSolver.java SudokuUI.java
# Directory to store compiled class files
BUILD_DIR := build
@ -27,9 +27,4 @@ compile: $(addprefix $(BUILD_DIR)/, $(SRCS:.java=.class))
# Rule to compile Java source files
$(BUILD_DIR)/%.class: %.java | $(BUILD_DIR)
$(JAVAC) -d $(BUILD_DIR) $
# Clean up compiled class files
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)
$(JAVAC) -d $(BUILD_DIR) $<

@ -1,22 +1,120 @@
public class Sudoku {
private Grid grid;
private boolean solved;
public Sudoku() {
this.grid = new Grid();
this.grid = new Grid(); // Initialiser avec une grille vide
this.solved = false;
}
public Grid getGrid() {
return grid;
}
public void setGrid(Grid newGrid) {
this.grid = newGrid;
}
public boolean isSolved() {
return solved;
}
public static void main(String[] args) {
if (args.length > 0) {
Sudoku sudoku = new Sudoku();
sudoku.getGrid().loadGridFromFile(args[0]);
// Pas besoin d'afficher la grille ici, car loadGridFromFile() le fait déjà
new SudokuUI(sudoku.getGrid());
} else {
System.err.println("Usage: java Sudoku <grid_file>");
Sudoku sudoku = new Sudoku();
sudoku.printGrid(); // Afficher la grille non résolue dans la console
new SudokuUI(sudoku);
}
public void printGrid() {
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
int value = grid.getCell(row, col).getValue();
System.out.print(value + " ");
}
System.out.println(); // Passer à la ligne suivante après chaque ligne de la grille
}
}
}
public boolean generateSudoku() {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (!solveSudoku(i, j)) {
return false;
}
}
}
return true;
}
private boolean solveSudoku(int row, int col) {
if (row == 9 - 1 && col == 9) {
return true;
}
if (col == 9) {
row++;
col = 0;
}
if (grid.getCell(row, col).getValue() != 0) {
return solveSudoku(row, col + 1);
}
for (int num = 1; num <= 9; num++) {
if (isSafe(row, col, num)) {
grid.getCell(row, col).setValue(num);
if (solveSudoku(row, col + 1)) {
return true;
}
}
grid.getCell(row, col).setValue(0);
}
return false;
}
private boolean isSafe(int row, int col, int num) {
// Vérifiez si nous trouvons le même numéro dans la même ligne,
// la même colonne ou la même sous-grille
return !findInRow(row, num) && !findInCol(col, num) && !findInBox(row - row % 3, col - col % 3, num);
}
// Méthodes findInRow, findInCol et findInBox ici
private boolean findInRow(int row, int num) {
for (int col = 0; col < 9; col++) {
if (grid.getCell(row, col).getValue() == num) {
return true;
}
}
return false;
}
private boolean findInCol(int col, int num) {
for (int row = 0; row < 9; row++) {
if (grid.getCell(row, col).getValue() == num) {
return true;
}
}
return false;
}
private boolean findInBox(int boxStartRow, int boxStartCol, int num) {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
if (grid.getCell(row + boxStartRow, col + boxStartCol).getValue() == num) {
return true;
}
}
}
return false;
}
public void loadGridFromFile(String fileName) {
this.grid.loadGridFromFile(fileName);
}
}

@ -4,45 +4,48 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SudokuButtonListener implements ActionListener {
private int row;
private int ligne;
private int col;
private Grid grid;
private JButton[][] buttons;
private Sudoku sudoku;
private JButton[][] boutons;
public SudokuButtonListener(int row, int col, Grid grid, JButton[][] buttons) {
this.row = row;
public SudokuButtonListener(int ligne, int col, Sudoku sudoku, JButton[][] boutons) {
this.ligne = ligne;
this.col = col;
this.grid = grid;
this.buttons = buttons;
this.sudoku = sudoku;
this.boutons = boutons;
}
@Override
public void actionPerformed(ActionEvent e) {
try {
int num = Integer.parseInt(JOptionPane.showInputDialog("Enter a number:"));
grid.getCell(row, col).setValue(num);
if (num == 0) {
buttons[row][col].setText(""); // Empty cell if the number is 0
} else {
buttons[row][col].setText(String.valueOf(num));
String input = JOptionPane.showInputDialog("Entrez un nombre :");
if (input != null && input.length() > 0) {
try {
int num = Integer.parseInt(input);
sudoku.getGrid().getCell(ligne, col).setValue(num);
if (num == 0) {
boutons[ligne][col].setText(""); // Case vide si le nombre est 0
} else {
boutons[ligne][col].setText(String.valueOf(num));
}
if (!isValidMove(num, ligne, col)) {
boutons[ligne][col].setForeground(Color.RED); // Met le texte en rouge en cas de mouvement invalide
} else {
boutons[ligne][col].setForeground(Color.BLACK); // Réinitialise la couleur du texte
}
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(null, "Veuillez entrer un nombre valide.");
}
if (!isValidMove(num, row, col)) {
buttons[row][col].setForeground(Color.RED); // Set text color to red for invalid move
} else {
buttons[row][col].setForeground(Color.BLACK); // Reset text color
}
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(null, "Please enter a valid number.");
}
}
private boolean isValidMove(int num, int row, int col) {
return isValidRow(num, row) && isValidCol(num, col) && isValidBox(num, row - row % 3, col - col % 3);
private boolean isValidMove(int num, int ligne, int col) {
return isValidRow(num, ligne) && isValidCol(num, col) && isValidBox(num, ligne - ligne % 3, col - col % 3);
}
private boolean isValidRow(int num, int row) {
private boolean isValidRow(int num, int ligne) {
for (int i = 0; i < 9; i++) {
if (grid.getCell(row, i).getValue() == num && i != col) {
if (sudoku.getGrid().getCell(ligne, i).getValue() == num && i != col) {
return false;
}
}
@ -51,23 +54,23 @@ public class SudokuButtonListener implements ActionListener {
private boolean isValidCol(int num, int col) {
for (int i = 0; i < 9; i++) {
if (grid.getCell(i, col).getValue() == num && i != row) {
if (sudoku.getGrid().getCell(i, col).getValue() == num && i != ligne) {
return false;
}
}
return true;
}
private boolean isValidBox(int num, int boxStartRow, int boxStartCol) {
private boolean isValidBox(int num, int Row, int Col) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
int row = i + boxStartRow;
int col = j + boxStartCol;
if (grid.getCell(row, col).getValue() == num && (row != this.row || col != this.col)) {
int ligne = i + Row;
int col = j + Col;
if (sudoku.getGrid().getCell(ligne, col).getValue() == num && (ligne != this.ligne || col != this.col)) {
return false;
}
}
}
return true;
}
}
}

57
SudokuSolver.java Normal file

@ -0,0 +1,57 @@
public class SudokuSolver {
public boolean solve(Grid grid) {
return solveRecursive(grid);
}
private boolean solveRecursive(Grid grid) {
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
if (grid.getCell(row, col).getValue() == 0) {
for (int num = 1; num <= 9; num++) {
if (isSafe(grid, row, col, num)) {
grid.getCell(row, col).setValue(num);
if (solveRecursive(grid)) {
return true;
}
// Backtrack si la solution actuelle n'est pas valide
grid.getCell(row, col).setValue(0);
}
}
// Aucun nombre n'est valide, donc la grille est insoluble
return false;
}
}
}
// Toutes les cellules ont été remplies, la grille est résolue
return true;
}
public boolean isSafe(Grid grid, int row, int col, int num) {
// Vérifier la ligne
for (int i = 0; i < 9; i++) {
if (grid.getCell(row, i).getValue() == num) {
return false;
}
}
// Vérifier la colonne
for (int i = 0; i < 9; i++) {
if (grid.getCell(i, col).getValue() == num) {
return false;
}
}
// Vérifier la région 3x3
int startRow = row - row % 3;
int startCol = col - col % 3;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (grid.getCell(i + startRow, j + startCol).getValue() == num) {
return false;
}
}
}
return true;
}
}

@ -1,12 +1,14 @@
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SudokuUI extends JFrame {
private Grid grid;
private Sudoku sudoku;
private JButton[][] buttons;
public SudokuUI(Grid grid) {
this.grid = grid;
public SudokuUI(Sudoku sudoku) {
this.sudoku = sudoku;
this.buttons = new JButton[9][9];
setTitle("Sudoku");
@ -15,26 +17,33 @@ public class SudokuUI extends JFrame {
gridPanel.setBackground(Color.WHITE);
gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// Boutons de la grille
// Création des boutons de la grille
createGridButtons(gridPanel);
// Création du bouton "Résoudre"
createSolveButton();
createGenerateButton();
// Calcul de la taille préférée de la fenêtre en fonction de la taille de la grille
int preferredSize = 50 * 9; // 50 est la taille approximative d'un bouton
gridPanel.setPreferredSize(new Dimension(preferredSize, preferredSize));
add(gridPanel);
pack();
pack(); // Redimensionne automatiquement la fenêtre pour s'adapter à la taille du panneau
setLocationRelativeTo(null);
setVisible(true);
}
private void createGridButtons(JPanel gridPanel) { // Création sudoku
private void createGridButtons(JPanel gridPanel) {
Grid grid = sudoku.getGrid(); // Obtenez la grille à partir de Sudoku
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
JButton button = new JButton();
button.setFont(new Font("Arial", Font.BOLD, 24));
button.setContentAreaFilled(false);
button.setBorder(BorderFactory.createLineBorder(Color.BLACK));
button.addActionListener(new SudokuButtonListener(row, col, grid, buttons));
button.addActionListener(new SudokuButtonListener(row, col, sudoku, buttons));
gridPanel.add(button);
buttons[row][col] = button;
@ -44,4 +53,63 @@ public class SudokuUI extends JFrame {
}
}
}
}
private void createSolveButton() {
JButton solveButton = new JButton("Résoudre");
solveButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SudokuSolver solver = new SudokuSolver(); // Créez une instance de SudokuSolver
Grid solvedGrid = new Grid(); // Créez une nouvelle grille pour stocker la solution
solvedGrid.copyFrom(sudoku.getGrid()); // Copiez la grille actuelle de Sudoku
// Résolvez la grille
if (solver.solve(solvedGrid)) {
// Si une solution est trouvée, mettez à jour la grille Sudoku avec la solution
sudoku.getGrid().copyFrom(solvedGrid);
updateGrid();
} else {
// Sinon, affichez un message indiquant que la grille est insoluble
JOptionPane.showMessageDialog(null, "La grille est insoluble.");
}
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(solveButton);
add(buttonPanel, BorderLayout.SOUTH);
}
private void createGenerateButton() {
JButton generateButton = new JButton("Générer");
generateButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// Générer une nouvelle grille Sudoku
Grid newGrid = GenerateGrid.generatePuzzleGrid();
// Définir la nouvelle grille dans l'instance de Sudoku
sudoku.setGrid(newGrid);
// Afficher la grille générée dans la console
sudoku.printGrid();
// Mettre à jour l'interface utilisateur pour afficher la nouvelle grille
updateGrid();
}
});
JPanel buttonPanel = new JPanel();
buttonPanel.add(generateButton);
add(buttonPanel, BorderLayout.EAST);
}
private void updateGrid() {
Grid grid = sudoku.getGrid();
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
int value = grid.getCell(row, col).getValue();
buttons[row][col].setText(value == 0 ? "" : String.valueOf(value));
}
}
}
}