diff --git a/Makefile b/Makefile index 9d20c79..285704a 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ JCFLAGS := -encoding UTF-8 -implicit:none JVM := java JVMFLAGS := -SRCDIR := ./src/GridMaker +SRCDIR := ./src/GridSolver OUTDIR := ./out DOCDIR := ./doc SRC := $(wildcard $(SRCDIR)/*.java) diff --git a/src/.DS_Store b/src/.DS_Store index 0d19af3..6f0c01f 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/GridMaker/GridMakerGrid.java b/src/GridMaker/GridMakerGrid.java index 33ab271..3536a28 100755 --- a/src/GridMaker/GridMakerGrid.java +++ b/src/GridMaker/GridMakerGrid.java @@ -110,10 +110,8 @@ public class GridMakerGrid extends JPanel { for (int j = 0; j < 9; j++) { temp.append(gridCases[i][j].getCellValue()); } - exportedGrid[i] = Integer.parseInt(temp.toString()); } - return exportedGrid; } diff --git a/src/GridSolver/Button.java b/src/GridSolver/Button.java new file mode 100644 index 0000000..d17c996 --- /dev/null +++ b/src/GridSolver/Button.java @@ -0,0 +1,61 @@ +import javax.swing.*; +import java.awt.*; + +/** + * Class containing custom settings for JButtons. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ + +public class Button extends JButton { + /** + * Constructor + * @param text The text of the button + */ + public Button (String text) { + super(text); + setFont(new Font("Arial", Font.BOLD, 15)); + setBackground(new Color(96, 175, 255)); + } + + /** + * Constructor + * @param text The text of the button + * @param dimension The dimension of the button + */ + public Button(String text, Dimension dimension) { + super(text); + setPreferredSize(dimension); + setFont(new Font("Arial", Font.BOLD, 20)); + setBackground(new Color(96, 175, 255)); + } + + /** + * Constructor + * @param text The text of the button + * @param dimension The dimension of the button + * @param font The font of the text in the button + */ + public Button(String text, Dimension dimension, Font font) { + super(text); + setPreferredSize(dimension); + setFont(font); + setBackground(new Color(96, 175, 255)); + } + + /** + * Constructor + * @param text The text of the button + * @param dimension The dimension of the button + * @param font The font of the text in the button + * @param color The background color of the button + */ + public Button(String text, Dimension dimension, Font font, Color color) { + super(text); + setPreferredSize(dimension); + setFont(font); + setBackground(color); + } + +} \ No newline at end of file diff --git a/src/GridSolver/CongratulationsDialog.java b/src/GridSolver/CongratulationsDialog.java new file mode 100644 index 0000000..b31c698 --- /dev/null +++ b/src/GridSolver/CongratulationsDialog.java @@ -0,0 +1,39 @@ +import javax.swing.JOptionPane; + +/** + * Cette classe permet de crée une boîte de dialogue de félicitations pour afficher le temps de résolution d'un Sudoku. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class CongratulationsDialog extends JOptionPane { + + /** + * Constructeur de la classe CongratulationsDialog. + * @param solvingStartTime Le temps de démarrage de la résolution du Sudoku en nanosecondes. + */ + public CongratulationsDialog(long solvingStartTime) { + super(); + + // Obtenir le temps actuel en nanosecondes + long currentTime = System.nanoTime(); + + // Calculer le temps de résolution en secondes + long solvingTime = (currentTime - solvingStartTime) / 1_000_000_000; + + // Créer le message de félicitations en fonction du temps de résolution + String message; + long minutes = solvingTime / 60; + long seconds = solvingTime % 60; + + // Gérer le pluriel pour les minutes + String minutesString = (minutes <= 1) ? " minute" : " minutes"; + + // Gérer le pluriel pour les secondes + String secondsString = (seconds <= 1) ? " seconde" : " secondes"; + + message = "Félicitations ! Vous avez résolu le Sudoku en " + minutes + minutesString + " et " + seconds + secondsString + "."; + + showMessageDialog(null, message, "Félicitations !", JOptionPane.PLAIN_MESSAGE); + } +} diff --git a/src/GridSolver/DialogManager.java b/src/GridSolver/DialogManager.java new file mode 100644 index 0000000..567a6d6 --- /dev/null +++ b/src/GridSolver/DialogManager.java @@ -0,0 +1,10 @@ + +/** + * Interface containing definition to showDialog box. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public interface DialogManager { + void showDialog(); +} \ No newline at end of file diff --git a/src/GridSolver/GSCase.java b/src/GridSolver/GSCase.java new file mode 100755 index 0000000..6962c54 --- /dev/null +++ b/src/GridSolver/GSCase.java @@ -0,0 +1,201 @@ +import java.awt.*; +import javax.swing.JPanel; +import javax.swing.JLabel; +import java.awt.event.MouseListener; +import java.awt.event.MouseEvent; + +/** + * La classe GSCase représente une case individuelle dans une grille de Sudoku. + */ +public class GSCase extends JPanel implements MouseListener{ + + private int primaryValue; // Valeur principale de la case + private int secondaryValue; // Deuxième valeur de la case (utilisée pour les cas spéciaux) + private int tertiaryValue; // Troisième valeur de la case (utilisée pour les cas spéciaux) + private int quaternaryValue; // Quatrième valeur de la case (utilisée pour les cas spéciaux) + private String text = ""; // Texte affiché dans la case + private Boolean isInitial = false; // Indique si la valeur de la case est initiale (fournie avec le puzzle) + private Boolean isActive; // Indique si la case est active (sélectionnée par l'utilisateur) + JLabel label = new JLabel(); // Composant pour afficher le texte dans la case + private byte digitCount = 0; // Compte le nombre de valeurs insérées dans la case + private int positionX; // Position X de la case dans la grille + private int positionY; // Position Y de la case dans la grille + private GSGrid parentGrid; // Référence à la grille parente + + /** + * Constructeur de la classe GSCase. + * @param grid La grille parente à laquelle cette case appartient. + * @param x Position X de la case dans la grille. + * @param y Position Y de la case dans la grille. + */ + public GSCase(GSGrid grid, int x, int y) { + this.positionX = x; + this.positionY = y; + + this.primaryValue = 0; + this.secondaryValue = 0; + this.tertiaryValue = 0; + this.quaternaryValue = 0; + + this.setBackground(Color.white); + this.addMouseListener(this); + this.parentGrid = grid; + this.add(label); + deactivateCell(); + layoutSetup(); + } + + /** + * Configure l'apparence initiale de la case. + */ + public void layoutSetup() { + this.label.setText(this.primaryValue != 0 ? this.text : ""); + this.label.setVisible(true); + this.repaint(); + } + + /** + * Initialise la case avec une valeur initiale. + */ + public void initializeCell() { + label.setText(this.text); + this.setBackground(this.isInitial ? Color.lightGray : Color.white); + layoutSetup(); + } + + /** + * Définit la valeur de la case. + * @param value La valeur à définir pour la case. + */ + public void setValue(int value) { + this.isInitial = (value != 0); + this.text = (value != 0) ? String.valueOf(value) : ""; + this.primaryValue = value; + initializeCell(); + } + + /** + * Obtient la valeur de la case. + * @return La valeur de la case. + */ + public int getValue(){ + return this.primaryValue; + } + + /** + * Gère les actions de clic de souris sur la case. + */ + public void mouseClicked(MouseEvent e) { + if (!this.isInitial) { + this.isActive = true; + this.setBackground(Color.GREEN); + this.parentGrid.testActivity(this.positionX, this.positionY); + } + } + + // D'autres méthodes de l'interface MouseListener + public void mousePressed(MouseEvent e) {} + public void mouseReleased(MouseEvent e) {} + public void mouseEntered(MouseEvent e) { + if (!isInitial) { + if (!isActive) { + this.setBackground(Color.yellow); + } + } + } + + public void mouseExited(MouseEvent e) { + if (!isInitial) { + if (!isActive) { + this.setBackground(Color.white); + } + } + } + + /** + * Désactive la case. + */ + public void deactivateCell(){ + this.isActive = false; + this.setBackground(Color.white); + } + + /** + * Obtient l'état d'activité de la case. + * @return True si la case est active, sinon False. + */ + public Boolean getActivity(){ + return this.isActive; + } + + /** + * Met à jour la valeur de la case. + * @param value La nouvelle valeur de la case. + */ + public void updateValue(int value){ + if (value == 0) { + this.primaryValue = 0; + this.digitCount = 0; + layoutSetup(); + } else { + if (!checkInputValue(value)) { + if (this.primaryValue != value ) { + if (this.digitCount == 0) { + this.digitCount++; + this.primaryValue = value; + this.text = String.valueOf(this.primaryValue); + } else if (this.digitCount == 1 && value != this.primaryValue) { + this.secondaryValue = value; + this.digitCount++; + this.text = String.valueOf(this.primaryValue + ", " + this.secondaryValue); + } else if (this.digitCount == 2 && value != this.primaryValue && value != this.secondaryValue) { + this.tertiaryValue = value; + this.digitCount++; + this.text = String.valueOf(this.primaryValue + ", " + this.secondaryValue + ", " + this.tertiaryValue); + } else if (this.digitCount == 3 && value != this.primaryValue && value != this.secondaryValue && value != this.tertiaryValue) { + this.quaternaryValue = value; + this.digitCount++; + this.text = String.valueOf(this.primaryValue + ", " + this.secondaryValue + ", " + this.tertiaryValue + ", " + this.quaternaryValue); + } + } + this.setBackground(Color.WHITE); + layoutSetup(); + } else if (checkInputValue(value)){ + this.setBackground(Color.RED); + } + } + } + + /** + * Vérifie si la valeur entrée est valide pour la case. + * @param val La valeur à vérifier. + * @return True si la valeur est valide, sinon False. + */ + public Boolean checkInputValue(int val){ + int temp = this.primaryValue; + this.primaryValue = val; + GSTest test = new GSTest(this.parentGrid); + Boolean isValid = test.test(); + this.primaryValue = temp; + return isValid; + } + + /** + * Obtient le nombre de valeurs insérées dans la case. + * @return Le nombre de valeurs insérées. + */ + public int getDigitCount(){ + return this.digitCount; + } + + /** + * Insère une valeur dans la case. + * @param value La valeur à insérer. + */ + public void insertValue(int value){ + this.primaryValue = value; + this.text = String.valueOf(this.primaryValue); + layoutSetup(); + } + +} diff --git a/src/GridSolver/GSGrid.java b/src/GridSolver/GSGrid.java new file mode 100755 index 0000000..2fc11bc --- /dev/null +++ b/src/GridSolver/GSGrid.java @@ -0,0 +1,180 @@ +import javax.swing.*; +import java.awt.*; +import javax.swing.border.Border; + +/** + * Cette classe représente la grille de jeu pour le Sudoku. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSGrid extends JLabel { + + private static final int GRID_SIZE = 9; + + private int[][] tableauGrille = new int[GRID_SIZE][GRID_SIZE]; + private GSCase[][] cases = new GSCase[GRID_SIZE][GRID_SIZE]; + private GSMenu menu; + + private int activeX = 0; + private int activeY = 0; + private Boolean isPlaying; + + /** + * Constructeur de la classe GSGrid. + * @param menu Le menu associé à la grille. + */ + public GSGrid(GSMenu menu) { + this.menu = menu; + initializeGrid(); + } + + // Initialise la grille avec des cases vides + private void initializeGrid() { + FlowLayout gestionnaire = new FlowLayout(); + this.setLayout(gestionnaire); + JPanel gridContainer = new JPanel(); + gridContainer.setLayout(new GridLayout(GRID_SIZE, GRID_SIZE)); + Dimension cellSize = new Dimension(91, 71); + + for (int i = 0; i < GRID_SIZE; i++) { + for (int j = 0; j < GRID_SIZE; j++) { + cases[i][j] = new GSCase(this, i, j); + setCellBorder(i, j); + cases[i][j].setPreferredSize(cellSize); + gridContainer.add(cases[i][j]); + } + } + this.add(gridContainer); + } + + // Détermine les bordures des cellules de la grille + private Border determineBorder(int i, int j) { + int top = (i == 0) ? 5 : 1; + int bottom = ((i + 1) % 3 == 0) ? 5 : 1; + int left = (j == 0) ? 5 : 1; + int right = ((j + 1) % 3 == 0) ? 5 : 1; + return BorderFactory.createMatteBorder(top, left, bottom, right, Color.BLACK); + } + + // Applique les bordures aux cellules de la grille + private void setCellBorder(int i, int j) { + cases[i][j].setBorder(determineBorder(i, j)); + } + + /** + * Importe les données du Sudoku dans la grille. + * @param go Tableau d'entiers représentant les données du Sudoku. + */ + public void importGrid(int[] go){ + int i,j; + int longueur; + for (i = 0; i < 9 ; i++) { + longueur = String.valueOf(go[i]).length(); + + for ( j = 0; j < 9 - longueur; j++) { + this.tableauGrille[i][j] = 0; + this.cases[i][j].setValue(this.tableauGrille[i][j]); + this.cases[i][j].repaint(); + } + int[] transfert = new int[longueur]; + String str = Integer.toString(go[i]); + for ( j = 0; j < longueur; j++) { + transfert[j] = (int) Character.getNumericValue(str.charAt(j)); + } + int k = 0; + for ( j = 9 - longueur; j < 9; j++) { + this.tableauGrille[i][j] = transfert[k]; + k++; + this.cases[i][j].setValue(this.tableauGrille[i][j]); + this.cases[i][j].repaint(); + } + } + this.menu.enablePlayOptions(); + } + + /** + * Récupère la valeur d'une case à une position spécifique dans la grille. + * @param i L'indice de ligne de la case. + * @param j L'indice de colonne de la case. + * @return La valeur de la case à la position spécifiée. + */ + public int getCellValue(int i, int j) { + return cases[i][j].getValue(); + } + + // Désactive les cases autres que celle indiquée + public void testActivity(int x, int y){ + for (int i = 0 ; i < GRID_SIZE ; i++ ) { + for (int j = 0 ; j < GRID_SIZE ; j++ ) { + if (cases[i][j].getActivity() && (i != x || j != y)) { + activeX = x; + activeY = y; + cases[i][j].deactivateCell(); + } + } + } + } + + // Retourne l'index X de la case active + public int whoIsActive_X() { + return activeX; + } + + // Retourne l'index Y de la case active + public int whoIsActive_Y() { + return activeY; + } + + // Met à jour la valeur d'une case dans la grille + public void setValuetoCase(int x, int y, int val) { + if (cases[x][y].getActivity() && getMode()) { + cases[x][y].updateValue(val); + } + } + + // Vérifie si la grille est complète + public Boolean isComplete(){ + for (int i = 0 ; i < 9 ; i++ ) { + for (int j = 0 ; j < 9 ; j++ ) { + if (this.cases[i][j].getDigitCount() != 1 && this.cases[i][j].getValue() == 0) { + return true; + } + } + } + return false; + } + + // Active ou désactive le mode de jeu + public void isPlaying(Boolean _bool) { + this.isPlaying = _bool; + } + + // Retourne le mode de jeu + public Boolean getMode() { + return this.isPlaying; + } + + // Résout le Sudoku + public boolean solve() { + for (int row = 0; row < 9; row++) { + for (int column = 0; column < 9; column++) { + if (this.tableauGrille[row][column] == 0) { + for (int k = 1; k <= 9; k++) { + this.tableauGrille[row][column] = k; + this.cases[row][column].insertValue(k); + GSTest _test = new GSTest(this); + if (_test.isValid(row, column) && solve()) { + return true; + } + this.tableauGrille[row][column] = 0; + this.cases[row][column].insertValue(0); + } + return false; + } + } + } + return true; + } + +} diff --git a/src/GridSolver/GSImport.java b/src/GridSolver/GSImport.java new file mode 100755 index 0000000..e6cc767 --- /dev/null +++ b/src/GridSolver/GSImport.java @@ -0,0 +1,84 @@ +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import java.io.*; + +/** + * La classe GSImport est utilisée pour importer une grille à partir d'un fichier. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSImport { + + private Window previousFrame; + private boolean accessible; + private File file; + private int[] importedValues = new int[9]; + + /** + * Constructeur pour créer une instance de GridMakerImport. + * @param frame La fenêtre précédente + * @param sudokuGrid La grille Sudoku + */ + public GSImport(Window frame) { + this.previousFrame = frame; + } + + /** + * Affiche une boîte de dialogue de sélection de fichier et importe la grille à partir du fichier sélectionné. + */ + public void importGrid() { + JFileChooser fileChooser = new JFileChooser(); + FileNameExtensionFilter filter = new FileNameExtensionFilter("Grid files (*.gri)", "gri"); + fileChooser.setFileFilter(filter); + int returnVal = fileChooser.showOpenDialog(previousFrame); + if (returnVal == JFileChooser.APPROVE_OPTION) { + file = fileChooser.getSelectedFile(); + if (readFile()) { + accessible = true; + } else { + accessible = false; + } + } + } + + /** + * Lit les données à partir du fichier sélectionné et les stocke dans un tableau. + * @return true si la lecture est réussie, false sinon + */ + public boolean readFile() { + try { + FileInputStream fileInputStream = new FileInputStream(file); + DataInputStream dataInputStream = new DataInputStream(fileInputStream); + int incrementable = 0; + while (dataInputStream.available() > 0 && incrementable < 9) { + importedValues[incrementable] = dataInputStream.readInt(); + incrementable++; + } + dataInputStream.close(); + return true; + } catch (FileNotFoundException e) { + System.err.println("File not found."); + return false; + } catch (IOException e) { + System.err.println("IOException."); + return false; + } catch (NumberFormatException e) { + System.err.println("NumberFormatException."); + return false; + } + } + + + public boolean isAccessible() { + return accessible; + } + + /** + * Gets the array of imported values. + * @return the array of imported values + */ + public int[] getImportedValues() { + return importedValues; + } +} diff --git a/src/GridSolver/GSMenu.java b/src/GridSolver/GSMenu.java new file mode 100755 index 0000000..6216c63 --- /dev/null +++ b/src/GridSolver/GSMenu.java @@ -0,0 +1,88 @@ +import javax.swing.*; +import java.awt.*; + +/** + * La classe GSMenu représente le menu jouer du jeu Sudoku. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSMenu { + + private Window window; // Fenêtre dans laquelle le menu est affiché + private JPanel titlePanel; // Panneau pour le titre + private JPanel buttonPanel; // Panneau pour les boutons + private Title titleLabel; // Étiquette pour le titre + private Button importerButton; // Bouton pour importer une grille + private Button jouerButton; // Bouton pour commencer à jouer + private Button autoSolveButton; // Bouton pour résoudre automatiquement la grille + + /** + * Constructeur de la classe GSMenu. + * @param window La fenêtre dans laquelle afficher le menu. + */ + public GSMenu(Window window) { + this.window = window; + this.window.setLayout(new BorderLayout()); + + // Initialisation du panneau de titre + this.titlePanel = new JPanel(); + this.titlePanel.setBackground(new Color(54, 91, 109)); + this.titlePanel.setLayout(new GridLayout(2, 1)); + + // Création des étiquettes de titre et sous-titre + this.titleLabel = new Title("Jouer", new Font("Copperplate", Font.BOLD, 45), Color.WHITE); + + // Ajout des étiquettes au panneau de titre + this.titlePanel.add(this.titleLabel); + + // Initialisation du panneau de boutons + this.buttonPanel = new JPanel(); + this.buttonPanel.setLayout(new GridLayout(1, 3, 10, 0)); + this.buttonPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + this.buttonPanel.setBackground(new Color(54, 91, 109)); + + // Création des boutons + this.importerButton = new Button("Charger une grille"); + this.jouerButton = new Button("Jouer"); + this.jouerButton.setEnabled(false); // Le bouton "Jouer" est désactivé par défaut + this.autoSolveButton = new Button("Résolution automatique"); + this.autoSolveButton.setEnabled(false); // Le bouton "Résolution automatique" est désactivé par défaut + + // Ajout des boutons au panneau de boutons + this.buttonPanel.add(this.importerButton); + this.buttonPanel.add(this.jouerButton); + this.buttonPanel.add(this.autoSolveButton); + + // Ajout des panneaux à la fenêtre + this.window.add(this.titlePanel, BorderLayout.NORTH); + this.window.add(this.buttonPanel, BorderLayout.CENTER); + + // Définition du titre de la page + this.window.setPageTitle("Menu jouer"); + + // Ajustement de la taille de la fenêtre en fonction de son contenu + this.window.pack(); + } + + /** + * Active les options de jeu dans le menu. + */ + public void enablePlayOptions() { + this.jouerButton.setEnabled(true); // Active le bouton "Jouer" + this.autoSolveButton.setEnabled(true); // Active le bouton "Résolution automatique" + } + + // Méthodes getters pour les composants + public Button getImporterButton() { + return this.importerButton; + } + + public Button getJouerButton() { + return this.jouerButton; + } + + public Button getAutoSolveButton() { + return this.autoSolveButton; + } +} diff --git a/src/GridSolver/GSMenuController.java b/src/GridSolver/GSMenuController.java new file mode 100644 index 0000000..79c69ab --- /dev/null +++ b/src/GridSolver/GSMenuController.java @@ -0,0 +1,60 @@ +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * La classe GSMenuController gère les actions déclenchées par les boutons du menu. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSMenuController implements ActionListener { + private GSMenu gsMenu; // Menu Sudoku + private Window mainWindow; // Fenêtre principale + private GSGrid sudokuGrid; // Grille de Sudoku + + /** + * Constructeur de la classe GSMenuController. + * @param gsMenu Le menu Sudoku à contrôler. + * @param mainWindow La fenêtre principale. + */ + public GSMenuController(GSMenu gsMenu, Window mainWindow) { + this.gsMenu = gsMenu; + this.mainWindow = mainWindow; + this.sudokuGrid = new GSGrid(gsMenu); // Initialise la grille de Sudoku + + // Ajout de l'action listener pour les boutons du menu + gsMenu.getImporterButton().addActionListener(this); + gsMenu.getJouerButton().addActionListener(this); + gsMenu.getAutoSolveButton().addActionListener(this); + } + + /** + * Méthode appelée lorsqu'une action est effectuée (clic sur un bouton). + * @param e L'événement associé à l'action. + */ + @Override + public void actionPerformed(ActionEvent e) { + // Si le bouton "Importer" est cliqué + if (e.getSource() == gsMenu.getImporterButton()) { + GSImport importer = new GSImport(mainWindow); // Crée un gestionnaire d'importation de grille + importer.importGrid(); // Importe une grille + if (importer.isAccessible()) { + sudokuGrid.importGrid(importer.getImportedValues()); // Met à jour la grille avec les valeurs importées + // Réactive les options de jeu dans le menu + gsMenu.enablePlayOptions(); + sudokuGrid.isPlaying(true); // Indique que le jeu est en cours + } + } + // Si le bouton "Jouer" est cliqué + else if (e.getSource() == gsMenu.getJouerButton()) { + GSPlay jeu = new GSPlay(this.mainWindow,this.sudokuGrid); // Crée un jeu Sudoku + GSPlayController jeuController = new GSPlayController(jeu); // Crée un contrôleur pour le jeu + gsMenu.getJouerButton().addKeyListener(jeuController); // Ajoute un écouteur de touches pour le jeu + jeu.showGame(); // Affiche le jeu + } + // Si le bouton "Résoudre automatiquement" est cliqué + else if (e.getSource() == gsMenu.getAutoSolveButton()) { + GSSolver resolveurDeGrille = new GSSolver(this.sudokuGrid,this.mainWindow); // Crée un résolveur de grille + } + } +} diff --git a/src/GridSolver/GSPlay.java b/src/GridSolver/GSPlay.java new file mode 100755 index 0000000..bb4d60d --- /dev/null +++ b/src/GridSolver/GSPlay.java @@ -0,0 +1,108 @@ +import java.awt.*; +import java.awt.event.*; + +/** + * Classe GSPlay pour jouer au Sudoku. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSPlay { + + // Valeur représentant une case vide + private static final int EMPTY_VALUE = 0; + + // Codes des touches numériques du pavé numérique + private static final int[] NUM_KEYS = {KeyEvent.VK_NUMPAD1, KeyEvent.VK_NUMPAD2, KeyEvent.VK_NUMPAD3, KeyEvent.VK_NUMPAD4, KeyEvent.VK_NUMPAD5, + KeyEvent.VK_NUMPAD6, KeyEvent.VK_NUMPAD7, KeyEvent.VK_NUMPAD8, KeyEvent.VK_NUMPAD9}; + + // Codes des touches numériques du clavier + private static final int[] KEY_NUMBERS = {KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_3, KeyEvent.VK_4, KeyEvent.VK_5, + KeyEvent.VK_6, KeyEvent.VK_7, KeyEvent.VK_8, KeyEvent.VK_9}; + + // Code de la touche de suppression + private static final int DELETE_KEY = KeyEvent.VK_BACK_SPACE; + + private Container content; + private GSGrid ma_Grille; + private Button boutonValider = new Button("Valider"); + private long startTime; + private long vraiTime; + private Window gameplay; + private GSPlayController gsPlayController; + + /** + * Constructeur de la classe GSPlay. + * @param grille La grille de Sudoku. + * @param frame La fenêtre principale. + */ + public GSPlay(Window window, GSGrid grille) { + this.ma_Grille = grille; + this.gameplay = window; + this.gsPlayController = new GSPlayController(this); + } + + + /** + * Méthode pour afficher la fenêtre de jeu. + */ + public void showGame() { + Window.removeAllComponents(this.gameplay); + this.gameplay.setPageTitle("Jouer"); + this.startTime = System.nanoTime(); + content = this.gameplay.getContentPane(); + BorderLayout gestionnaireGameplay = new BorderLayout(); + this.gameplay.setLayout(gestionnaireGameplay); + this.gameplay.setSize(650, 730); + this.gameplay.setFocusable(true); + this.gameplay.requestFocusInWindow(); + this.gameplay.addKeyListener(gsPlayController); + this.boutonValider.setEnabled(false); + this.vraiTime = System.nanoTime() - this.startTime; + boutonValider.addActionListener(gsPlayController); + this.gameplay.add(boutonValider, BorderLayout.SOUTH); + content.add(this.ma_Grille, BorderLayout.CENTER); + } + + /** + * Méthode pour obtenir la valeur associée à une touche. + * @param e L'événement KeyEvent associé à la touche. + * @return La valeur correspondante à la touche ou -1 si aucune correspondance. + */ + public int getKeyValue(KeyEvent e) { + int keyCode = e.getKeyCode(); + if (keyCode == DELETE_KEY) { + return EMPTY_VALUE; + } + for (int i = 0; i < NUM_KEYS.length; i++) { + if (keyCode == NUM_KEYS[i] || keyCode == KEY_NUMBERS[i]) { + return i + 1; + } + } + return -1; + } + + /** + * Méthode pour vérifier si le jeu est terminé. + * @return true si le jeu est terminé, sinon false. + */ + public Boolean isGameOver() { + return !this.ma_Grille.isComplete(); + } + + public Button getBoutonValider() { + return boutonValider; + } + + public GSGrid getMaGrille() { + return ma_Grille; + } + + /** + * Méthode pour obtenir le temps de démarrage du jeu. + * @return Le temps de démarrage du jeu. + */ + public long getStartTime() { + return this.startTime; + } +} diff --git a/src/GridSolver/GSPlayController.java b/src/GridSolver/GSPlayController.java new file mode 100644 index 0000000..de44857 --- /dev/null +++ b/src/GridSolver/GSPlayController.java @@ -0,0 +1,69 @@ +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +/** + * Le contrôleur pour le jeu de la grille. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSPlayController implements KeyListener, ActionListener { + private GSPlay gsPlay; + + /** + * Constructeur du contrôleur du jeu de la grille. + * @param gsPlay Le jeu de la grille associé à ce contrôleur. + */ + public GSPlayController(GSPlay gsPlay) { + this.gsPlay = gsPlay; + } + + /** + * Gère les actions lorsqu'un événement se produit. + * @param e L'événement déclenché. + */ + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == gsPlay.getBoutonValider()) { + // Crée une fenêtre de dialogue pour afficher le temps écoulé depuis le début du jeu. + GSWin gestionVictoire = new GSWin(gsPlay.getStartTime()); + gestionVictoire.showDialog(); + } + } + + /** + * Gère les événements lorsque la touche est enfoncée. + * @param e L'événement de la touche enfoncée. + */ + @Override + public void keyPressed(KeyEvent e) { + int keyValue = gsPlay.getKeyValue(e); + if (keyValue != -1) { + // Met à jour la valeur dans la case active de la grille avec la touche appuyée. + gsPlay.getMaGrille().setValuetoCase(gsPlay.getMaGrille().whoIsActive_X(), gsPlay.getMaGrille().whoIsActive_Y(), keyValue); + // Active le bouton de validation si le jeu est terminé. + if (gsPlay.isGameOver()) { + gsPlay.getBoutonValider().setEnabled(true); + } + } + } + + /** + * Gère les événements lorsque la touche est relâchée. + * @param e L'événement de la touche relâchée. + */ + @Override + public void keyReleased(KeyEvent e) { + } + + /** + * Gère les événements lorsque la touche est tapée. + * @param e L'événement de la touche tapée. + */ + @Override + public void keyTyped(KeyEvent e) { + // Non utilisé + } +} diff --git a/src/GridSolver/GSSolver.java b/src/GridSolver/GSSolver.java new file mode 100755 index 0000000..e74be62 --- /dev/null +++ b/src/GridSolver/GSSolver.java @@ -0,0 +1,56 @@ +import javax.swing.*; +import java.awt.*; + +/** + * La classe GSSolver résout une grille de Sudoku et affiche le résultat dans une fenêtre. + * @version 1.O + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSSolver { + + private GSGrid grid; // Grille de Sudoku à résoudre + private Window window; // Fenêtre dans laquelle afficher la résolution + private JLabel label = new JLabel("Resolution en cours..."); // Étiquette pour afficher le statut de la résolution + private long startTime; // Temps de début de la résolution + + /** + * Constructeur de la classe GSSolver. + * @param grid La grille de Sudoku à résoudre. + * @param window La fenêtre dans laquelle afficher la résolution. + */ + public GSSolver(GSGrid grid, Window window) { + this.grid = grid; + this.window = window; + Window.removeAllComponents(this.window); // Efface tous les composants de la fenêtre + this.window.setPageTitle("Résolution automatique"); + this.startSolving(); // Démarre la résolution + } + + /** + * Démarre le processus de résolution de la grille de Sudoku. + */ + private void startSolving() { + startTime = System.nanoTime(); // Enregistre le temps de début de la résolution + BorderLayout layout = new BorderLayout(); // Gestionnaire de mise en page pour la fenêtre + window.setLayout(layout); // Définit le gestionnaire de mise en page pour la fenêtre + window.getContentPane().add(label, BorderLayout.SOUTH); // Ajoute l'étiquette au bas de la fenêtre + window.getContentPane().add(grid, BorderLayout.CENTER); // Ajoute la grille au centre de la fenêtre + grid.solve(); // Résout la grille de Sudoku + + // Vérifie si le jeu est terminé + if (isGameOver()) { + double time = (double) (System.nanoTime() - startTime) / 1_000_000_000; // Calcule le temps écoulé en secondes + label.setText("Résolu en " + time + " secondes."); // Met à jour le texte de l'étiquette avec le temps écoulé + label.setForeground(Color.WHITE); // Définit la couleur du texte sur blanc + } + } + + /** + * Vérifie si le jeu est terminé. + * @return true si le jeu est terminé, false sinon. + */ + private boolean isGameOver() { + return !grid.isComplete(); // Vérifie si la grille est complète + } +} diff --git a/src/GridSolver/GSTest.java b/src/GridSolver/GSTest.java new file mode 100755 index 0000000..d506064 --- /dev/null +++ b/src/GridSolver/GSTest.java @@ -0,0 +1,137 @@ +/** + * La classe GSTest contient les méthodes pour tester la validité d'une grille de Sudoku. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSTest { + + private GSGrid ma_Grille; + + /** + * Constructeur de la classe GSTest. + * @param Grid La grille de Sudoku à tester. + */ + public GSTest(GSGrid Grid) { + this.ma_Grille = Grid; + } + + /** + * Vérifie s'il y a des doublons dans la grille de Sudoku. + * @return true s'il y a des doublons, false sinon. + */ + public boolean test() { + // Vérification colonne + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + for (int i_prime = i + 1; i_prime < 9; i_prime++) { + if (this.ma_Grille.getCellValue(i, j) == this.ma_Grille.getCellValue(i_prime, j) && this.ma_Grille.getCellValue(i, j) != 0) { + return true; + } + } + } + } + + // Vérification ligne + for (int j = 0; j < 9; j++) { + for (int i = 0; i < 9; i++) { + for (int j_prime = j + 1; j_prime < 9; j_prime++) { + if (this.ma_Grille.getCellValue(i, j) == this.ma_Grille.getCellValue(i, j_prime) && this.ma_Grille.getCellValue(i, j) != 0) { + return true; + } + } + } + } + + // Vérification région + for (int i = 0; i < 9; i += 3) { + for (int j = 0; j < 9; j += 3) { + if (verificationRegion(i, j)) { + return true; + } + } + } + return false; // Aucun doublon trouvé dans la grille + } + + /** + * Vérifie s'il y a des doublons dans une région spécifique de la grille. + * @param x L'indice x du coin supérieur gauche de la région. + * @param y L'indice y du coin supérieur gauche de la région. + * @return true s'il y a des doublons, false sinon. + */ + public boolean verificationRegion(int x, int y) { + final int REGION_SIZE = 3; // Taille de chaque région (3x3) + + // Calcul des coordonnées du coin supérieur gauche de la région spécifiée + int regionX = (x / REGION_SIZE) * REGION_SIZE; + int regionY = (y / REGION_SIZE) * REGION_SIZE; + + // Tableau pour suivre les valeurs déjà vues dans la région (de 1 à 9) + boolean[] seen = new boolean[10]; + + // Parcours de chaque cellule de la région spécifiée + for (int i = 0; i < REGION_SIZE; i++) { + for (int j = 0; j < REGION_SIZE; j++) { + // Obtention de la valeur de la cellule + int value = this.ma_Grille.getCellValue(regionX + i, regionY + j); + // Vérification si la valeur est différente de zéro (cellule remplie) + if (value != 0) { + // Si la valeur a déjà été vue dans la région, il y a un doublon + if (seen[value]) { + return true; // Valeur en double trouvée + } + seen[value] = true; // Marquage de la valeur comme vue + } + } + } + return false; // Aucune valeur en double trouvée dans la région + } + + /** + * Vérifie s'il y a des doublons dans une ligne spécifique de la grille. + * @param x L'indice de la ligne à vérifier. + * @return true s'il y a des doublons, false sinon. + */ + public boolean verificationLigne(int x) { + int i = x; + + for (int j = 0; j < 8; j++) { // Parcours des colonnes jusqu'à l'avant-dernière colonne + for (int j_prime = j + 1; j_prime < 9; j_prime++) { // Parcours des colonnes suivantes + // Comparaison des valeurs des cellules + if (ma_Grille.getCellValue(i, j) == ma_Grille.getCellValue(i, j_prime) && ma_Grille.getCellValue(i, j) != 0) { + return true; // Doublon trouvé, on peut retourner true directement + } + } + } + return false; // Aucun doublon trouvé dans la ligne + } + + /** + * Vérifie s'il y a des doublons dans une colonne spécifique de la grille. + * @param y L'indice de la colonne à vérifier. + * @return true s'il y a des doublons, false sinon. + */ + public boolean verificationColonne(int y) { + int j = y; + for (int i = 0; i < 8; i++) { // Parcours des lignes jusqu'à l'avant-dernière ligne + for (int i_prime = i + 1; i_prime < 9; i_prime++) { // Parcours des lignes suivantes + // Comparaison des valeurs des cellules + if (ma_Grille.getCellValue(i, j) == ma_Grille.getCellValue(i_prime, j) && ma_Grille.getCellValue(i, j) != 0) { + return true; // Doublon trouvé, on peut retourner true directement + } + } + } + return false; // Aucun doublon trouvé dans la colonne + } + + /** + * Vérifie si une cellule spécifique de la grille est valide. + * @param row L'indice de la ligne de la cellule. + * @param column L'indice de la colonne de la cellule. + * @return true si la cellule est valide, false sinon. + */ + public Boolean isValid(int row, int column){ + return (!verificationLigne(row) && !verificationColonne(column) && !verificationRegion(row, column)); + } +} diff --git a/src/GridSolver/GSWin.java b/src/GridSolver/GSWin.java new file mode 100755 index 0000000..d1b207b --- /dev/null +++ b/src/GridSolver/GSWin.java @@ -0,0 +1,27 @@ +/** + * La classe GSWin gère l'affichage d'une fenêtre de félicitations pour avoir résolu le Sudoku. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class GSWin implements DialogManager { + + private long solvingTime; // Temps de résolution du Sudoku + + /** + * Constructeur de la classe GSWin. + * @param solvingTime Le temps de résolution du Sudoku. + */ + public GSWin(long solvingTime) { + this.solvingTime = solvingTime; + } + + /** + * Affiche la fenêtre de félicitations pour avoir résolu le Sudoku. + */ + @Override + public void showDialog() { + // Créer et afficher une nouvelle fenêtre de félicitations + CongratulationsDialog congratsWindow = new CongratulationsDialog(solvingTime); + } +} diff --git a/src/GridSolver/HomeButtonClickListener.java b/src/GridSolver/HomeButtonClickListener.java new file mode 100644 index 0000000..2706b76 --- /dev/null +++ b/src/GridSolver/HomeButtonClickListener.java @@ -0,0 +1,48 @@ +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Listener for button clicks in the menu. + * It performs different actions based on the button clicked. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +class HomeButtonClickListener implements ActionListener { + private Window window; + private DialogManager rulesDialogManager; + private GSMenu menuJeu; + + /** + * Constructs a ButtonClickListener with the specified window. + * @param window The window where the actions will be performed. + */ + public HomeButtonClickListener(Window window) { + this.window = window; + this.rulesDialogManager = new RulesDialogManager(); + } + + /** + * Performs an action based on the button clicked. + * @param e The ActionEvent representing the button click. + */ + @Override + public void actionPerformed(ActionEvent e) { + String buttonText = ((Button) e.getSource()).getText(); + switch (buttonText) { + case "Jouer": + Window.removeAllComponents(this.window); + this.menuJeu = new GSMenu(this.window); + GSMenuController menuController = new GSMenuController(this.menuJeu, this.window); + break; + case "Règles": + rulesDialogManager.showDialog(); // Afficher les règles + break; + case "Quitter": + System.exit(0); // Quitter le programme + break; + default: + break; + } + } +} diff --git a/src/GridSolver/HomeView.java b/src/GridSolver/HomeView.java new file mode 100644 index 0000000..eab4167 --- /dev/null +++ b/src/GridSolver/HomeView.java @@ -0,0 +1,103 @@ +import javax.swing.*; +import java.awt.*; + +/** + * HomeView représente la vue de la page d'accueil de l'application Sudoku. + * Cette classe étend JPanel et affiche les éléments de la page d'accueil, y compris le titre, les boutons et les contrôles audio. + * Elle utilise également les classes Title, Button, et MusicButton. + * + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class HomeView extends JPanel { + + // Constantes pour les chemins des icônes et des fichiers audio, ainsi que pour les dimensions et les couleurs + private final String AUDIO_ON = "img/iconeAudio.png"; + private final String AUDIO_OFF = "img/iconeAudioMuted.png"; + private final String MUSIC_FILE = "audio/musiqueDeFond.wav"; + private final Dimension BUTTON_SIZE = new Dimension(300, 60); + private final Color BACKGROUND_COLOR = new Color(54, 91, 109); + private final Color TITLE_TEXT_COLOR = Color.WHITE; + private final Font TITLE_FONT = new Font("Copperplate", Font.BOLD, 75); + private final Font SUBTITLE_FONT = new Font("Copperplate", Font.PLAIN, 24); + private final Font BUTTON_FONT = new Font("Copperplate", Font.BOLD, 24); + private final String[] BUTTON_TEXTS = {"Jouer", "Règles", "Quitter"}; + + // Tableau de titres pour le titre principal et le sous-titre + private final Title[] labels = { + new Title("Sudoku Game", TITLE_FONT, TITLE_TEXT_COLOR), + new Title("Par Moncef & Marco", SUBTITLE_FONT, TITLE_TEXT_COLOR) + }; + + private MusicButton musicButton; // Bouton pour contrôler la musique + private final Window window; // Fenêtre parente + private JPanel titlePanel; // Panneau pour le titre + private JPanel buttonPanel; // Panneau pour les boutons + private JLabel imageLabel; // Étiquette pour l'image + + /** + * Constructeur de la classe HomeView. + * Initialise la fenêtre parente et crée les composants de la page d'accueil. + * @param window La fenêtre parente. + */ + public HomeView(Window window) { + this.window = window; + createComponents(); + addComponentsToWindow(); + } + + /** + * Crée les composants de la page d'accueil, y compris les panneaux de titre et de boutons. + */ + private void createComponents() { + titlePanel = new JPanel(); + buttonPanel = new JPanel(); + ImageIcon iconeSudoku = new ImageIcon("img/sudoku.png"); + imageLabel = new JLabel(iconeSudoku); + + // Configuration du panneau de titre + GridLayout titleLayout = new GridLayout(2, 1); + titlePanel.setLayout(titleLayout); + titlePanel.setBackground(BACKGROUND_COLOR); + // Utilisation de la classe Title pour le titre et le sous-titre + for (Title label : labels) { + titlePanel.add(label); + } + + // Configuration du panneau de boutons + GridLayout buttonLayout = new GridLayout(BUTTON_TEXTS.length, 1, 0, 10); + buttonPanel.setLayout(buttonLayout); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + buttonPanel.setBackground(BACKGROUND_COLOR); + HomeButtonClickListener listenerButton = new HomeButtonClickListener(window); + for (String text : BUTTON_TEXTS) { + Button button = new Button(text, BUTTON_SIZE, BUTTON_FONT, BACKGROUND_COLOR); + button.addActionListener(listenerButton); + buttonPanel.add(button); + } + + musicButton = new MusicButton(AUDIO_ON, AUDIO_OFF, MUSIC_FILE); // Bouton pour contrôler la musique + } + + /** + * Ajoute les composants créés à la fenêtre parente. + */ + public void addComponentsToWindow() { + BorderLayout layout = new BorderLayout(); + window.getContentPane().setLayout(layout); + window.add(titlePanel, BorderLayout.NORTH); + window.add(buttonPanel, BorderLayout.WEST); + window.add(imageLabel, BorderLayout.EAST); + window.setPageTitle("Menu principal"); // Définit le titre de la page dans la fenêtre + + FlowLayout controlPanelLayout = new FlowLayout(FlowLayout.RIGHT); + JPanel controlPanel = new JPanel(controlPanelLayout); // Panneau pour les contrôles audio + controlPanel.setBackground(BACKGROUND_COLOR); + controlPanel.add(musicButton); // Ajoute le bouton de contrôle audio + window.add(controlPanel, BorderLayout.SOUTH); // Ajoute le panneau de contrôles à la fenêtre + + window.pack(); // Ajuste la taille de la fenêtre pour s'adapter à son contenu + window.setVisible(true); // Rend la fenêtre visible + } +} diff --git a/src/GridSolver/Main.java b/src/GridSolver/Main.java new file mode 100644 index 0000000..cf5d61f --- /dev/null +++ b/src/GridSolver/Main.java @@ -0,0 +1,6 @@ +public class Main{ + public static void main(String[] args) { + Window fenetre = new Window(); // Création d'une fenêtre + HomeView menu = new HomeView(fenetre); // Création du menu sur la fenêtre + } +} \ No newline at end of file diff --git a/src/GridSolver/MusicButton.java b/src/GridSolver/MusicButton.java new file mode 100644 index 0000000..a383176 --- /dev/null +++ b/src/GridSolver/MusicButton.java @@ -0,0 +1,47 @@ +import javax.swing.*; + +/** + * It provides a button that toggles between playing and stopping music when clicked. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ + public class MusicButton extends JButton { + private static MusicPlayer currentMusicPlayer; + private ImageIcon iconOn; + private ImageIcon iconOff; + private MusicPlayer musicPlayer; + + /** + * Constructs a MusicButton. + * @param onIconPath The file path for the icon when music is on. + * @param offIconPath The file path for the icon when music is off. + * @param musicFilePath The file path for the music file to be played. + */ + public MusicButton(String onIconPath, String offIconPath, String musicFilePath) { + + this.iconOn = new ImageIcon(onIconPath); + this.iconOff = new ImageIcon(offIconPath); + setIcon(this.iconOff); + + // Vérifie s'il y a déjà une musique en cours de lecture et l'arrête si nécessaire + if (currentMusicPlayer != null && currentMusicPlayer.isPlaying()) { + currentMusicPlayer.stop(); + currentMusicPlayer = null; + } + + this.musicPlayer = new MusicPlayer(musicFilePath); + + addActionListener(e -> { + if (currentMusicPlayer != null && currentMusicPlayer.isPlaying()) { + currentMusicPlayer.stop(); + currentMusicPlayer = null; + setIcon(this.iconOff); + } else { + this.musicPlayer.play(); + setIcon(this.iconOn); + currentMusicPlayer = this.musicPlayer; + } + }); + } +} diff --git a/src/GridSolver/MusicPlayer.java b/src/GridSolver/MusicPlayer.java new file mode 100644 index 0000000..0a451e8 --- /dev/null +++ b/src/GridSolver/MusicPlayer.java @@ -0,0 +1,57 @@ +import java.io.File; +import javax.sound.sampled.*; + +/** + * Class containign a simple music player that allows playing and stopping music. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ + +public class MusicPlayer { + private Clip clip; + private boolean isPlaying; + + /** + * Constructs a MusicPlayer with the specified file path. + * @param filePath The path to the music file to be played. + */ + public MusicPlayer(String filePath) { + try { + File file = new File(filePath); + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file); + this.clip = AudioSystem.getClip(); + this.clip.open(audioInputStream); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Starts playing the music. + */ + public void play() { + if (this.clip != null && !this.isPlaying) { + this.clip.start(); + this.isPlaying = true; + } + } + + /** + * Stops the music. + */ + public void stop() { + if (this.clip != null && this.isPlaying) { + this.clip.stop(); + this.isPlaying = false; + } + } + + /** + * Checks if the music is currently playing. + * @return true if the music is playing, false otherwise. + */ + public boolean isPlaying() { + return this.isPlaying; + } +} diff --git a/src/GridSolver/RulesDialogManager.java b/src/GridSolver/RulesDialogManager.java new file mode 100644 index 0000000..c903d04 --- /dev/null +++ b/src/GridSolver/RulesDialogManager.java @@ -0,0 +1,20 @@ +import javax.swing.JOptionPane; + +/** + * RulesDialogManager gère l'affichage de la boîte de dialogue des règles. + * Cette classe implémente DialogManager pour définir la méthode showDialog. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class RulesDialogManager implements DialogManager { + /** + * Affiche la boîte de dialogue des règles du Sudoku. + */ + @Override + public void showDialog() { + RulesSudoku rulesPanel = new RulesSudoku(); // Création du panneau contenant les règles + JOptionPane.showMessageDialog(null, rulesPanel, "Règles du Sudoku", JOptionPane.PLAIN_MESSAGE); // Affichage de la boîte de dialogue + } +} + diff --git a/src/GridSolver/RulesSudoku.java b/src/GridSolver/RulesSudoku.java new file mode 100644 index 0000000..606dc34 --- /dev/null +++ b/src/GridSolver/RulesSudoku.java @@ -0,0 +1,49 @@ +import javax.swing.*; +import java.awt.*; + +/** + * RulesSudoku représente le panneau affichant les règles du Sudoku. + * Cette classe étend JPanel et définit le contenu des règles. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class RulesSudoku extends JPanel { + private Dimension FRAME_SIZE = new Dimension(400, 500); // Taille de la fenêtre des règles + private Color BACKGROUND_COLOR = new Color(54, 91, 109); // Couleur d'arrière-plan du panneau + + /** + * Constructeur par défaut de RulesSudoku. + * Initialise le contenu des règles et configure l'apparence du panneau. + */ + public RulesSudoku() { + BorderLayout gestionnaireBorderLayout = new BorderLayout(); + this.setLayout(gestionnaireBorderLayout); + this.setBackground(this.BACKGROUND_COLOR); // Couleur d'arrière-plan du panneau + + JLabel titleLabel = new JLabel("Règles du Sudoku"); + titleLabel.setFont(new Font("Copperplate", Font.BOLD, 40)); // Police du titre + titleLabel.setForeground(Color.WHITE); // Couleur du titre + + JTextArea rulesTextArea = new JTextArea(); + rulesTextArea.setText("Les règles du Sudoku :\n\n" + + "1. Le but du jeu est de remplir la grille avec une série de chiffres de 1 à 9 de telle sorte que chaque ligne, chaque colonne et chaque région de 3x3 contienne tous les chiffres de 1 à 9 sans répétition.\n\n" + + "2. Certains chiffres sont déjà placés dans la grille au départ et ne peuvent pas être modifiés.\n\n" + + "3. Utilisez la logique et le raisonnement pour remplir la grille avec les chiffres manquants.\n\n" + + "4. Le jeu est terminé lorsqu'il n'y a plus de cases vides et que toutes les règles sont respectées."); + rulesTextArea.setEditable(false); + rulesTextArea.setLineWrap(true); + rulesTextArea.setWrapStyleWord(true); + rulesTextArea.setFont(new Font("Arial", Font.PLAIN, 20)); // Police du texte des règles + rulesTextArea.setForeground(Color.WHITE); // Couleur du texte des règles + rulesTextArea.setBackground(this.BACKGROUND_COLOR); // Couleur d'arrière-plan du texte des règles + + JScrollPane scrollPane = new JScrollPane(rulesTextArea); + + this.add(titleLabel, BorderLayout.NORTH); + this.add(scrollPane, BorderLayout.CENTER); + + this.setPreferredSize(this.FRAME_SIZE); // Taille de la fenêtre des règles + } +} + diff --git a/src/GridSolver/Title.java b/src/GridSolver/Title.java new file mode 100644 index 0000000..c953378 --- /dev/null +++ b/src/GridSolver/Title.java @@ -0,0 +1,26 @@ +import javax.swing.*; +import java.awt.*; + +/** + * Title est une étiquette Swing personnalisée utilisée pour afficher un titre centré avec une police et une couleur spécifiées. + * Cette classe étend JLabel. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class Title extends JLabel { + + /** + * Constructeur de Title. + * Crée une étiquette avec le texte, la police et la couleur spécifiés, et la centre horizontalement. + * @param text Le texte à afficher. + * @param font La police à utiliser pour le texte. + * @param color La couleur du texte. + */ + public Title(String text, Font font, Color color) { + super(text, SwingConstants.CENTER); // Centre le texte horizontalement + setFont(font); // Définit la police du texte + setForeground(color); // Définit la couleur du texte + } +} + diff --git a/src/GridSolver/Window.java b/src/GridSolver/Window.java new file mode 100644 index 0000000..0cdf564 --- /dev/null +++ b/src/GridSolver/Window.java @@ -0,0 +1,71 @@ +import javax.swing.*; +import java.awt.*; + +/** + * Window est une classe représentant la fenêtre principale de l'application Sudoku. + * Cette classe étend JFrame et gère l'affichage des différentes pages de l'application. + * @version 1.0 + * @author Moncef STITI + * @author Marco ORFAO + */ +public class Window extends JFrame { + /** + * La taille minimale de la fenêtre. + */ + private static final Dimension MIN_WINDOW_SIZE = new Dimension(850, 700); + /** + * Le titre du programme. + */ + private static final String PROGRAM_TITLE = "Sudoku"; + + /** + * La couleur d'arrière plan par défaut de la fenêtre + */ + private static final Color BACKGROUND_COLOR = new Color(54, 91, 109); + + /** + * Le titre de la page actuelle. + */ + private String PAGE_TITLE = ""; + + /** + * Constructeur de la classe Window. + * Initialise la fenêtre avec le titre du programme, la taille minimale et la couleur de fond. + */ + public Window() { + super(PROGRAM_TITLE); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setMinimumSize(MIN_WINDOW_SIZE); + this.setLocationRelativeTo(null); + getContentPane().setBackground(BACKGROUND_COLOR); + } + + /** + * Obtient le titre de la page actuelle. + * @return Le titre de la page actuelle. + */ + public String getPageTitle() { + return this.PAGE_TITLE; + } + + /** + * Définit le titre de la page actuelle. + * Met à jour le titre de la fenêtre pour inclure le titre de la page et le titre du programme. + * @param title Le titre de la page actuelle. + */ + public void setPageTitle(String title) { + this.PAGE_TITLE = title; + this.setTitle(this.PAGE_TITLE + " - " + Window.PROGRAM_TITLE); + } + + /** + * Supprime tous les composants de la fenêtre. + * Utilisé pour effacer le contenu de la fenêtre. + * @param window La fenêtre à nettoyer. + */ + public static void removeAllComponents(Window window) { + window.getContentPane().removeAll(); // Supprime tous les composants de la fenêtre + window.revalidate(); // Revalide la disposition des composants + window.repaint(); // Redessine la fenêtre + } +}