Projet Java Swing – S2
Remarques :
• Projet individuel à rendre pendant la première séance de TP de la semaine du 17/5/2021.
Votre enseignant évaluera votre travail en vous posant des questions concernant la conception et l'implémentation de l'application.
• Toute tentative de copiage sera fortement pénalisée.
Enoncé :
Description générale
:
Vous devez développer le jeu « Memory » en Java Swing ou FX selon les figures suivantes :
La fenêtre principale contient une grille en 2D (4*4) de boutons, un chronomètre et le nombre d'essais restants. Chaque bouton dans la grille 2D cache une image et il existe 2 exemplaires de chaque image dans la grille 2D. Lorsque le joueur appuie sur un bouton, l'image associée à ce bouton est affichée. L'objectif du jeu est de découvrir le plus rapidement possible les 2 exemplaires de chaque image dans la grille 2D en appuyant successivement sur les 2 boutons où les 2 exemplaires sont cachées. S'il se trompe il perd un essai des essais restants, initialement il a trois essais. Le jeu se termine si le joueur découvre la position de toutes les paires d'images cachées ou s'il a perdu tous ses essais. À la fin du jeu, une petite fenêtre est affichée. Si le joueur a perdu, la fenêtre contiendra un message de consolation. Sinon, la fenêtre affichera un message de félicitation et le temps pris par l'utilisateur. Ce dernier sera sauvegardé dans un fichier s'il est un des trois meilleurs temps réalisés.
La fenêtre principale contient aussi un menu pour recommencer le jeu et afficher dans une petite fenêtre les meilleurs temps réalisés.
Implémentation
:
Votre application suivra le modèle MVC :
• Modèle : Le modèle stocke les images, l'ordre dans lequel elles ont été dissimulées dans la grille 2D (l'ordre des images dans la grille 2D est aléatoire et différent pour chaque partie) et leurs états dans la partie courante (trouvées ou pas), le nombre d'essais restants, le temps final de la partie courante, l'état de la partie courante et les meilleurs trois temps réalisés.
Ajoutons à cela, les getters et setters de toutes ces données.
• Vue : La vue affiche la grille, le chronomètre et le nombre d'essais restants. Elle comprend aussi un menu avec deux items : « Nouvelle partie » et « meilleurs scores ».
• Contrôleurs : Il existe deux contrôleurs pour gérer les interactions entre l'interface graphique et l'utilisateur :
ControlBouton : Gère les évènements de type ActionEvent générés lorsqu'un des boutons de la grille est appuyé :
1. Si le jeu n'a pas encore démarré, cet événement commence le jeu en changeant son état dans le modèle, active le chronomètre (classe Chrono.java publiée sur la même page web que cet énoncé), affiche l'image cachée sous le bouton sélectionné et désactive le bouton.
2. Si le jeu a démarré et une image est déjà affichée, l'image cachée sous le bouton sélectionné est affichée et comparée à l'autre. Si elles sont identiques les deux images restent affichées, leurs boutons désactivés, l'état de la partie est mis a jour et l'image marquée comme trouvée dans le modèle. Sinon, les deux images sont cachées de nouveau, les deux boutons sont réactivés, le nombre d'essais restants est décrémenté de 1 et l'état de la partie est mis a jour.
3. Lorsque l'utilisateur résout le puzzle, le chronomètre est arrêté, tous les boutons sont désactivés, une petite fenêtre est affichée et le temps est comparé aux meilleurs scores. S'il est meilleur que les scores existants, il est sauvegardé dans le fichier des scores. Le fichier des scores est un fichier texte qui contient les trois meilleurs scores.
4. Lorsque l'utilisateur perd tous ses essais, une fenêtre est affichée et tous les boutons sont désactivés.
ControlMenu : Gère le menu :
1. Si le menu « Nouvelle partie » est sélectionné, la partie est réinitialisée (la vue et le modèle sont réinitialisés).
2. Si le menu « Meilleurs scores » est sélectionné, les meilleurs scores sont affichés dans une petite fenêtre.
Fonctionnalité à implémenter pour gagner un bonus
:
Ajouter à la barre de menu, un menu taille qui comprend trois items : « 3x3 », « 4x4 » et « 5x5 ».
Ces options permettent à l'utilisateur de choisir la taille de la grille du puzzle. Bien sûr le nombre d'essais doit être proportionnel à la taille de la grille et les meilleurs scores doivent être sauvegardés dans trois fichiers différents en fonction de la taille de la grille. Enfin, il faut ajouter un bouton piège pour les grilles avec un nombre de boutons impair.
I ndices :
• Utiliser setPreferredSize(...) pour régler les dimensions d'un bouton
• Utiliser setEnabled(false) pour désactiver un bouton
• Utiliser setIcon(...) puis setDisabledIcon(...) pour afficher une image sur un bouton désactivé
• Pour utiliser le chronomètre de la classe Chrono : Chrono c=new Chrono(labelTemps) ; c.start() ; //pour démarrer le chronomètre c.stop() ; //pour arrêter le chronomètre
• Pour charger les images d'un répertoire avec le chemin absolu stocké dans chemin : ImageIcon [] listeImages=new ImageIcon[nbreImages];
try{
File folder=new File(chemin);
int cpt=0;
for (final File fileEntry : folder.listFiles()) {
listeImages[cpt]=new ImageIcon(chemin+'/'+fileEntry.getName()) ; cpt++ ;
}
}catch(Exception e){ e.printStackTrace(); }
• Pour charger les meilleurs scores du fichier avec le chemin absolu stocké dans chemin : try{
cpt=0 ;
BufferedReader br = new BufferedReader(new FileReader(chemin));
while ((s = br.readLine()) != null) { bestScores[cpt]=Float.parseFloat(s);
cpt++;
}
br.close();
}
catch (Exception e){ e.printStackTrace(); }
• Pour sauvegarder les meilleurs scores : try{
BufferedWriter bw = new BufferedWriter(new FileWriter(scoresPath));
bw.write(""+bestScores[0]);
bw.newLine();
bw.write(""+bestScores[1]);
bw.newLine();
bw.write(""+bestScores[2]);
bw.newLine();
bw.close();
}catch(Exception e){ e.printStackTrace(); }
• Pour calculer un ordre aléatoire des images au début de la partie et le stocké dans order : int [] order=new int[sizeOfGrid];
Vector v=new Vector();
for(int i=0;i<sizeOfGrid;i++) v.add((int)(i%(sizeOfGrid/2)));
for(int i=0;i<sizeOfGrid;i++){
int rand= (int) (Math.random()*v.size());
order[i]=(Integer)(v.elementAt(rand));
v.removeElementAt(rand); }