Projet 2023

This commit is contained in:
Yles ZOURDANI 2023-05-25 14:37:01 +02:00
parent 4f68cae45c
commit 443f2c7ffc
28 changed files with 2638 additions and 1 deletions

2
.gitignore vendored
View File

@ -59,3 +59,5 @@ tags
# Built Visual Studio Code Extensions
*.vsix
# Apple
.DS_Store

73
Makefile Normal file
View File

@ -0,0 +1,73 @@
### VARIABLES ###
DOC = ./documentation/
SRC = ./sources/
JC = javac
JCFLAGS = -encoding UTF-8 -implicit:none -cp ./sources/
JVM = java -cp ./sources/
JVMFLAGS =
### REGLES ESSENTIELLES ###
${SRC}Main.class : ${SRC}Main.java ${SRC}MenuAccueil.class
${JC} ${JCFLAGS} ${SRC}Main.java
${SRC}MenuAccueil.class : ${SRC}MenuAccueil.java ${SRC}ControleurAccueil.class
${JC} ${JCFLAGS} ${SRC}MenuAccueil.java
${SRC}ControleurAccueil.class : ${SRC}ControleurAccueil.java ${SRC}MenuInitialisation.class
${JC} ${JCFLAGS} ${SRC}ControleurAccueil.java
${SRC}MenuInitialisation.class : ${SRC}MenuInitialisation.java ${SRC}ControleurInitialisation.class
${JC} ${JCFLAGS} ${SRC}MenuInitialisation.java
${SRC}ControleurInitialisation.class : ${SRC}ControleurInitialisation.java ${SRC}GrilleConfig.class
${JC} ${JCFLAGS} ${SRC}ControleurInitialisation.java
${SRC}GrilleConfig.class : ${SRC}GrilleConfig.java ${SRC}GrillePanel.class
${JC} ${JCFLAGS} ${SRC}GrilleConfig.java
${SRC}GrillePanel.class : ${SRC}GrillePanel.java ${SRC}MenuModification.class
${JC} ${JCFLAGS} ${SRC}GrillePanel.java
${SRC}MenuModification.class : ${SRC}MenuModification.java ${SRC}ControleurModification.class
${JC} ${JCFLAGS} ${SRC}MenuModification.java
${SRC}ControleurModification.class : ${SRC}ControleurModification.java ${SRC}ModificateurGrille.class
${JC} ${JCFLAGS} ${SRC}ControleurModification.java
${SRC}ModificateurGrille.class : ${SRC}ModificateurGrille.java ${SRC}MenuSimulation.class
${JC} ${JCFLAGS} ${SRC}ModificateurGrille.java
${SRC}MenuSimulation.class : ${SRC}MenuSimulation.java ${SRC}ControleurSimulation.class
${JC} ${JCFLAGS} ${SRC}MenuSimulation.java
${SRC}ControleurSimulation.class : ${SRC}ControleurSimulation.java ${SRC}Algorithmes.class
${JC} ${JCFLAGS} ${SRC}ControleurSimulation.java
${SRC}Algorithmes.class : ${SRC}Algorithmes.java
${JC} ${JCFLAGS} ${SRC}Algorithmes.java
### REGLES OPTIONNELLES ###
run : ${SRC}Main.class
${JVM} ${JVMFLAGS} Main
doc :
javadoc -d ${DOC} ${SRC}*.java
firefox ${DOC}allclasses-index.html
clean :
rm -f ${SRC}*.class
rm -r ${DOC}
mrproper : clean ${SRC}Main.class
### BUTS FACTICES ###
.PHONY : run clean mrproper
### FIN ###

144
README.md
View File

@ -1,2 +1,144 @@
# Projet_Java_Labyrinthe
# Situation d'Apprentissage et d'Évaluation (Semestre 2) - Java
## Sommaire
1. [Rappel du sujet](#rappel-du-sujet)
1. [Pricipes généraux](#principes-généraux)
2. [Fonctionnalités demandées](#fonctionnalités-demandées)
3. [Format de sauvegarde des grilles](#format-de-sauvegarde-des-grilles)
2. [Rapport](#rapport)
1. [Makefile](#makefile)
2. [Déroulement du programme](#déroulement-du-programme)
1. [Menu d'accueil](#menu-daccueil)
2. [Menu d'initialisation](#menu-dinitialisation)
3. [Menu de modification](#menu-de-modification)
4. [Menu de simulation](#menu-de-simulation)
## Rappel du sujet
### L'algorithme d'Ariane
Ce projet a pour but d'étudier un algorithme de guidage, visant à conduire un objet mobile jusqu'à son but à travers un parcours d'obstacles. En hommage à la mythologie grecque, nous supposerons qu'il s'agit de Thésée, perdu dans le labyrinthe crétois, à la recherche de la sortie (on aurait pu aussi choisir une souris recherchant un morceau de fromage, ou un robot cherchant son point de ravitaillement).
 
Vous produirez un programme écrit en Java, sans emprunt extérieur (sauf l'API officielle), et accompagné d'un rapport. Le travail sera fait seul ou en binôme (un binôme est fortement recommandé, afin de vous permettre de vous familiariser avec les techniques de développement collaboratif), et devra être terminé avant le **vendredi 28 avril 2023 à 23h59**.
 
La partie logicielle sera développée dès le départ à l'aide du [serveur Gitea du département](https://dwarves.iut-fbleau.fr/gitiut). Le rapport prendra la forme d'un fichier au format PDF joint aux sources.
#### Principes généraux
Pour simplifier le problème, le labyrinthe sera représenté sous la forme d'une grille carrée. Chaque cellule de la grille peut être bloquée ou libre. La position initiale de Thésée ainsi que celle de la sortie pourront être placées sur n'importe quelles cases libres distinctes.
![la grille du labyrinthe](img/grille.png)
À chaque étape de la simulation, Thésée a quatre options :
* se déplacer d'une case vers le nord,
* se déplacer d'une case vers le sud,
* se déplacer d'une case vers l'est,
* se déplacer d'une case vers l'ouest.
Ce mouvement peut avoir trois conséquences :
* la case de destination est bloquée (et Thésée n'a donc pas changé de case),
* la case de destination est libre (et Thésée s'y est rendu),
* la case de destination est la sortie (et la simulation est terminée).
L'algorithme que vous devez écrire dictera à Thésée son prochain déplacement à chaque étape.
#### Fonctionnalités demandées
Le programme se déroulera en trois parties : choix de la grille, choix de l'algorithme, puis test de l'algorithme.
1. Pour choisir la grille, l'utilisateur pourra charger une grille existante (en sélectionnant un fichier au format approprié à l'aide d'un [JFileChooser](http://www.iut-fbleau.fr/docs/java/api/javax/swing/JFileChooser.html)) ou construire une nouvelle grille.
 
S'il choisit cette dernière option, l'utilisateur décidera d'abord de la taille de la grille. Il pourra ensuite partir d'une grille vide, ou d'une grille remplie aléatoirement. Il sera alors capable de modifier individuellement l'état de chaque case, puis décidera de la position initiale de Thésée et de la sortie.
 
Une fois la grille terminée, l'utilisateur aura la possibilité de la sauvegarder. Vous pouvez ici aussi employer un [JFileChooser](http://www.iut-fbleau.fr/docs/java/api/javax/swing/JFileChooser.html) pour donner à l'utilisateur le choix de l'emplacement et du nom de la sauvegarde.
2. L'utilisateur pourra ensuite choisir entre deux algorithmes : l'un, totalement aléatoire (chaque direction a autant de chances d'être choisie), et l'autre déterministe.
 
Ce dernier devra baser ses décisions uniquement sur les coordonnées actuelles de Thésée et sur sa mémoire des conséquences de ses actions précédentes. Il n'aura aucune connaissance préalable de l'emplacement des cases obstruées ou de la sortie.
3. Une fois l'algorithme et la grille choisie, l'utilisateur devra sélectionner une visualisation du déroulement de la simulation : manuelle ou automatique.
 
Dans le mode manuel, l'avancement de Thésée sera représenté sur la grille, et le choix préconisé par l'algorithme pour la prochaine étape sera affiché. L'utilisateur fera défiler les étapes en appuyant sur une touche (capturée par un [KeyListener](http://www.iut-fbleau.fr/docs/java/api/java/awt/event/KeyListener.html)), jusqu'à la fin de la simulation. Ce mode sert à voir en détail le comportement de l'algorithme.
 
Dans le mode automatique, la grille n'est pas affichée, et la simulation est effectuée sans intervention de l'utilisateur. On affichera seulement le nombre d'étapes nécessaires pour compléter la simulation. Dans le cas de l'algorithme aléatoire, on fera tourner la simulation 100 fois avant d'afficher le nombre d'étapes moyen (puisque le résultat sera différent à chaque simulation).
 
Vous vous efforcerez d'écrire l'algorithme déterministe le plus efficace possible. Il devra impérativement être capable de trouver la sortie si un chemin existe (vous devrez en fournir la preuve dans le rapport), et ses performances (en termes de nombre d'étapes nécessaires pour trouver la sortie) devront être visiblement meilleures que celles de l'algorithme aléatoire.
Toutes les interactions devront impérativement être en mode graphique. Les seuls affichages permis à la console sont les messages envoyés à la sortie sur erreur afin de documenter les problèmes rencontrés.
#### Format de sauvegarde des grilles
Le fichier ne contiendra pas de texte. Le premier octet servira à stocker la taille de la grille. Le second et le troisième stockeront respectivement les numéros de ligne et de colonne de la position initiale de Thésée, le quatrième et le cinquième octet jouant le même rôle pour la position de la sortie.
 
A partir de ce point, chaque bit représente l'état d'une case : 0 si elle est libre, ou 1 si elle est bloquée. La grille est ainsi décrite colonne par colonne.
 
Exemple :
![exemple de grille](img/exemple.png)
Cette grille correspond au fichier suivant :
![exemple de fichier](img/fichier.png)
Vous pouvez télécharger [ce fichier](http://www.iut-fbleau.fr/sitebp/pt21/21_2022/petit.lab) pour tester votre programme.
## Rapport
### Makefile
* **`make run`** compile et exécute le programme
* **`make doc`** génère et ouvre la documentation
* **`make clean`** supprime les exécutables et la documentation
### Déroulement du programme
#### Menu d'accueil
Après avoir exécuté le programme, l'application ouvre une fenêtre contenant le premier menu : le menu d'accueil.
![menu d'accueil](img/accueil.png)
On a alors 3 possibilités :
* charger une grille existante ;
* construire une nouvelle grille ;
* quitter.
La première possibilité permet de charger un fichier `.lab` contenant la sauvegarde d'une grille, ce qui nous dirige directement vers le menu de modification. À noter qu'on vérifie scrupuleusement le contenu du fichier pour vérifier sa compatibilité. Il est impossible de charger un fichier qui n'est pas au bon format, même si son extension est .`lab`.
![fichier incompatible](img/erreur.png)
La deuxième redirige vers le menu d'initialisation qu'on verra juste après.
Enfin, la troisième possibilité quitte simplement le programme en fermant l'application.
#### Menu d'initialisation
En choisissant la deuxième option, l'application ouvre le menu d'initialisation d'une nouvelle grille. On peut alors choisir la taille de celle-ci via le slider, entre 3x3 et 20x20 (10x10 par défaut), et si l'on veut partir d'une grille totalement vide ou remplie aléatoirement à 50 %.
![menu d'initialisation](img/initialisation.png)
#### Menu de modification
Une fois la grille initialisée, on passe maintenant à sa modification. On peut modifier l'état de la grille case par case en cliquant sur cette dernière. Il y a sur le panneu gauche deux boutons que l'on peut activer et désactiver s'il on veut placer Thésée ou la sortie.
Une fois les modification apportée, on a la possibilité de la sauvegarder ou de passer directement à la simulation.
![menu de modification](img/modification.png)
#### Menu de simulation
Maintenant qu'on a notre grille, on a le choix entre les deux algorithmes : aléatoire ou déterministe, mais aussi entre les deux visualisations : manuelle ou automatique.
![menu de simulation](img/simulation.png)
Le premier algorithme est totalement aléatoire comme le prévoit la consigne, l'algorithme choisie une direction sur les quatres aléatoirement et essaye d'y déplacer Thésée. Si la case est libre, alors Thésée s'y rend, et ainsi de suite.
Lors de la visualisation automatique, on effectue ces essais de déplacement un certain nombre de fois avant d'en déduire que Thésée est bloqué.
![thésée bloqué](img/finBloquee.png)
L'algorithme déterministe tente, dans l'ordre, les directions Nord, Sud, Est puis Ouest. À la différence de l'algorithme aléatoire, il vérifie non seulement que la case n'est pas bloqué, mais aussi qu'il ne s'y ait pas déjà rendu auparavent via une pile. Tant qu'il y a des cases dans la pile, on continue :
1. On regarde la case en haut de la pile (la case actuelle).
2. Si la case actuelle est la sortie du labyrinthe, on a trouvé le chemin et on arrête l'algorithme.
3. Sinon, on regarde les cases voisines non visitées de la case actuelle.
4. Si on trouve une case voisine qui est valide (à l'intérieur du labyrinthe), non visitée et libre (pas de blocage), on l'ajoute à la pile et on la marque comme visitée.
5. Si on ne trouve aucune case voisine non visitée, cela signifie que la case actuelle est un "cul-de-sac" et on la retire de la pile.
L'algorithme utilisé , qui est le parcours en profondeur (DFS), est garanti de trouver la sortie si un chemin vers la sortie existe. Voici une démonstration pour prouver cette propriété :
1. Supposons qu'il existe un chemin de la case de départ à la sortie du labyrinthe.
2. L'algorithme commence par la case de départ et explore systématiquement les voisins non visités jusqu'à ce qu'il atteigne la sortie ou qu'il n'y ait plus de voisins non visités à explorer.
3. Comme l'algorithme utilise une approche déterministe, il explore chaque branche du labyrinthe jusqu'à son terme avant d'explorer une autre branche. Cela signifie qu'il visite chaque case possible dans le labyrinthe.
4. Si la sortie est atteignable, l'algorithme finira par atteindre la case de sortie, car il examine tous les voisins non visités avant de revenir en arrière.
5. Lorsque l'algorithme atteint la case de sortie, il s'arrête et indique que le chemin a été trouvé.
6. Par conséquent, si un chemin existe dans le labyrinthe, l'algorithme de parcours en profondeur utilisé ici sera en mesure de le trouver.
![fin déterministe](img/finDeterministe.png)
La visualisation manuelle permet une visualisation de chaque étape de l'algorithme, comme ici avec l'algorithme aléatoire :
![étape 1](img/deplacementBloquee.png)
![étape 2](img/deplacement.png)

BIN
img/accueil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

BIN
img/deplacement.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
img/deplacementBloquee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

BIN
img/erreur.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
img/exemple.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
img/fichier.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
img/finBloquee.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
img/finDeterministe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
img/grille.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
img/initialisation.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

BIN
img/modification.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

BIN
img/simulation.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

420
sources/Algorithmes.java Normal file
View File

@ -0,0 +1,420 @@
import java.awt.Point;
import java.util.Random;
import java.util.Stack;
import javax.swing.JOptionPane;
/**
* La classe <code>Algorithmes</code> est utilisée implémenter les deux algorithmes : Déterministe et Aléatoire.
* Chacun de ses deux algorithmes à deux mode de déroulement : Automatique l'on affiche seulement le résultat et Manuel on affiche chaque étape.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class Algorithmes {
/**
* Position initiale X de Thésée
*/
private int theseeDepartX;
/**
* Position initiale Y de Thésée
*/
private int theseeDepartY;
/**
* Position X de Thésée
*/
private int theseeX;
/**
* Position Y de Thésée
*/
private int theseeY;
/**
* Position X de la sortie
*/
private int sortieX;
/**
* Position Y de la sortie
*/
private int sortieY;
/**
* la grille sous forme d'un tableau boolean en deux dimensions
*/
private boolean[][] grille;
/**
* la taille de la grille
*/
private int tailleGrille;
/**
* Les directions possibles pour Thésée
*/
private final String[] DIRECTIONS = {"Nord", "Sud", "Est", "Ouest"};
/**
* L'index des directions dans le tableau.
*/
private int direction; // 0 => Nord ; 1 => Sud ; 2 => Est ; 3 => Ouest
/**
* Nombre de coups effectués pour atteindre la sortie.
*/
private int coupsTotal;
/**
* Le panel de la grille pour déplacer Thésée.
*/
private GrillePanel grillePanel;
/**
* Le menu qui affiche la simulation.
*/
private MenuSimulation menuSimulation;
/**
* Crée un tableau pour stocker les cases visitées
*/
private boolean[][] memoire;
/**
* Initialise la pile pour stocker les cases du chemin emprunté
*/
private Stack<Point> chemin;
/*
* Le nombre de voisins connus utilisé comme index au tableau dimensions.
*/
private int voisin;
/**
* Constrcteur de la classe <code>Algorithmes</code> Initialise les variables dont auront besoin les algorithmes à chaque lancement de ces derniers.
*
* @param grilleConfig la configuration de la grille
* @param grillePanel le panneau qui affiche la grille
* @param menuSimulation le menu qui affiche la simulation
*/
public Algorithmes(GrilleConfig grilleConfig, GrillePanel grillePanel, MenuSimulation menuSimulation) {
this.grillePanel = grillePanel;
this.menuSimulation = menuSimulation;
tailleGrille = grilleConfig.getTailleGrille();
grille = new boolean[tailleGrille][tailleGrille];
memoire = new boolean[tailleGrille][tailleGrille];
chemin = new Stack<>();
// Récupère les positions de Thésée et de la sortie
theseeDepartX = grilleConfig.getColThe();
theseeDepartY = grilleConfig.getLigneThe();
sortieX = grilleConfig.getColSortie();
sortieY = grilleConfig.getLigneSortie();
// Ajoute la position initiale de Thésée dans la pile
chemin.push(new Point(theseeDepartX, theseeDepartY));
memoire[theseeDepartY][theseeDepartX] = true;
// Converti le tableau 1D de type byte en un tableau 2D de type boolean
for (int ligne = 0; ligne < tailleGrille; ligne++) {
for (int colonne = 0; colonne < tailleGrille; colonne++) {
if (grilleConfig.getGrille()[ligne * tailleGrille + colonne] == 1) {
grille[ligne][colonne] = true;
} else {
grille[ligne][colonne] = false;
}
if (grillePanel.thesee[ligne][colonne]) { // Supprime Thésée (s'il a été déplacé)
grillePanel.thesee[ligne][colonne] = false;
}
}
}
// Replace Thésée à sa poisition de départ
grillePanel.thesee[theseeDepartY][theseeDepartX] = true;
grillePanel.repaint();
// Place Thésée sur sa position de initiale.
theseeX = theseeDepartX;
theseeY = theseeDepartY;
// Initialise le nombre de coups effectués et le nombre de voisin connus à zéro.
coupsTotal = 0;
voisin = 0;
}
/**
* Méthode pour résoudre le labyrinthe avec un algorithme totalement aléatoire de manière automatique.
*/
public void aleatoireAuto() {
Random rand = new Random();
int coupsMoyen;
int maxCoups = 1000000; // nombre maximal de coups avant de considérer que Thésée a échoué
int echecs = 0;
for (int i = 0; i < 100; i++) { // On exécute l'algorithme 100 fois pour obtenir une moyenne de coups
theseeX = theseeDepartX;
theseeY = theseeDepartY;
for (; theseeX != sortieX || theseeY != sortieY; coupsTotal++) { // Tant que Thésée n'a pas trouvé la sortie
direction = rand.nextInt(DIRECTIONS.length); // On choisi une direction au hasard
if (direction == 0) {
if (theseeY - 1 >= 0 && !grille[theseeX][theseeY - 1]) {
theseeY--;
}
} else if (direction == 1) {
if (theseeY + 1 < tailleGrille && !grille[theseeX][theseeY + 1]) {
theseeY++;
}
} else if (direction == 2) {
if (theseeX + 1 < tailleGrille && !grille[theseeX + 1][theseeY]) {
theseeX++;
}
} else if (direction == 3) {
if (theseeX - 1 >= 0 && !grille[theseeX - 1][theseeY]) {
theseeX--;
}
}
if (coupsTotal == maxCoups) { // Si le nombre de coups maximal a été atteint
echecs++; // On compte le nombre d'échecs
coupsTotal -= maxCoups; // On ne compte pas les coups de ce tour pour ne pas fausser la moyenne
break; // on sort de la boucle
}
}
}
// Cache Thésée
grillePanel.thesee[theseeDepartY][theseeDepartX] = false;
grillePanel.repaint();
// S'il y a 100 % d'echecs, on considère que la sortie est impossible a atteindre
if (echecs == 100) {
JOptionPane.showMessageDialog(null, "Thésée est mort de faim avant d'avoir trouvé la sortie.", "Fin de la simulation", JOptionPane.PLAIN_MESSAGE);
} else {
coupsMoyen = (int) coupsTotal / (100 - echecs); // Moyenne réelle sans compter les éventuelles échecs
JOptionPane.showMessageDialog(null, "Thésée a trouvé la sortie ! \nNombre d'étapes moyen : " + coupsMoyen + ".", "Fin de la simulation", JOptionPane.PLAIN_MESSAGE);
}
// Réaffiche Thésée
grillePanel.thesee[theseeDepartY][theseeDepartX] = true;
grillePanel.repaint();
}
/**
* Méthode pour résoudre le labyrinthe avec un algorithme totalement aléatoire de manière manuelle.
*
* @return <code>true</code> lorsque la simulation est terminée
*/
public boolean aleatoireMan() {
Random rand = new Random();
direction = rand.nextInt(DIRECTIONS.length); // On choisi une direction au hasard
if (direction == 0) {
menuSimulation.modifierInfo("↑ Le vent pousse Thésée vers le Nord ↑");
if (theseeY - 1 >= 0 && !grille[theseeX][theseeY - 1]) {
grillePanel.thesee[theseeY][theseeX] = false;
theseeY--;
grillePanel.thesee[theseeY][theseeX] = true;
grillePanel.repaint();
menuSimulation.modifierInfo("↑ Thésée va vers le Nord ↑");
}
} else if (direction == 1) {
menuSimulation.modifierInfo("↓ Le vent pousse Thésée va vers Sud ↓");
if (theseeY + 1 < tailleGrille && !grille[theseeX][theseeY + 1]) {
grillePanel.thesee[theseeY][theseeX] = false;
theseeY++;
grillePanel.thesee[theseeY][theseeX] = true;
grillePanel.repaint();
menuSimulation.modifierInfo("↓ Thésée va vers le Sud ↓");
}
} else if (direction == 2) {
menuSimulation.modifierInfo("→ Le vent pousse Thésée vers l'Est →");
if (theseeX + 1 < tailleGrille && !grille[theseeX + 1][theseeY]) {
grillePanel.thesee[theseeY][theseeX] = false;
theseeX++;
grillePanel.thesee[theseeY][theseeX] = true;
grillePanel.repaint();
menuSimulation.modifierInfo("→ Thésée va vers l'Est →");
}
} else if (direction == 3) {
menuSimulation.modifierInfo("← Le vent pousse Thésée vers l'Ouest ←");
if (theseeX - 1 >= 0 && !grille[theseeX - 1][theseeY]) {
grillePanel.thesee[theseeY][theseeX] = false;
theseeX--;
grillePanel.thesee[theseeY][theseeX] = true;
grillePanel.repaint();
menuSimulation.modifierInfo("← Thésée va vers l'Ouest ←");
}
}
coupsTotal++;
if (theseeX == sortieX && theseeY == sortieY) {
// Cache Thésée
grillePanel.thesee[theseeY][theseeX] = false;
grillePanel.repaint();
JOptionPane.showMessageDialog(null, "Thésée a trouvé la sortie ! \nNombre d'étapes : " + coupsTotal, "Fin de la simulation", JOptionPane.PLAIN_MESSAGE);
// Réaffiche Thésée
grillePanel.thesee[theseeDepartY][theseeDepartX] = true;
grillePanel.repaint();
return true;
}
return false;
}
/**
* Méthode pour résoudre le labyrinthe avec un algorithme déterministe automatiquement.
* On utilise ici l'algorithme de parcours en profondeur (DFS : depth-first search) de la théorie des graphes
*/
public void deterministeAuto() {
// Tant que la pile n'est pas vide
while (!chemin.isEmpty()) {
// Obtient la case en haut de la pile
Point positionActuelle = chemin.peek();
// Si la case actuelle est la sortie, le chemin a été trouvé
if (positionActuelle.x == sortieX && positionActuelle.y == sortieY) {
break;
}
// Vérifie s'il y a un voisin non visité
boolean voisinInconnu = false;
for (int[] direction : new int[][] {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}) {
int nouveauX = positionActuelle.x + direction[0];
int nouveauY = positionActuelle.y + direction[1];
coupsTotal++;
// Si le voisin est valide, non visité et libre
if (nouveauX >= 0 && nouveauX < tailleGrille && nouveauY >= 0 && nouveauY < tailleGrille && !memoire[nouveauY][nouveauX] && !grille[nouveauX][nouveauY]) {
// Ajoute le voisin à la pile et le marque comme visité
chemin.push(new Point(nouveauX, nouveauY));
memoire[nouveauY][nouveauX] = true;
voisinInconnu = true;
break;
}
}
// Si la case actuelle n'a pas de voisin non visité, on la retire de la pile
if (!voisinInconnu) {
chemin.pop();
}
}
// Si la pile est vide, aucun chemin n'a été trouvé
if (chemin.isEmpty()) {
JOptionPane.showMessageDialog(null, "Thésée n'a pas pu trouver la sortie.", "Fin de la simulation", JOptionPane.PLAIN_MESSAGE);
} else {
// Cache Thésée
grillePanel.thesee[theseeDepartY][theseeDepartX] = false;
grillePanel.repaint();
JOptionPane.showMessageDialog(null, "Thésée a trouvé la sortie !\nNombre d'étapes : " + coupsTotal + ".", "Fin de la simulation", JOptionPane.PLAIN_MESSAGE);
}
// Réaffiche Thésée
grillePanel.thesee[theseeDepartY][theseeDepartX] = true;
grillePanel.repaint();
}
/**
* Méthode pour résoudre le labyrinthe avec un algorithme déterministe manuellement.
* On utilise ici l'algorithme de parcours en profondeur (DFS : depth-first search) de la théorie des graphes
*
* @return <code>true</code> lorsque la simulation est terminée
*/
public boolean deterministeMan() {
// Si la pile est vide, aucun chemin n'a été trouvé
if (chemin.isEmpty()) {
JOptionPane.showMessageDialog(null, "Thésée n'a pas pu trouver la sortie.", "Fin de la simulation", JOptionPane.PLAIN_MESSAGE);
// Cache Thésée
grillePanel.thesee[theseeY][theseeX] = false;
grillePanel.repaint();
// Réaffiche Thésée
grillePanel.thesee[theseeDepartY][theseeDepartX] = true;
grillePanel.repaint();
return true;
}
// Obtient la case en haut de la pile
Point positionActuelle = chemin.peek();
// Vérifie s'il y a un voisin non visité
if (voisin < 4) {
int[] direction = new int[][] {{0, -1}, {0, 1}, {-1, 0}, {1, 0}}[voisin];
int nouveauX = positionActuelle.x + direction[0];
int nouveauY = positionActuelle.y + direction[1];
coupsTotal++;
// Indique la direction préconisée
String directionInfo = "";
if (direction[0] == 0 && direction[1] == -1) {
directionInfo = "↑ Thésée vérifie le Nord ↑";
} else if (direction[0] == 0 && direction[1] == 1) {
directionInfo = "↓ Thésée vérifie le Sud ↓";
} else if (direction[0] == -1 && direction[1] == 0) {
directionInfo = "← Thésée vérifie l'Ouest ←";
} else if (direction[0] == 1 && direction[1] == 0) {
directionInfo = "→ Thésée vérifie l'Est →";
}
menuSimulation.modifierInfo(directionInfo);
// Si le voisin est valide, non visité et libre
if (nouveauX >= 0 && nouveauX < tailleGrille && nouveauY >= 0 && nouveauY < tailleGrille && !memoire[nouveauY][nouveauX] && !grille[nouveauX][nouveauY]) {
// Ajoute la nouvelle position de Thésée à la pile et la marque comme visitée
chemin.push(new Point(nouveauX, nouveauY));
memoire[nouveauY][nouveauX] = true;
// Déplace Thésée vers la direction optimale
grillePanel.thesee[positionActuelle.y][positionActuelle.x] = false;
grillePanel.thesee[nouveauY][nouveauX] = true;
grillePanel.repaint();
voisin = 0;
// Indique la direction empruntée
if (nouveauY < positionActuelle.y) {
directionInfo = "↑ Thésée va vers le Nord ↑";
} else if (nouveauY > positionActuelle.y) {
directionInfo = "↓ Thésée va vers le Sud ↓";
} else if (nouveauX < positionActuelle.x) {
directionInfo = "← Thésée va vers l'Ouest ←";
} else if (nouveauX > positionActuelle.x) {
directionInfo = "→ Thésée va vers l'Est →";
}
menuSimulation.modifierInfo(directionInfo);
// Si la case actuelle est la sortie, le chemin a été trouvé
if (nouveauX == sortieX && nouveauY == sortieY) {
// Cache Thésée
grillePanel.thesee[nouveauY][nouveauX] = false;
grillePanel.repaint();
JOptionPane.showMessageDialog(null, "Thésée a trouvé la sortie !\nNombre d'étapes : " + coupsTotal + ".", "Fin de la simulation", JOptionPane.PLAIN_MESSAGE);
// Réaffiche Thésée
grillePanel.thesee[theseeDepartY][theseeDepartX] = true;
grillePanel.repaint();
return true;
}
} else {
// Incrémente l'indice de vérification des voisins pour le prochain appel de la méthode
voisin++;
}
} else {
// Stock temporairement les coordonnées de la position actuelle
int ancienX = positionActuelle.x;
int ancienY = positionActuelle.y;
// Si tous les voisins ont été vérifiés, retire la position actuelle de la pile et réinitialise l'indice de vérification des voisins
chemin.pop();
voisin = 0;
// Met à jour l'affichage de Thésée pour supprimer l'icône de Thésée de la position actuelle
grillePanel.thesee[ancienY][ancienX] = false;
// Met à jour l'affichage de Thésée pour afficher l'icône de Thésée à la nouvelle position
Point nouvellePosition = chemin.peek(); // récupère la nouvelle position en haut de la pile
grillePanel.thesee[nouvellePosition.y][nouvellePosition.x] = true;
// Met à jour l'affichage
grillePanel.repaint();
// Indique la direction empruntée
String directionInfo = "";
if (nouvellePosition.y < ancienY) {
directionInfo = "↑ Thésée retourne vers le Nord ↑";
} else if (nouvellePosition.y > ancienY) {
directionInfo = "↓ Thésée retourne vers le Sud ↓";
} else if (nouvellePosition.x < ancienX) {
directionInfo = "← Thésée retourne vers l'Ouest ←";
} else if (nouvellePosition.x > ancienX) {
directionInfo = "→ Thésée retourne vers l'Est →";
}
menuSimulation.modifierInfo(directionInfo);
}
return false;
}
}

View File

@ -0,0 +1,186 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;
/**
* La classe <code>ControleurAccueil</code> est utilisée pour gérer les actions associées aux boutons du menu d'accueil.
* Elle implémente l'interface <code>ActionListener</code> qui permet de détecter les événements de clic sur les boutons.
* Lorsqu'un événement est détecté, la méthode <code>actionPerformed</code> est appelée et traite l'action correspondante en fonction du texte du bouton cliqué.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class ControleurAccueil implements ActionListener {
/**
* Constante définissant le filtre de fichier pour n'afficher que les fichiers .lab.
*/
private static final FileNameExtensionFilter FILTRE = new FileNameExtensionFilter("Fichiers Lab (*.lab)", "lab");
/**
* Composante du bouton source de l'action.
*/
private JButton source;
/**
* Composante permettant de stocker les informations de la grille.
*/
private GrilleConfig grilleConfig;
/**
* Constructeur utilisé pour gérer la configuration de la grille
*/
public ControleurAccueil() {
grilleConfig = new GrilleConfig();
}
/**
* Méthode appelée lorsque l'utilisateur clique sur un bouton.
* Elle récupère le texte du bouton cliqué et exécute l'action correspondante.
*
* @param evenement l'événement de clic sur le bouton.
*/
@Override
public void actionPerformed(ActionEvent evenement) {
source = (JButton) evenement.getSource();
String commande = evenement.getActionCommand();
switch (commande) {
case "Charger une grille existante":
selectionnerFichier();
break;
case "Construire une nouvelle grille":
afficherMenuGrille();
break;
case "Quitter":
System.exit(0); // Ferme le programme
break;
default:
break;
}
}
/**
* Méthode pour sélectionner un fichier au format approprié.
*/
private void selectionnerFichier() {
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(FILTRE);
int returnVal = chooser.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File fichier = chooser.getSelectedFile();
try {
if (verifierFichier(fichier)) {
afficherConstructeur(fichier);
}
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Erreur lors de la vérification du fichier : " + ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
}
/**
* Méthode pour vérifier le format du fichier.
*
* @param fichier le fichier à vérifier
* @throws IOException
* @return <code>true</code> si le fichier est valide, <code>false</code> sinon
*/
private boolean verifierFichier(File fichier) throws IOException {
try (InputStream in = new FileInputStream(fichier)) {
// Vérifier le type du fichier
String nomFichier = fichier.getName();
if (!nomFichier.endsWith(".lab")) {
JOptionPane.showMessageDialog(null, "Le fichier sélectionné n'est pas au format .lab.", "Erreur", JOptionPane.ERROR_MESSAGE);
return false;
}
// Vérifier la taille du fichier
if (fichier.length() < 6) {
JOptionPane.showMessageDialog(null, "Le fichier ne contient pas assez de données.", "Erreur", JOptionPane.ERROR_MESSAGE);
return false;
}
// Lire la taille de la grille
int tailleGrille = in.read();
if (tailleGrille < 0 || tailleGrille > 255) {
JOptionPane.showMessageDialog(null, "La taille de la grille n'est pas valide.", "Erreur", JOptionPane.ERROR_MESSAGE);
return false;
}
// Lire les positions initiales de Thésée et de la sortie
int ligneThe = in.read();
int colThe = in.read();
int ligneSortie = in.read();
int colSortie = in.read();
if (ligneThe < 0 || ligneThe > tailleGrille - 1 || colThe < 0 || colThe > tailleGrille - 1 ||
ligneSortie < 0 || ligneSortie > tailleGrille - 1 || colSortie < 0 || colSortie > tailleGrille - 1) {
JOptionPane.showMessageDialog(null, "Les positions de Thésée et/ou de la sortie ne sont pas valides.", "Erreur", JOptionPane.ERROR_MESSAGE);
return false;
}
// Lire la grille
byte[] grille = new byte[tailleGrille * tailleGrille];
int offset = 0;
int b;
while ((b = in.read()) != -1) {
for (int i = 7; i >= 0 && offset < grille.length; i--) {
grille[offset++] = (byte) ((b >> i) & 1);
}
}
// Vérifier que toutes les cases de la grille sont soit 0 soit 1
for (byte caseGrille : grille) {
if (caseGrille != 0 && caseGrille != 1) {
JOptionPane.showMessageDialog(null, "La grille contient une case invalide.", "Erreur", JOptionPane.ERROR_MESSAGE);
return false;
}
}
// Stocker la configuration de la grille
grilleConfig.setTailleGrille(tailleGrille);
grilleConfig.setLigneThe(ligneThe);
grilleConfig.setColThe(colThe);
grilleConfig.setLigneSortie(ligneSortie);
grilleConfig.setColSortie(colSortie);
grilleConfig.setGrille(grille);
return true;
} catch (IOException | SecurityException | NullPointerException ex) {
JOptionPane.showMessageDialog(null, "Erreur lors de la vérification du fichier : " + ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
return false;
}
}
/**
* Méthode pour afficher le menu de paramétrage.
*/
private void afficherConstructeur(File fichier) {
JFrame fenetre = (JFrame) SwingUtilities.getWindowAncestor(source); // Obtient la fenêtre courante
fenetre.setTitle("Labyrinthe - Modification de la grille");
fenetre.getContentPane().removeAll(); // Supprime le menu d'accueil
fenetre.getContentPane().add(new MenuModification(grilleConfig)); // Ajoute le menu pour modifier la grille
fenetre.revalidate(); // Rafraîchit l'affichage
}
/**
* Méthode pour afficher le menu qui permet la construction d'une nouvelle grille.
*/
private void afficherMenuGrille() {
JFrame fenetre = (JFrame) SwingUtilities.getWindowAncestor(source); // Obtient la fenêtre courante
fenetre.setTitle("Labyrinthe - Initialisation de la grille");
fenetre.getContentPane().removeAll(); // Supprime le menu d'accueil
fenetre.getContentPane().add(new MenuInitialisation()); // Ajoute le menu pour générer une nouvelle grille
fenetre.revalidate(); // Rafraîchit l'affichage
}
}

View File

@ -0,0 +1,104 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
/**
* La classe <code>ControleurGrille</code> est utilisée pour gérer les actions associées aux boutons de la classe <code>MenuInitialisation</code>.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class ControleurInitialisation implements ActionListener {
/**
* Composante du bouton source de l'action.
*/
private JButton source;
/**
* Composante permettant de récupérer les informations de la grille.
*/
private MenuInitialisation menuInitialisation;
/**
* Composante permettant de stocker les informations de la grille.
*/
private GrilleConfig grilleConfig;
/**
* Constructeur de la classe <code>ControleurGrille</code>.
*
* @param menuInitialisation le menu qui affiche les options d'initialisation de la grille
* @param grilleConfig la configuration de la grille (taille, etc.)
*/
public ControleurInitialisation(MenuInitialisation menuInitialisation, GrilleConfig grilleConfig) {
this.menuInitialisation = menuInitialisation;
this.grilleConfig = grilleConfig;
}
/**
* Méthode appelée lorsque l'utilisateur clique sur un bouton.
* Elle récupère le texte du bouton cliqué et exécute l'action correspondante.
*
* @param evenement l'événement de clic sur le bouton.
*/
@Override
public void actionPerformed(ActionEvent evenement) {
if (evenement.getSource() instanceof JButton) {
source = (JButton) evenement.getSource();
}
String commande = evenement.getActionCommand();
switch (commande) {
case "Retour":
afficherMenuPrecedent();
break;
case "Valider":
enregistrerConfiguration();
afficherMenuSuivant();
break;
default:
// Vérifie si une configuration de départ est sélectionnée
if (menuInitialisation.isGrilleVideSelected() || menuInitialisation.isGrilleAleatoireSelected()) {
// Réactive le bouton Valider
menuInitialisation.getBoutonValider().setEnabled(true);
}
break;
}
}
/**
* Méthode pour réafficher le menu précédent.
*/
private void afficherMenuPrecedent() {
JFrame fenetre = (JFrame) SwingUtilities.getWindowAncestor(source); // Obtient la fenêtre courante
fenetre.setTitle("Labyrinthe - Menu principal");
fenetre.getContentPane().removeAll(); // Supprime le menu actuel
fenetre.getContentPane().add(new MenuAccueil()); // Ajoute le menu d'accueil
fenetre.revalidate(); // Rafraîchit l'affichage
}
/**
* Méthode pour stocker la configuration de départ de la grille.
*/
private void enregistrerConfiguration() {
int tailleGrille = menuInitialisation.getTailleGrille();
boolean grilleVide = menuInitialisation.isGrilleVideSelected();
boolean grilleAleatoire = menuInitialisation.isGrilleAleatoireSelected();
grilleConfig.setTailleGrille(tailleGrille);
grilleConfig.setGrilleVide(grilleVide);
grilleConfig.setGrilleAleatoire(grilleAleatoire);
}
/**
* Méthode pour afficher le menu suivant.
*/
private void afficherMenuSuivant() {
JFrame fenetre = (JFrame) SwingUtilities.getWindowAncestor(source); // Obtient la fenêtre courante
fenetre.setTitle("Labyrinthe - Modification de la grille");
fenetre.getContentPane().removeAll(); // Supprime le menu actuel
fenetre.getContentPane().add(new MenuModification(grilleConfig)); // Ajoute le menu permettant de modifier la grille
fenetre.revalidate(); // Rafraîchit l'affichage
}
}

View File

@ -0,0 +1,219 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileNameExtensionFilter;
/**
* La classe <code>ControleurConstructeur</code> est utilisée pour gérer les actions associées à la classe <code>MenuModification</code>.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class ControleurModification implements ActionListener {
/**
* Composante du bouton "Placer Thésée" pour permettre de connaitre son état.
*/
private JToggleButton boutonPlacerThesee;
/**
* Composante du bouton "Placer sortie" pour permettre de connaitre son état.
*/
private JToggleButton boutonPlacerSortie;
/**
* Composante du bouton source de l'action.
*/
private JButton source;
/**
* Composante déclarant une instance de GrilleConfig pour permettre de vérifier si une grille a été chargée depuis un fichier.
*/
private GrilleConfig grilleConfig;
/**
* Composante déclarant la grille affichée.
*/
private GrillePanel grillePanel;
/**
* Constructeur de la classe <code>ControleurConstructeur</code>.
*
* @param placerThesee le bouton pour placer Thésée sur la grille
* @param placerSortie le bouton pour placer la sortie sur la grille
* @param grilleConfig la configuration de la grille (taille, etc.)
* @param grillePanel le panneau qui affiche la grille
*/
public ControleurModification(JToggleButton placerThesee, JToggleButton placerSortie, GrilleConfig grilleConfig, GrillePanel grillePanel) {
boutonPlacerThesee = placerThesee;
boutonPlacerSortie = placerSortie;
this.grilleConfig = grilleConfig;
this.grillePanel = grillePanel;
}
/**
* Méthode appelée lorsque l'utilisateur clique sur un bouton.
* Elle récupère le texte du bouton cliqué et exécute l'action correspondante.
*
* @param evenement l'événement de clic sur le bouton.
*/
@Override
public void actionPerformed(ActionEvent evenement) {
if (evenement.getSource() instanceof JButton) {
source = (JButton) evenement.getSource();
}
switch (evenement.getActionCommand()) {
case "Placer Thésée":
if (boutonPlacerThesee.isSelected()) {
if (boutonPlacerSortie.isSelected()) { // Empêche d'avoir les deux boutons sélectionnés en même temps
boutonPlacerSortie.setSelected(false);
}
}
break;
case "Placer sortie":
if (boutonPlacerSortie.isSelected()) {
if (boutonPlacerThesee.isSelected()) { // Empêche d'avoir les deux boutons sélectionnés en même temps
boutonPlacerThesee.setSelected(false);
}
}
break;
case "Retour":
source = (JButton) evenement.getSource();
afficherMenuPrecedent();
break;
case "Sauvegarder":
sauvegarder();
break;
case "Continuer":
afficherMenuSuivant();
break;
default:
break;
}
}
/**
* Méthode pour réafficher le menu précédent.
*/
private void afficherMenuPrecedent() {
JFrame fenetre = (JFrame) SwingUtilities.getWindowAncestor(source); // Obtient la fenêtre courante
fenetre.getContentPane().removeAll(); // Supprime le menu actuel
if (grilleConfig.getGrille() != null) { // Si la grille a été chargée depuis un fichier
fenetre.getContentPane().add(new MenuAccueil()); // Ajoute le menu d'accueil
fenetre.setTitle("Labyrinthe - Menu principal");
} else { // Si la grille vient d'être créée
fenetre.getContentPane().add(new MenuInitialisation()); // Ajoute le menu permettant d'initialiser la grille
fenetre.setTitle("Labyrinthe - Initialisation de la grille");
}
fenetre.revalidate(); // Rafraîchit l'affichage
}
/**
* Méthode pour sélectionner l'emplacement et d'y enregistrer la grille.
*/
private void sauvegarder() {
JFileChooser chooser = new JFileChooser();
chooser.setDialogType(JFileChooser.SAVE_DIALOG);
// Ajout du filtre de fichier
chooser.setFileFilter(new FileNameExtensionFilter("Fichiers Lab (*.lab)", "lab"));
File fichier = null;
while (fichier == null || fichier.exists()) {
int returnVal = chooser.showSaveDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
fichier = chooser.getSelectedFile();
// Vérification si le fichier existe déjà
if (fichier.exists()) {
int confirmation = JOptionPane.showConfirmDialog(null, "Le fichier existe déjà. Voulez-vous le remplacer ?", "Confirmation", JOptionPane.YES_NO_OPTION);
if(confirmation != JOptionPane.YES_OPTION) {
fichier = null; // Réaffichage de la boîte de dialogue
} else {
// Écrasement du fichier
fichier.delete();
fichier = chooser.getSelectedFile();
}
}
} else {
return; // L'utilisateur a annulé la sélection
}
}
// Forcer l'extension du fichier à être ".lab"
String fileName = fichier.getName();
if (!fileName.endsWith(".lab")) {
fichier = new File(fichier.getParentFile(), fileName + ".lab");
}
// Écriture du fichier
try {
ecrireFichier(fichier);
} catch (SecurityException | IllegalArgumentException ex) {
JOptionPane.showMessageDialog(null, "Erreur lors de l'écriture du fichier : " + ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Méthode pour écrire la grille dans un fichier .lab au format approprié.
*
* @param fichier le fichier à écrire
*/
private void ecrireFichier(File fichier) {
int tailleGrille = grilleConfig.getTailleGrille();
byte[] grilleBytes = new byte[tailleGrille * tailleGrille / 8 + 1]; // La capacité du tableau est le nombre d'octet nécessaires
int index = 0;
byte octet = 0; // Variable utilisée pour stocker les bits correspondant à chaque case sous forme d'octets
int bitIndex = 0; // Variable utilisée pour suivre la position actuelle dans l'octet. Permet de savoir quand on a rempli un octet
for (int colonne = 0; colonne < tailleGrille; colonne++) {
for (int ligne = 0; ligne < tailleGrille; ligne++) {
if (grillePanel.grille[ligne][colonne]) {
octet |= (1 << (7 - bitIndex)); // Définition du bit à 1 dans l'octet pour la case courante
}
bitIndex++;
if (bitIndex == 8) { // Passe à l'octet (index) suivant quand nécéssaire
grilleBytes[index] = octet;
index++;
octet = 0;
bitIndex = 0;
}
}
}
if (bitIndex != 0) { // Écriture de l'octet partiel s'il en existe un
grilleBytes[index] = octet;
}
// Écritue des données dans le fichier
try (OutputStream out = new FileOutputStream(fichier)) {
out.write(tailleGrille); // Écriture de la taille de la grille (Octet 1)
out.write(grilleConfig.getLigneThe()); // Écriture de la ligne de Thésée (Octet 2)
out.write(grilleConfig.getColThe()); // Écriture de la colonne de Thésée (Octet 3)
out.write(grilleConfig.getLigneSortie()); // Écriture de la ligne de la sortie (Octet 4)
out.write(grilleConfig.getColSortie()); // Écriture de la colonne de la sortie (Octet 5)
out.write(grilleBytes); // Écriture des cases (Octets 6 à n)
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Erreur lors de l'écriture du fichier : " + ex.getMessage(), "Erreur", JOptionPane.ERROR_MESSAGE);
}
}
/**
* Méthode pour afficher le menu suivant.
*/
private void afficherMenuSuivant() {
JFrame fenetre = (JFrame) SwingUtilities.getWindowAncestor(source); // Obtient la fenêtre courante
fenetre.setTitle("Labyrinthe - Simulation");
fenetre.getContentPane().removeAll(); // Supprime le menu actuel
fenetre.getContentPane().add(new MenuSimulation(grilleConfig)); // Ajoute le menu de simulation
fenetre.revalidate(); // Rafraîchit l'affichage
}
}

View File

@ -0,0 +1,180 @@
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
/**
* La classe <code>ControleurSimulation</code> est utilisée pour gérer les actions associées aux boutons de la classe <code>MenuSimulation</code>.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class ControleurSimulation implements ActionListener {
/**
* Composante du bouton source de l'action.
*/
private JButton source;
/**
* Composante permettant de récupérer les informations du menu.
*/
private MenuSimulation menuSimulation;
/**
* Composante permettant de stocker les informations de la grille.
*/
private GrilleConfig grilleConfig;
/**
* Le panel de la grille pour déplacer Thésée.
*/
private GrillePanel grillePanel;
/**
* Composante permettant d'appeler les différents algorithmes.
*/
private Algorithmes algorithme;
/**
* L'écouteur de clavier actuel.
*/
private KeyAdapter keyListenerActuel;
/**
* Constructeur de la classe <code>ControleurSimulation</code>.
*
* @param menuSimulation le menu qui affiche la simulation
* @param grilleConfig la configuration de la grille (taille, etc.)
* @param grillePanel le panneau qui affiche la grille
*/
public ControleurSimulation(MenuSimulation menuSimulation, GrilleConfig grilleConfig, GrillePanel grillePanel) {
this.menuSimulation = menuSimulation;
this.grilleConfig = grilleConfig;
this.grillePanel = grillePanel;
}
/**
* Méthode appelée lorsque l'utilisateur clique sur un bouton.
* Elle récupère le texte du bouton cliqué et exécute l'action correspondante.
*
* @param evenement l'événement de clic sur le bouton.
*/
@Override
public void actionPerformed(ActionEvent evenement) {
if (evenement.getSource() instanceof JButton) {
source = (JButton) evenement.getSource();
}
switch (evenement.getActionCommand()) {
case "Commencer":
commencerSimulation();
break;
case "Arrêter":
finSimulation();
break;
case "Retour":
afficherMenuPrecedent();
break;
case "Quitter":
System.exit(0); // Ferme le programme
break;
default:
// Vérifie si la simulation est configurée
if (menuSimulation.isAlgoSelected() && menuSimulation.isVisuSelected()) {
// Active le bouton Commencer
menuSimulation.getBoutonOnOff().setEnabled(true);
menuSimulation.modifierInfo("Vous pouvez commencer !");
}
break;
}
}
/**
* Méthode pour réafficher le menu précédent.
*/
private void afficherMenuPrecedent() {
JFrame fenetre = (JFrame) SwingUtilities.getWindowAncestor(source); // Obtient la fenêtre courante
fenetre.getContentPane().removeAll(); // Supprime le menu actuel
fenetre.getContentPane().add(new MenuModification(grilleConfig)); // Ajoute le menu de modification
fenetre.setTitle("Labyrinthe - Modification de la grille");
fenetre.revalidate(); // Rafraîchit l'affichage
}
/**
* Méthode appelée lorsque la simulation commence.
*/
private void debutSimulation() {
menuSimulation.getBoutonOnOff().setText("Arrêter");
// Empêche de modifier la configuration de la simulation en cours d'exécution (esthétique)
menuSimulation.getBoutonAleatoire().setEnabled(false);
menuSimulation.getBoutonDeterministe().setEnabled(false);
menuSimulation.getBoutonManuelle().setEnabled(false);
menuSimulation.getBoutonAutomatique().setEnabled(false);
}
/**
* Méthode pour commencer la simulation.
*/
private void commencerSimulation() {
debutSimulation();
menuSimulation.modifierInfo("Simulation en cours...");
algorithme = new Algorithmes(grilleConfig, grillePanel, menuSimulation);
// Supprime le KeyListener actuel s'il y en a un
if (keyListenerActuel != null) {
menuSimulation.removeKeyListener(keyListenerActuel);
}
if (menuSimulation.getBoutonAleatoire().isSelected()) { // Algorithme aléatoire
if (menuSimulation.getBoutonAutomatique().isSelected()) { // Visualisation automatique
algorithme.aleatoireAuto();
finSimulation();
} else if (menuSimulation.getBoutonManuelle().isSelected()) { // Visualisation manuelle
menuSimulation.modifierInfo("Appuyez sur une touche pour avancer.");
menuSimulation.ajouterKeyListener(keyListenerActuel = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent evenement) {
if (algorithme.aleatoireMan()) {
menuSimulation.removeKeyListener(this);
finSimulation();
}
}
});
menuSimulation.requestFocus();
}
} else if (menuSimulation.getBoutonDeterministe().isSelected()) { // Algorithme déterministe
if (menuSimulation.getBoutonAutomatique().isSelected()) { // Visualisation automatique
algorithme.deterministeAuto();
finSimulation();
} else if (menuSimulation.getBoutonManuelle().isSelected()) { // Visualisation manuelle
menuSimulation.modifierInfo("Appuyez sur une touche pour avancer.");
menuSimulation.ajouterKeyListener(keyListenerActuel = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent evenement) {
if (algorithme.deterministeMan()) {
menuSimulation.removeKeyListener(this);
finSimulation();
}
}
});
menuSimulation.requestFocus();
}
}
}
/**
* Méthode appelée lorsque la simulation est terminée.
*/
protected void finSimulation() {
menuSimulation.getBoutonOnOff().setText("Commencer");
// Réactive les boutons de configuration
menuSimulation.getBoutonAleatoire().setEnabled(true);
menuSimulation.getBoutonDeterministe().setEnabled(true);
menuSimulation.getBoutonManuelle().setEnabled(true);
menuSimulation.getBoutonAutomatique().setEnabled(true);
menuSimulation.modifierInfo("Vous pouvez commencer !");
}
}

193
sources/GrilleConfig.java Normal file
View File

@ -0,0 +1,193 @@
/**
* La classe <code>GrilleConfig</code> est utilisée pour stocker et partager les informations de configuration de la grille entre différentes classes de l'application.
* Cette classe contient des informations telles que la taille de la grille, sa configuration de départ ou l'état des cases.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class GrilleConfig {
/**
* Composante de la taille de la grille
*/
private int tailleGrille;
/**
* Composante de la configuration initiale de la grille (vide)
*/
private boolean grilleVide;
/**
* Composante de la configuration initiale de la grille (remplissage aléatoire)
*/
private boolean grilleAleatoire;
/**
* Composante de la position y de Thésée
*/
private int ligneThe;
/**
* Composante de la position x de Thésée
*/
private int colThe;
/**
* Composante de la position y de la sortie
*/
private int ligneSortie;
/**
* Composante de la position x de la sortie
*/
private int colSortie;
/**
* Composante de l'état de chaque case de la grille (vide ou bloquée)
*/
private byte[] grille;
/**
* Constructeur par défaut de la classe <code>GrilleConfig</code>.
*/
public GrilleConfig() {
// Volontairement vide
}
/**
* Récupère la taille de la grille.
*
* @return la taille de la grille
*/
public int getTailleGrille() {
return tailleGrille;
}
/**
* Définit la taille de la grille.
*
* @param tailleGrille la nouvelle taille de la grille
*/
public void setTailleGrille(int tailleGrille) {
this.tailleGrille = tailleGrille;
}
/**
* Indique si la grille doit être vide au départ.
*
* @return <code>true</code> si la grille doit être vide, <code>false</code> sinon
*/
public boolean isGrilleVide() {
return grilleVide;
}
/**
* Définit si la grille doit être vide au départ.
*
* @param grilleVide <code>true</code> pour une grille vide, <code>false</code> sinon
*/
public void setGrilleVide(boolean grilleVide) {
this.grilleVide = grilleVide;
}
/**
* Indique si la grille doit être remplie aléatoirement au départ.
*
* @return <code>true</code> si la grille doit être remplie aléatoirement, <code>false</code> sinon
*/
public boolean isGrilleAleatoire() {
return grilleAleatoire;
}
/**
* Définit si la grille doit être remplie aléatoirement au départ.
*
* @param grilleAleatoire <code>true</code> pour une grille remplie aléatoirement, <code>false</code> sinon
*/
public void setGrilleAleatoire(boolean grilleAleatoire) {
this.grilleAleatoire = grilleAleatoire;
}
/**
* Retourne la ligne se situe Thésée.
*
* @return la position y de Thésée
*/
public int getLigneThe() {
return ligneThe;
}
/**
* Définit la ligne se situe Thésée.
*
* @param ligneThe la position y de Thésée
*/
public void setLigneThe(int ligneThe) {
this.ligneThe = ligneThe;
}
/**
* Retourne la colonne se situe Thésée.
*
* @return la position x de Thésée
*/
public int getColThe() {
return colThe;
}
/**
* Définit la colonne se situe Thésée.
*
* @param colThe la position x de Thésée
*/
public void setColThe(int colThe) {
this.colThe = colThe;
}
/**
* Retourne la ligne se situe la sortie.
*
* @return la position y de la sortie
*/
public int getLigneSortie() {
return ligneSortie;
}
/**
* Définit la ligne se situe la sortie.
*
* @param ligneSortie la position y de la sortie
*/
public void setLigneSortie(int ligneSortie) {
this.ligneSortie = ligneSortie;
}
/**
* Retourne la colonne se situe la sortie.
*
* @return la position x de la sortie
*/
public int getColSortie() {
return colSortie;
}
/**
* Définit la colonne se situe la sortie.
*
* @param colSortie la position x de la sortie
*/
public void setColSortie(int colSortie) {
this.colSortie = colSortie;
}
/**
* Retourne l'état de chaque case sous forme d'un tableau de type byte.
*
* @return grille la grille
*/
public byte[] getGrille() {
return grille;
}
/**
* Définit l'état de chaque case sous forme d'un tableau de type byte.
*
* @param grille la grille
*/
public void setGrille(byte[] grille) {
this.grille = grille;
}
}

174
sources/GrillePanel.java Normal file
View File

@ -0,0 +1,174 @@
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
/**
* La classe <code>GrillePanel</code> est utilisée pour générer la grille en fonction de sa configuration.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class GrillePanel extends JPanel {
/**
* Composante déclarant une instance de GrilleConfig pour permettre d'initialiser la grille.
*/
private GrilleConfig grilleConfig;
/**
* Composante de l'état des cases de la grille.
*/
protected boolean[][] grille;
/**
* Composante de la position de Thésée.
*/
protected boolean[][] thesee;
/**
* Composante de la position de la sortie
*/
protected boolean[][] sortie;
/**
* Composante de la présence de Thésée dans la grille
*/
protected boolean theseePresent;
/**
* Composante de la présence de la sortie dans la grille
*/
protected boolean sortiePresente;
/**
* Constructeur de la classe <code>GrillePanel</code>.
*
* @param menuModification le menu qui permet de modifier la grille
* @param grilleConfig la configuration de la grille (taille, etc.)
* @param boutonPlacerThesee le bouton pour placer Thésée sur la grille
* @param boutonPlacerSortie le bouton pour placer la sortie sur la grille
*/
public GrillePanel(MenuModification menuModification, GrilleConfig grilleConfig, JToggleButton boutonPlacerThesee, JToggleButton boutonPlacerSortie) {
this.grilleConfig = grilleConfig;
int tailleGrille = grilleConfig.getTailleGrille();
grille = new boolean[tailleGrille][tailleGrille];
thesee = new boolean[tailleGrille][tailleGrille];
sortie = new boolean[tailleGrille][tailleGrille];
initialiserGrille();
}
/**
* Méthode pour initialiser la grille en fonction de la configuration.
*/
private void initialiserGrille() {
int tailleGrille = grilleConfig.getTailleGrille();
// Si la configuration vient d'un fichier, on parcourt chaque case en vérifiant son contenu
if (grilleConfig.getGrille() != null) {
byte grilleFichier[] = grilleConfig.getGrille();
for (int ligne = 0; ligne < tailleGrille; ligne++) {
for (int colonne = 0; colonne < tailleGrille; colonne++) {
// Converti le tableau grilleFichier de type byte en un tableau grille de type boolean
if (grilleFichier[colonne * tailleGrille + ligne] == 1) {
grille[ligne][colonne] = true;
} else {
grille[ligne][colonne] = false;
}
// Assigne la position de Thésée dans un tableau de type boolean
if (ligne == grilleConfig.getLigneThe() && colonne == grilleConfig.getColThe()) {
thesee[ligne][colonne] = true;
theseePresent = true;
} else {
thesee[ligne][colonne] = false;
}
// Assigne la position de la sortie dans un tableau de type boolean
if (ligne == grilleConfig.getLigneSortie() && colonne == grilleConfig.getColSortie()) {
sortie[ligne][colonne] = true;
sortiePresente = true;
} else {
sortie[ligne][colonne] = false;
}
}
}
// Si la grille vient d'être créée
} else {
byte[] grilleByte = new byte[tailleGrille * tailleGrille];
for (int ligne = 0; ligne < tailleGrille; ligne++) {
for (int colonne = 0; colonne < tailleGrille; colonne++) {
if (grilleConfig.isGrilleVide() || (grilleConfig.isGrilleAleatoire() && Math.random() < 0.5)) { // On écrit les cases vides
grille[ligne][colonne] = false;
grilleByte[colonne * tailleGrille + ligne] = 0;
} else { // On écrit les cases bloquées
grille[ligne][colonne] = true;
grilleByte[colonne * tailleGrille + ligne] = 1;
}
}
}
grilleConfig.setGrille(grilleByte);
}
}
/**
* Redéfinition de la méthode <code>paintComponent</code> pour dessiner la grille.
*
* @param pinceau le pinceau graphique utilisé pour le dessin
*/
@Override
protected void paintComponent(Graphics pinceau) {
super.paintComponent(pinceau);
// obligatoire : on crée un nouveau pinceau pour pouvoir le modifier plus tard
Graphics secondPinceau = pinceau.create();
// obligatoire : si le composant n'est pas censé être transparent
if (this.isOpaque()) {
// obligatoire : on repeint toute la surface avec la couleur de fond
secondPinceau.setColor(this.getBackground());
secondPinceau.fillRect(0, 0, this.getWidth(), this.getHeight());
}
// Définition des tailles du tableau
int tailleGrille = grilleConfig.getTailleGrille();
int tailleCase = Math.min(getWidth(), getHeight()) / tailleGrille; // La grille sera toujours au format carré
// Dessin de la grille case par case.
for (int ligne = 0; ligne < tailleGrille; ligne++) {
for (int colonne = 0; colonne < tailleGrille; colonne++) {
if (grille[ligne][colonne]) { // Dessine les cases bloquées (true)
secondPinceau.setColor(Color.BLACK);
secondPinceau.fillRect(colonne * tailleCase, ligne * tailleCase, tailleCase, tailleCase);
} else { // Dessine les cases vides (false)
secondPinceau.setColor(Color.WHITE);
secondPinceau.fillRect(colonne * tailleCase, ligne * tailleCase, tailleCase, tailleCase);
if (thesee[ligne][colonne]) { // Dessine Thésée
secondPinceau.setColor(Color.BLACK);
secondPinceau.setFont(new Font("Arial", Font.PLAIN, tailleCase));
// Récupère la taille du texte pour définir sa position
FontMetrics fm = secondPinceau.getFontMetrics();
int x = colonne * tailleCase + (tailleCase - fm.stringWidth("Θ")) / 2;
int y = ligne * tailleCase + (tailleCase - fm.getHeight()) / 2 + fm.getAscent();
secondPinceau.drawString("Θ", x, y);
} else if (sortie[ligne][colonne]) { // Dessine la sortie
secondPinceau.setColor(Color.BLACK);
secondPinceau.setFont(new Font("Arial", Font.PLAIN, tailleCase));
// Récupère la taille du texte pour définir sa position
FontMetrics fm = secondPinceau.getFontMetrics();
int x = colonne * tailleCase + (tailleCase - fm.stringWidth("")) / 2;
int y = ligne * tailleCase + (tailleCase - fm.getHeight()) / 2 + fm.getAscent();
secondPinceau.drawString("", x, y);
}
}
// Dessine les lignes de la grille
secondPinceau.setColor(Color.BLACK);
secondPinceau.drawRect(colonne * tailleCase, ligne * tailleCase, tailleCase, tailleCase);
}
}
}
}

44
sources/Main.java Normal file
View File

@ -0,0 +1,44 @@
import java.awt.Dimension;
import javax.swing.JFrame;
/**
* La classe <code>Main</code> est utilisée pour afficher la fenêtre du programme.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class Main {
/**
* Constante définissant la largeur minimale la fenêtre.
*/
private static final int LARGEUR_MIN = 300;
/**
* Constante définissant la hauteur minimale de la fenêtre.
*/
private static final int HAUTEUR_MIN = 300;
/**
* Constante définissant le titre de la fenêtre
*/
private static final String TITRE_FENETRE = "Labyrinthe - Menu principal";
/**
* La méthode principale qui est exécutée lors de l'exécution du programme.
* Crée et affiche la fenêtre graphique de l'application.
*
* @param args les arguments de ligne de commande, non utilisés dans cette application
*/
public static void main(String[] args) {
JFrame fenetre = new JFrame(TITRE_FENETRE);
MenuAccueil menuAccueil = new MenuAccueil();
fenetre.setMinimumSize(new Dimension(LARGEUR_MIN, HAUTEUR_MIN));
fenetre.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fenetre.add(menuAccueil);
fenetre.pack();
fenetre.setLocationRelativeTo(null);
fenetre.setVisible(true);
}
}

66
sources/MenuAccueil.java Normal file
View File

@ -0,0 +1,66 @@
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JPanel;
/**
* La classe <code>MenuAccueil</code> est utilisée pour générer le premier menu d'accueil, celui sur lequel on atterit lors du lancement du programme et qui propose 3 options :
* 1. charger une grille existante,
* 2. construire une nouvelle grille,
* 3. quitter le programme.
* Les boutons sont centrés verticalement et espacés avec un certaine marge.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class MenuAccueil extends JPanel {
/**
* Constante définissant les dimensions préférées du menu.
*/
private static final Dimension DIMENSIONS_MENU = new Dimension(640, 360);
/**
* Constante définissant les dimensions préférées d'un bouton.
*/
private static final Dimension DIMENSIONS_BOUTON = new Dimension(250, 50);
/**
* Constante définissant la marge d'un bouton.
*/
private static final int MARGE = 20;
/**
* Constructeur destiné à la création du menu.
* Crée le panneau, définit le gestionnaire de mise en page et ajoute les boutons avec leurs contraintes de positionnement.
*/
public MenuAccueil() {
setLayout(new GridBagLayout());
setPreferredSize(DIMENSIONS_MENU);
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(MARGE, MARGE, MARGE, MARGE); // Définition des marges tout autour des boutons
ajouterBouton("Charger une grille existante", 0, gbc);
ajouterBouton("Construire une nouvelle grille", 1, gbc);
ajouterBouton("Quitter", 2, gbc);
}
/**
* Méthode pour ajouter un bouton au panneau.
*
* @param text le texte du bouton
* @param gridY la position verticale du bouton (ou indice de la ligne)
* @param gbc les contraintes de positionnement du bouton
*/
private void ajouterBouton(String text, int gridY, GridBagConstraints gbc) {
gbc.gridy = gridY; // Attribut une ligne pour chaque bouton
JButton bouton = new JButton(text);
bouton.setPreferredSize(DIMENSIONS_BOUTON);
bouton.addActionListener(new ControleurAccueil()); // Ajoute l'écouteur d'événement
add(bouton, gbc);
}
}

View File

@ -0,0 +1,188 @@
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.border.EmptyBorder;
/**
* La classe <code>MenuInitialisation</code> est utilisée pour générer le menu qui permet la création d'une nouvelle grille.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class MenuInitialisation extends JPanel {
/**
* Constante définissant la marge du panneau.
*/
private static final int MARGE = 10;
/**
* Constante définissant la taille minimale d'une grille.
*/
private static final int GRILLE_MIN = 3;
/**
* Constante définissant la taille maximale d'une grille.
*/
private static final int GRILLE_MAX = 20;
/**
* Constante définissant la taille par défaut de la grille.
*/
private static final int GRILLE_INITIALE = 10;
/**
* Composante déclarant le slider permettant de décider de la taille de la grille.
*/
private JSlider sliderTailleGrille;
/**
* Composante déclarant le bouton permettant de partir d'une grille vide.
*/
private JRadioButton boutonGrilleVide;
/**
* Composante déclarant le bouton permettant de partir d'une grille remplie aléatoirement.
*/
private JRadioButton boutonGrilleAleatoire;
/**
* Composante déclarant le bouton permettant de revenir en arrière.
*/
private JButton boutonRetour;
/**
* Composante déclarant le bouton permettant de valider les options.
*/
private JButton boutonValider;
/**
* Composante de la configuration initiale de la grille.
*/
private GrilleConfig grilleConfig;
/**
* Constructeur destiné à la création du menu permettant à l'utilisateur de personnaliser la création de la grille de jeu.
* Ce panneau est composé d'un slider permettant de choisir la taille de la grille, de deux boutons radio permettant de choisir la configuration de départ de la grille, ainsi que de deux boutons pour valider les choix ou retourner en arrière.
* Le panneau est disposé en utilisant les gestionnaires <code>BorderLayout</code> et le <code>GridBagLayout</code>.
*/
public MenuInitialisation() {
// Création de l'instance de GrilleConfig
this.grilleConfig = new GrilleConfig();
// Configuration générale du menu
setLayout(new BorderLayout());
setBorder(new EmptyBorder(MARGE, MARGE, MARGE, MARGE));
// Configuration du panel principale contenant les paramètres de la grille
JPanel panelCentre = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1; // La colonne est redimensionnée pour occuper toute la largeur disponible
// Configuration du panel secondaire contenant les bouton "Valider" et "Retour"
JPanel panelSud = new JPanel(new BorderLayout());
// Création des composants
JLabel labelTailleGrille = new JLabel("Taille de la grille :");
sliderTailleGrille = new JSlider(GRILLE_MIN, GRILLE_MAX, GRILLE_INITIALE);
sliderTailleGrille.setMajorTickSpacing(1);
sliderTailleGrille.setPaintLabels(true);
sliderTailleGrille.setSnapToTicks(true);
JLabel labelGrilleDepart = new JLabel("Configuration de départ :");
boutonGrilleVide = new JRadioButton("Vide");
boutonGrilleAleatoire = new JRadioButton("Aléatoire");
ButtonGroup choixGrilleDepart = new ButtonGroup();
choixGrilleDepart.add(boutonGrilleVide);
choixGrilleDepart.add(boutonGrilleAleatoire);
boutonGrilleVide.addActionListener(new ControleurInitialisation(this, grilleConfig));
boutonGrilleAleatoire.addActionListener(new ControleurInitialisation(this, grilleConfig));
boutonRetour = new JButton("Retour");
boutonValider = new JButton("Valider");
boutonValider.setEnabled(false);
boutonRetour.addActionListener(new ControleurInitialisation(this, grilleConfig));
boutonValider.addActionListener(new ControleurInitialisation(this, grilleConfig));
// Positionnement et ajout des composants
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.insets = new Insets(20, 60, 10, 20);
gbc.fill = GridBagConstraints.HORIZONTAL; // Le composant est étiré horizontalement
panelCentre.add(labelTailleGrille, gbc);
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 2;
gbc.insets = new Insets(10, 20, 40, 20);
gbc.fill = GridBagConstraints.HORIZONTAL; // Le composant est étiré horizontalement
panelCentre.add(sliderTailleGrille, gbc);
gbc.gridx = 0;
gbc.gridy = 2;
gbc.gridwidth = 2;
gbc.insets = new Insets(20, 60, 10, 20);
gbc.fill = GridBagConstraints.HORIZONTAL; // Le composant est étiré horizontalement
panelCentre.add(labelGrilleDepart, gbc);
gbc.gridx = 0;
gbc.gridy = 3;
gbc.gridwidth = 1;
gbc.insets = new Insets(10, 20, 40, 20);
gbc.fill = GridBagConstraints.NONE; // Le composant n'est pas étiré
panelCentre.add(boutonGrilleVide, gbc);
gbc.gridx = 1;
gbc.gridy = 3;
gbc.gridwidth = 1;
gbc.insets = new Insets(10, 20, 40, 20);
gbc.fill = GridBagConstraints.NONE; // Le composant n'est pas étiré
panelCentre.add(boutonGrilleAleatoire, gbc);
panelSud.add(boutonRetour, BorderLayout.WEST);
panelSud.add(boutonValider, BorderLayout.EAST);
add(panelCentre, BorderLayout.CENTER);
add(panelSud, BorderLayout.SOUTH);
}
/**
* Méthode pour récupérer la valeur du slider.
*
* @return la valeur sélectionnée pour la taille de la grille
*/
public int getTailleGrille() {
return sliderTailleGrille.getValue();
}
/**
* Méthode pour récupérer la valeur du bouton grille vide.
*
* @return <code>true</code> si le bouton grille vide est sélectionné, <code>false</code> sinon
*/
public boolean isGrilleVideSelected() {
return boutonGrilleVide.isSelected();
}
/**
* Méthode pour récupérer la valeur du bouton grille aléatoire.
*
* @return <code>true</code> si le bouton grille aléatoire est sélectionné, <code>false</code> sinon
*/
public boolean isGrilleAleatoireSelected() {
return boutonGrilleAleatoire.isSelected();
}
/**
* Méthode pour récupérer la valeur du bouton valider.
*
* @return le bouton valider
*/
public JButton getBoutonValider() {
return boutonValider;
}
}

View File

@ -0,0 +1,137 @@
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
/**
* La classe <code>MenuModification</code> est utilisée pour générer le menu qui permet d'afficher la grille et de la modifier.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class MenuModification extends JPanel {
/**
* Composante déclarant la grille qui sera affichée.
*/
private GrillePanel grillePanel;
/**
* Composante déclarant le bouton pour placer Thésée.
*/
private JToggleButton boutonPlacerThesee;
/**
* Composante déclarant le bouton pour placer la sortie.
*/
private JToggleButton boutonPlacerSortie;
/**
* Composante déclarant le texte d'information sur l'utilisation du menu.
*/
private JLabel texteInfo;
/**
* Composante déclarant le bouton de retour.
*/
private JButton boutonRetour;
/**
* Composante déclarant le bouton de sauvegarde.
*/
private JButton boutonSauvegarder;
/**
* Composante déclarant le bouton pour continuer avec la grille.
*/
private JButton boutonContinuer;
/**
* Constructeur destiné à la création du menu permettant à l'utilisateur de modifier la grille.
* Ce panneau est consitué de la grille au centre avec des boutons à gauche permettant de choisir de placer Thésée ou la sortie. En bas de la grille, les boutons permettant de sauvegarder et de continuer sont désactivés si la grille n'est pas complète.
*
* @param grilleConfig la configuration de la grille (taille, etc.)
*/
public MenuModification(GrilleConfig grilleConfig) {
// Définition du gestionnaire de mise en page
setLayout(new BorderLayout());
// Ajout des boutons "Placer Thésée" et "Placer Sortie" à gauche de la grille
JPanel panelOuest = new JPanel(new GridLayout(2, 1));
boutonPlacerThesee = new JToggleButton("Placer Thésée");
boutonPlacerSortie = new JToggleButton("Placer sortie");
// Ajout de la grille au centre
grillePanel = new GrillePanel(this, grilleConfig, boutonPlacerThesee, boutonPlacerSortie);
grillePanel.addMouseListener(new ModificateurGrille(this, grillePanel, grilleConfig, boutonPlacerThesee, boutonPlacerSortie));
add(grillePanel, BorderLayout.CENTER);
// Ajout des écouteurs
ControleurModification controleur = new ControleurModification(boutonPlacerThesee, boutonPlacerSortie, grilleConfig, grillePanel);
boutonPlacerThesee.addActionListener(controleur);
boutonPlacerSortie.addActionListener(controleur);
panelOuest.add(boutonPlacerThesee);
panelOuest.add(boutonPlacerSortie);
add(panelOuest, BorderLayout.WEST);
// Ajout du texte d'information au dessus de la grille
JPanel panelNord = new JPanel(new FlowLayout());
texteInfo = new JLabel("Cliquez sur une case pour modifier son état.");
panelNord.add(texteInfo);
add(panelNord, BorderLayout.NORTH);
// Ajout des boutons "Retour", "Sauvegarder" et "Continuer" en bas de la fenêtre
JPanel panelSud = new JPanel(new GridLayout(1, 3));
boutonRetour = new JButton("Retour");
boutonSauvegarder = new JButton("Sauvegarder");
boutonContinuer = new JButton("Continuer");
if (!grillePanel.theseePresent && !grillePanel.sortiePresente) {
boutonSauvegarder.setEnabled(false);
boutonContinuer.setEnabled(false);
}
// Ajout des écouteurs
boutonRetour.addActionListener(controleur);
boutonSauvegarder.addActionListener(controleur);
boutonContinuer.addActionListener(controleur);
panelSud.add(boutonRetour);
panelSud.add(boutonSauvegarder);
panelSud.add(boutonContinuer);
add(panelSud, BorderLayout.SOUTH);
}
/**
* Méthode pour modifier le texte d'information
*
* @param texte la texte à afficher
*/
public void modifierInfo(String texte) {
texteInfo.setText(texte);
}
/**
* Méthode pour récupérer la valeur du bouton sauvegarder.
*
* @return le bouton sauvegarder
*/
public JButton getBoutonSauvegarder() {
return boutonSauvegarder;
}
/**
* Méthode pour récupérer la valeur du bouton continuer.
*
* @return le bouton contineur
*/
public JButton getBoutonContinuer() {
return boutonContinuer;
}
}

212
sources/MenuSimulation.java Normal file
View File

@ -0,0 +1,212 @@
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.KeyListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
/**
* La classe <code>MenuSimulation</code> est utilisée pour générer le menu qui permet de configurer lancer la simulation.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class MenuSimulation extends JPanel {
/**
* Bouton radio permettant de choisir l'algorithme aléatoire.
*/
private JRadioButton boutonAleatoire;
/**
* Bouton radio permettant de choisir l'algorithme déterministe.
*/
private JRadioButton boutonDeterministe;
/**
* Bouton permettant de choisir la visualisation du déroulement manuelle.
*/
private JRadioButton boutonManuelle;
/**
* Bouton permettant de choisir la visualisation du déroulement automatique.
*/
private JRadioButton boutonAutomatique;
/**
* Bouton permettant de commencer ou arrêter la simulation.
*/
private JButton boutonOnOff;
/**
* Composante déclarant le texte d'information sur la simulation.
*/
private JLabel texteInfo;
/**
* Constructeur destiné à la création du menu permettant à l'utilisateur de choisir un algorithme et son mode de visualisation
* Ce panneau est consitué de la grille au centre, avec des boutons radio à gauche pour configurer la simulation et des boutons à droite pour la lancer ou l'arrêter.
*
* @param grilleConfig la configuration de la grille (taille, etc.)
*/
public MenuSimulation(GrilleConfig grilleConfig) {
// Définition du gestionnaire de mise en page
setLayout(new BorderLayout());
// Ajout du panel de gauche pour choisir l'algorithme et la visualisation du déroulement de la simulation
JPanel panelOuest = new JPanel(new GridLayout(6, 1));
//Ajout des boutons pour choisir l'algorithme
JLabel choixAlgo = new JLabel("Algorithme :");
boutonAleatoire = new JRadioButton("Aléatoire");
boutonDeterministe = new JRadioButton("Déterministe");
ButtonGroup groupeAlgo = new ButtonGroup();
groupeAlgo.add(boutonAleatoire);
groupeAlgo.add(boutonDeterministe);
panelOuest.add(choixAlgo);
panelOuest.add(boutonAleatoire);
panelOuest.add(boutonDeterministe);
// Ajout des boutons pour choisir la visualisation
JLabel choixVisu = new JLabel("Visualisation :");
boutonManuelle = new JRadioButton("Manuelle");
boutonAutomatique = new JRadioButton("Automatique");
ButtonGroup groupeVisu = new ButtonGroup();
groupeVisu.add(boutonManuelle);
groupeVisu.add(boutonAutomatique);
panelOuest.add(choixVisu);
panelOuest.add(boutonManuelle);
panelOuest.add(boutonAutomatique);
add(panelOuest, BorderLayout.WEST);
// Ajout du texte d'information au dessus de la grille
JPanel panelNord = new JPanel(new FlowLayout());
texteInfo = new JLabel("Veuillez paramétrer la simulation.");
panelNord.add(texteInfo);
add(panelNord, BorderLayout.NORTH);
// Ajout de la grille au centre
GrillePanel grillePanel = new GrillePanel(null, grilleConfig, null, null);
add(grillePanel, BorderLayout.CENTER);
// Ajout du bouton pour commencer ou arrêter la simulation
boutonOnOff = new JButton("Commencer");
boutonOnOff.setEnabled(false); // Désactivé par défaut
add(boutonOnOff, BorderLayout.EAST);
// Ajout des boutons "Retour" et "Quitter"
JPanel panelSud = new JPanel(new GridLayout(1, 2));
JButton boutonRetour = new JButton("Retour");
JButton boutonQuitter = new JButton("Quitter");
panelSud.add(boutonRetour);
panelSud.add(boutonQuitter);
add(panelSud, BorderLayout.SOUTH);
// Ajout des écouteurs
ControleurSimulation controleur = new ControleurSimulation(this, grilleConfig, grillePanel);
boutonAleatoire.addActionListener(controleur);
boutonDeterministe.addActionListener(controleur);
boutonManuelle.addActionListener(controleur);
boutonAutomatique.addActionListener(controleur);
boutonOnOff.addActionListener(controleur);
boutonRetour.addActionListener(controleur);
boutonQuitter.addActionListener(controleur);
}
/**
* Méthode pour ajouter les écouteurs d'action sur le clavier.
*
* @param keyListener l'écouteur du clavier
*/
public void ajouterKeyListener(KeyListener keyListener) {
addKeyListener(keyListener);
setFocusable(true);
}
/**
* Méthode pour vérifier si un KeyListener est déjà présent.
*
* @return <code>true</code> si un KeyListener est présent, <code>false</code> sinon
*/
public boolean KeyListenerPresent() {
return getKeyListeners().length > 0;
}
/**
* Méthode pour modifier le texte d'information
*
* @param texte la texte à afficher
*/
public void modifierInfo(String texte) {
texteInfo.setText(texte);
}
/**
* Méthode pour vérifier si un algorithme est sélectionné.
*
* @return <code>true</code> si un algorithme est sélectionné, <code>false</code> sinon
*/
public boolean isAlgoSelected() {
if (!(boutonAleatoire.isSelected() || boutonDeterministe.isSelected())) {
return false;
} else {
return true;
}
}
/**
* Méthode pour vérifier si une visualisation est sélectionnée.
*
* @return <code>true</code> si une visualisation est sélectionnée, <code>false</code> sinon
*/
public boolean isVisuSelected() {
if (!(boutonManuelle.isSelected() || boutonAutomatique.isSelected())) {
return false;
} else {
return true;
}
}
/**
* Méthode pour récupérer la valeur du bouton commencer.
*
* @return le bouton On/Off
*/
public JButton getBoutonOnOff() {
return boutonOnOff;
}
/**
* Méthode pour récupérer la valeur du bouton aléatoire.
*
* @return le bouton aléatoire
*/
public JRadioButton getBoutonAleatoire() {
return boutonAleatoire;
}
/**
* Méthode pour récupérer la valeur du bouton déterministe.
*
* @return le bouton déterministe
*/
public JRadioButton getBoutonDeterministe() {
return boutonDeterministe;
}
/**
* Méthode pour récupérer la valeur du bouton manuelle.
*
* @return le bouton manuelle
*/
public JRadioButton getBoutonManuelle() {
return boutonManuelle;
}
/**
* Méthode pour récupérer la valeur du bouton automatique.
*
* @return le bouton automatique
*/
public JRadioButton getBoutonAutomatique() {
return boutonAutomatique;
}
}

View File

@ -0,0 +1,297 @@
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JToggleButton;
/**
* La classe <code>ModificateurGrille</code> implémente <code>MouseListener</code> et permet de gérer les événements de la souris sur la grille.
* Elle permet de placer Thésée, la sortie, et de modifier l'état des cases (bloquées ou non) en fonction des boutons sélectionnés.
*
* @version 1.1
* @author Aimad MACHROUH
* @author Yles ZOURDANI
*/
public class ModificateurGrille implements MouseListener {
/**
* Le menu pour récupérer les boutons.
*/
private MenuModification menuModification;
/**
* Le panel de la grille pour récupérer les coordonnées des clics de souris.
*/
private GrillePanel grillePanel;
/**
* La configuration de la grille (taille, etc.).
*/
private GrilleConfig grilleConfig;
/**
* Le bouton pour placer Thésée sur la grille.
*/
private JToggleButton boutonPlacerThesee;
/**
* Le bouton pour placer la sortie sur la grille.
*/
private JToggleButton boutonPlacerSortie;
/**
* Grille au format byte[] pour la stocker dans GrilleConfig
*/
private byte[] grilleByte;
/**
* Constructeur de la classe <code>ModificateurGrille</code>.
*
* @param menuModification le menu pour récupérer les boutons.
* @param grillePanel le panel de la grille pour récupérer les coordonnées des clics de souris
* @param grilleConfig la configuration de la grille (taille, etc.)
* @param boutonPlacerThesee le bouton pour placer Thésée sur la grille
* @param boutonPlacerSortie le bouton pour placer la sortie sur la grille
*/
public ModificateurGrille(MenuModification menuModification, GrillePanel grillePanel, GrilleConfig grilleConfig, JToggleButton boutonPlacerThesee, JToggleButton boutonPlacerSortie) {
this.menuModification = menuModification;
this.grillePanel = grillePanel;
this.grilleConfig = grilleConfig;
this.boutonPlacerThesee = boutonPlacerThesee;
this.boutonPlacerSortie = boutonPlacerSortie;
grilleByte = grilleConfig.getGrille();
}
/**
* Méthode appelée lorsqu'un clic de souris est détecté sur la grille.
*
* @param evenement l'événement de clic de souris
*/
public void mouseClicked(MouseEvent evenement) {
// Récupération des dimensions de la grille et de ses cases
int tailleGrille = grilleConfig.getTailleGrille();
int tailleCase = Math.min(grillePanel.getWidth(), grillePanel.getHeight()) / tailleGrille;
// Distinction des cases en fonction de leur taille, pour permettre à la méthode de savoir on clique
int ligne = evenement.getY() / tailleCase;
int colonne = evenement.getX() / tailleCase;
menuModification.modifierInfo("Cliquez sur une case pour modifier son état.");
if (ligne >= 0 && ligne < tailleGrille && colonne >= 0 && colonne < tailleGrille) { // Vérifie qu'on est bien à l'intérieur de la grille
if (boutonPlacerThesee.isSelected()) {
placerThesee(ligne, colonne);
} else if (boutonPlacerSortie.isSelected()) {
placerSortie(ligne, colonne);
} else {
inverserEtatCase(ligne, colonne);
}
grillePanel.repaint();
}
}
/**
* Place Thésée sur la case spécifiée ou le supprime s'il est déjà présent.
* Supprime la sortie si elle est présente sur la case.
*
* @param ligne la ligne de la case
* @param colonne la colonne de la case
*/
private void placerThesee(int ligne, int colonne) {
if (!grillePanel.grille[ligne][colonne]) {
if (grillePanel.sortie[ligne][colonne]) {
supprimerSortie(ligne, colonne);
}
if (grillePanel.thesee[ligne][colonne]) {
supprimerThesee(ligne, colonne);
} else {
deplacerThesee(ligne, colonne);
}
} else {
menuModification.modifierInfo("Cette case est bloquée.");
}
// On active/désactive les boutons "Sauvegarder" et "Continuer" en fonction de la présence de Thésée et de la sortie
if (grillePanel.theseePresent && grillePanel.sortiePresente) {
menuModification.getBoutonSauvegarder().setEnabled(true);
menuModification.getBoutonContinuer().setEnabled(true);
} else {
menuModification.getBoutonSauvegarder().setEnabled(false);
menuModification.getBoutonContinuer().setEnabled(false);
}
}
/**
* Place la sortie sur la case spécifiée ou la supprime si elle est déjà présente.
* Supprime Thésée si il est présente sur la case.
*
* @param ligne la ligne de la case
* @param colonne la colonne de la case
*/
private void placerSortie(int ligne, int colonne) {
if (!grillePanel.grille[ligne][colonne]) {
if (grillePanel.thesee[ligne][colonne]) {
supprimerThesee(ligne, colonne);
}
if (grillePanel.sortie[ligne][colonne]) {
supprimerSortie(ligne, colonne);
} else {
deplacerSortie(ligne, colonne);
}
// On active/désactive les boutons "Sauvegarder" et "Continuer" en fonction de la présence de Thésée et de la sortie
if (grillePanel.theseePresent && grillePanel.sortiePresente) {
menuModification.getBoutonSauvegarder().setEnabled(true);
menuModification.getBoutonContinuer().setEnabled(true);
} else {
menuModification.getBoutonSauvegarder().setEnabled(false);
menuModification.getBoutonContinuer().setEnabled(false);
}
} else {
menuModification.modifierInfo("Cette case est bloquée.");
}
}
/**
* Inverse l'état (vide ou bloquée) de la case spécifiée.
* Supprime Thésée ou la sortie si l'un d'eux est présent sur la case.
*
* @param ligne la ligne de la case
* @param colonne la colonne de la case
*/
private void inverserEtatCase(int ligne, int colonne) {
if (grillePanel.thesee[ligne][colonne]) {
supprimerThesee(ligne, colonne);
} else if (grillePanel.sortie[ligne][colonne]) {
supprimerSortie(ligne, colonne);
}
grillePanel.grille[ligne][colonne] = !grillePanel.grille[ligne][colonne];
if (grillePanel.grille[ligne][colonne]) {
grilleByte[colonne * grilleConfig.getTailleGrille() + ligne] = 1;
} else {
grilleByte[colonne * grilleConfig.getTailleGrille() + ligne] = 0;
}
grilleConfig.setGrille(grilleByte);
}
/**
* Supprime Thésée de la case spécifiée.
*
* @param ligne la ligne de la case
* @param colonne la colonne de la case
*/
private void supprimerThesee(int ligne, int colonne) {
grillePanel.thesee[ligne][colonne] = false;
grillePanel.theseePresent = false;
// On active/désactive les boutons "Sauvegarder" et "Continuer" en fonction de la présence de Thésée et de la sortie
if (grillePanel.theseePresent && grillePanel.sortiePresente) {
menuModification.getBoutonSauvegarder().setEnabled(true);
menuModification.getBoutonContinuer().setEnabled(true);
} else {
menuModification.getBoutonSauvegarder().setEnabled(false);
menuModification.getBoutonContinuer().setEnabled(false);
}
}
/**
* Déplace Thésée de sa position actuelle à la case spécifiée.
*
* @param ligne la ligne de la case
* @param colonne la colonne de la case
*/
private void deplacerThesee(int ligne, int colonne) {
supprimerTheseeExistant();
grillePanel.thesee[ligne][colonne] = true;
grillePanel.theseePresent = true;
grilleConfig.setLigneThe(ligne);
grilleConfig.setColThe(colonne);
}
/**
* Explore les case chaque cases de la grille jusqu'à trouver Thésée et le supprime.
*/
private void supprimerTheseeExistant() {
for (int i = 0; i < grilleConfig.getTailleGrille(); i++) {
for (int j = 0; j < grilleConfig.getTailleGrille(); j++) {
if (grillePanel.thesee[i][j]) {
supprimerThesee(i, j);
return;
}
}
}
}
/**
* Supprime la sortie de la case spécifiée.
*
* @param ligne la ligne de la case
* @param colonne la colonne de la case
*/
private void supprimerSortie(int ligne, int colonne) {
grillePanel.sortie[ligne][colonne] = false;
grillePanel.sortiePresente = false;
// On active/désactive les boutons "Sauvegarder" et "Continuer" en fonction de la présence de Thésée et de la sortie
if (grillePanel.theseePresent && grillePanel.sortiePresente) {
menuModification.getBoutonSauvegarder().setEnabled(true);
menuModification.getBoutonContinuer().setEnabled(true);
} else {
menuModification.getBoutonSauvegarder().setEnabled(false);
menuModification.getBoutonContinuer().setEnabled(false);
}
}
/**
* Déplace la sortie de sa position actuelle à la case spécifiée.
*
* @param ligne la ligne de la case
* @param colonne la colonne de la case
*/
private void deplacerSortie(int ligne, int colonne) {
supprimerSortieExistante();
grillePanel.sortie[ligne][colonne] = true;
grillePanel.sortiePresente = true;
grilleConfig.setLigneSortie(ligne);
grilleConfig.setColSortie(colonne);
}
/**
* Explore les case chaque cases de la grille jusqu'à trouver la sortie et la supprime.
*/
private void supprimerSortieExistante() {
for (int i = 0; i < grilleConfig.getTailleGrille(); i++) {
for (int j = 0; j < grilleConfig.getTailleGrille(); j++) {
if (grillePanel.sortie[i][j]) {
supprimerSortie(i, j);
return;
}
}
}
}
/**
* Implémentation de la méthode <code>mousePressed</code> de l'interface <code>MouseListener</code>.
* Cette méthode est vide car elle n'est pas utilisée dans cette classe.
*/
public void mousePressed(MouseEvent e) {
// Auto-generated method stub
}
/**
* Implémentation de la méthode <code>mouseReleased</code> de l'interface <code>MouseListener</code>.
* Cette méthode est vide car elle n'est pas utilisée dans cette classe.
*/
public void mouseReleased(MouseEvent e) {
// Auto-generated method stub
}
/**
* Implémentation de la méthode <code>mouseEntered</code> de l'interface <code>MouseListener</code>.
* Cette méthode est vide car elle n'est pas utilisée dans cette classe.
*/
public void mouseEntered(MouseEvent e) {
// Auto-generated method stub
}
/**
* Implémentation de la méthode <code>MouseExited</code> de l'interface <code>MouseListener</code>.
* Cette méthode est vide car elle n'est pas utilisée dans cette classe.
*/
public void mouseExited(MouseEvent e) {
// Auto-generated method stub
}
}