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) { public void setValue(int value) {
this.value = 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() { public Grid() {
cells = new Cell[9][9]; cells = new Cell[9][9];
for (int ligne = 0; ligne < 9; ligne++) { for (int i = 0; i < 9; i++) {
for (int column = 0; column < 9; column++) { for (int j = 0; j < 9; j++) {
cells[ligne][column] = new Cell(); 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) { public Cell getCell(int ligne, int col) {
return cells[ligne][column]; return cells[ligne][col];
} }
public void setCell(int ligne, int column, int value) { public void copyFrom(Grid other) {
cells[ligne][column].setValue(value); for (int row = 0; row < 9; row++) {
} for (int col = 0; col < 9; col++) {
this.cells[row][col].setValue(other.cells[row][col].getValue());
@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(" ");
} }
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 # 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 # Directory to store compiled class files
BUILD_DIR := build BUILD_DIR := build
@ -27,9 +27,4 @@ compile: $(addprefix $(BUILD_DIR)/, $(SRCS:.java=.class))
# Rule to compile Java source files # Rule to compile Java source files
$(BUILD_DIR)/%.class: %.java | $(BUILD_DIR) $(BUILD_DIR)/%.class: %.java | $(BUILD_DIR)
$(JAVAC) -d $(BUILD_DIR) $ $(JAVAC) -d $(BUILD_DIR) $<
# Clean up compiled class files
.PHONY: clean
clean:
rm -rf $(BUILD_DIR)

@ -1,22 +1,120 @@
public class Sudoku { public class Sudoku {
private Grid grid; private Grid grid;
private boolean solved;
public Sudoku() { public Sudoku() {
this.grid = new Grid(); this.grid = new Grid(); // Initialiser avec une grille vide
this.solved = false;
} }
public Grid getGrid() { public Grid getGrid() {
return grid; return grid;
} }
public void setGrid(Grid newGrid) {
this.grid = newGrid;
}
public boolean isSolved() {
return solved;
}
public static void main(String[] args) { public static void main(String[] args) {
if (args.length > 0) { Sudoku sudoku = new Sudoku();
Sudoku sudoku = new Sudoku(); sudoku.printGrid(); // Afficher la grille non résolue dans la console
sudoku.getGrid().loadGridFromFile(args[0]); new SudokuUI(sudoku);
// 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>");
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; import java.awt.event.ActionListener;
public class SudokuButtonListener implements ActionListener { public class SudokuButtonListener implements ActionListener {
private int row; private int ligne;
private int col; private int col;
private Grid grid; private Sudoku sudoku;
private JButton[][] buttons; private JButton[][] boutons;
public SudokuButtonListener(int row, int col, Grid grid, JButton[][] buttons) { public SudokuButtonListener(int ligne, int col, Sudoku sudoku, JButton[][] boutons) {
this.row = row; this.ligne = ligne;
this.col = col; this.col = col;
this.grid = grid; this.sudoku = sudoku;
this.buttons = buttons; this.boutons = boutons;
} }
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
try { String input = JOptionPane.showInputDialog("Entrez un nombre :");
int num = Integer.parseInt(JOptionPane.showInputDialog("Enter a number:")); if (input != null && input.length() > 0) {
grid.getCell(row, col).setValue(num); try {
if (num == 0) { int num = Integer.parseInt(input);
buttons[row][col].setText(""); // Empty cell if the number is 0 sudoku.getGrid().getCell(ligne, col).setValue(num);
} else { if (num == 0) {
buttons[row][col].setText(String.valueOf(num)); 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) { private boolean isValidMove(int num, int ligne, int col) {
return isValidRow(num, row) && isValidCol(num, col) && isValidBox(num, row - row % 3, col - col % 3); 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++) { 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; return false;
} }
} }
@ -51,23 +54,23 @@ public class SudokuButtonListener implements ActionListener {
private boolean isValidCol(int num, int col) { private boolean isValidCol(int num, int col) {
for (int i = 0; i < 9; i++) { 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 false;
} }
} }
return true; 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 i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) { for (int j = 0; j < 3; j++) {
int row = i + boxStartRow; int ligne = i + Row;
int col = j + boxStartCol; int col = j + Col;
if (grid.getCell(row, col).getValue() == num && (row != this.row || col != this.col)) { if (sudoku.getGrid().getCell(ligne, col).getValue() == num && (ligne != this.ligne || col != this.col)) {
return false; return false;
} }
} }
} }
return true; 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 javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SudokuUI extends JFrame { public class SudokuUI extends JFrame {
private Grid grid; private Sudoku sudoku;
private JButton[][] buttons; private JButton[][] buttons;
public SudokuUI(Grid grid) { public SudokuUI(Sudoku sudoku) {
this.grid = grid; this.sudoku = sudoku;
this.buttons = new JButton[9][9]; this.buttons = new JButton[9][9];
setTitle("Sudoku"); setTitle("Sudoku");
@ -15,26 +17,33 @@ public class SudokuUI extends JFrame {
gridPanel.setBackground(Color.WHITE); gridPanel.setBackground(Color.WHITE);
gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); gridPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// Boutons de la grille // Création des boutons de la grille
createGridButtons(gridPanel); 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 int preferredSize = 50 * 9; // 50 est la taille approximative d'un bouton
gridPanel.setPreferredSize(new Dimension(preferredSize, preferredSize)); gridPanel.setPreferredSize(new Dimension(preferredSize, preferredSize));
add(gridPanel); add(gridPanel);
pack(); pack(); // Redimensionne automatiquement la fenêtre pour s'adapter à la taille du panneau
setLocationRelativeTo(null); setLocationRelativeTo(null);
setVisible(true); 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 row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) { for (int col = 0; col < 9; col++) {
JButton button = new JButton(); JButton button = new JButton();
button.setFont(new Font("Arial", Font.BOLD, 24)); button.setFont(new Font("Arial", Font.BOLD, 24));
button.setContentAreaFilled(false); button.setContentAreaFilled(false);
button.setBorder(BorderFactory.createLineBorder(Color.BLACK)); 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); gridPanel.add(button);
buttons[row][col] = 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));
}
}
}
}