Projet 2023
2
.gitignore
vendored
@ -59,3 +59,5 @@ tags
|
||||
# Built Visual Studio Code Extensions
|
||||
*.vsix
|
||||
|
||||
# Apple
|
||||
.DS_Store
|
||||
|
73
Makefile
Normal 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
@ -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
After Width: | Height: | Size: 177 KiB |
BIN
img/deplacement.png
Normal file
After Width: | Height: | Size: 218 KiB |
BIN
img/deplacementBloquee.png
Normal file
After Width: | Height: | Size: 219 KiB |
BIN
img/erreur.png
Normal file
After Width: | Height: | Size: 166 KiB |
BIN
img/exemple.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
img/fichier.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
img/finBloquee.png
Normal file
After Width: | Height: | Size: 120 KiB |
BIN
img/finDeterministe.png
Normal file
After Width: | Height: | Size: 123 KiB |
BIN
img/grille.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
img/initialisation.png
Normal file
After Width: | Height: | Size: 268 KiB |
BIN
img/modification.png
Normal file
After Width: | Height: | Size: 281 KiB |
BIN
img/simulation.png
Normal file
After Width: | Height: | Size: 304 KiB |
420
sources/Algorithmes.java
Normal 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 où l'on affiche seulement le résultat et Manuel où 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;
|
||||
}
|
||||
}
|
186
sources/ControleurAccueil.java
Normal 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
|
||||
}
|
||||
}
|
104
sources/ControleurInitialisation.java
Normal 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
|
||||
}
|
||||
}
|
219
sources/ControleurModification.java
Normal 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
|
||||
}
|
||||
}
|
180
sources/ControleurSimulation.java
Normal 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
@ -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 où se situe Thésée.
|
||||
*
|
||||
* @return la position y de Thésée
|
||||
*/
|
||||
public int getLigneThe() {
|
||||
return ligneThe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit la ligne où 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 où se situe Thésée.
|
||||
*
|
||||
* @return la position x de Thésée
|
||||
*/
|
||||
public int getColThe() {
|
||||
return colThe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit la colonne où 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 où se situe la sortie.
|
||||
*
|
||||
* @return la position y de la sortie
|
||||
*/
|
||||
public int getLigneSortie() {
|
||||
return ligneSortie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit la ligne où se situe la sortie.
|
||||
*
|
||||
* @param ligneSortie la position y de la sortie
|
||||
*/
|
||||
public void setLigneSortie(int ligneSortie) {
|
||||
this.ligneSortie = ligneSortie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la colonne où se situe la sortie.
|
||||
*
|
||||
* @return la position x de la sortie
|
||||
*/
|
||||
public int getColSortie() {
|
||||
return colSortie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Définit la colonne où 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
@ -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
@ -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
@ -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);
|
||||
}
|
||||
}
|
188
sources/MenuInitialisation.java
Normal 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;
|
||||
}
|
||||
}
|
137
sources/MenuModification.java
Normal 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
@ -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;
|
||||
}
|
||||
}
|
297
sources/ModificateurGrille.java
Normal 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 où 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
|
||||
}
|
||||
}
|