diff --git a/Cell.java b/Cell.java index 9847835..8d0fda6 100644 --- a/Cell.java +++ b/Cell.java @@ -17,4 +17,4 @@ public class Cell { public void setValue(int value) { this.value = value; } -} \ No newline at end of file +} diff --git a/GenerateGrid.java b/GenerateGrid.java new file mode 100644 index 0000000..f2de7e2 --- /dev/null +++ b/GenerateGrid.java @@ -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(); + } + } +} diff --git a/Grid.java b/Grid.java index 9d21032..8500fc1 100644 --- a/Grid.java +++ b/Grid.java @@ -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(); } -} \ No newline at end of file + + 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(); + } + } +} diff --git a/Makefile b/Makefile index 1707241..4cec9cb 100644 --- a/Makefile +++ b/Makefile @@ -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) $< diff --git a/Sudoku.java b/Sudoku.java index 63310b2..c2250ee 100644 --- a/Sudoku.java +++ b/Sudoku.java @@ -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 } } -} \ No newline at end of file + + 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); + } + +} diff --git a/SudokuButtonListener.java b/SudokuButtonListener.java index 5563c3b..811b70e 100644 --- a/SudokuButtonListener.java +++ b/SudokuButtonListener.java @@ -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; } -} \ No newline at end of file +} diff --git a/SudokuSolver.java b/SudokuSolver.java new file mode 100644 index 0000000..fbdd8d8 --- /dev/null +++ b/SudokuSolver.java @@ -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; + } +} diff --git a/SudokuUI.java b/SudokuUI.java index db2f01c..ca1d89f 100644 --- a/SudokuUI.java +++ b/SudokuUI.java @@ -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 { } } } -} \ No newline at end of file + + 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)); + } + } + } +}