Derniere version du jeu
This commit is contained in:
@@ -1,28 +1,92 @@
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Représente une case individuelle sur le plateau du Démineur.
|
||||||
|
* Elle contient son état (minée, révélée) et son marqueur (étoile, interrogation).
|
||||||
|
*/
|
||||||
public class Case implements Serializable {
|
public class Case implements Serializable {
|
||||||
|
|
||||||
|
/** Constante indiquant que la case n'a aucun marqueur. */
|
||||||
|
public static final int MARQUEUR_VIDE = 0;
|
||||||
|
|
||||||
|
/** Constante indiquant que la case est marquée d'une étoile (mine supposée). */
|
||||||
|
public static final int MARQUEUR_ETOILE = 1;
|
||||||
|
|
||||||
|
/** Constante indiquant que la case est marquée d'un point d'interrogation (doute). */
|
||||||
|
public static final int MARQUEUR_INTERROGATION = 2;
|
||||||
|
|
||||||
|
/** Constante définissant le nombre total d'états de marqueurs possibles. */
|
||||||
|
private static final int NOMBRE_ETATS_MARQUEUR = 3;
|
||||||
|
|
||||||
|
/** Indique si la case contient une mine (true) ou non (false). */
|
||||||
private boolean minee;
|
private boolean minee;
|
||||||
|
|
||||||
|
/** Indique si la case a été cliquée et révélée au joueur. */
|
||||||
private boolean revelee;
|
private boolean revelee;
|
||||||
|
|
||||||
|
/** Stocke l'état actuel du marqueur posé par le joueur (clic droit). */
|
||||||
private int marqueur;
|
private int marqueur;
|
||||||
|
|
||||||
|
/** Stocke le nombre de mines présentes dans les 8 cases adjacentes. */
|
||||||
private int nbMinesVoisines;
|
private int nbMinesVoisines;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructeur par défaut d'une Case.
|
||||||
|
* Initialise une case vide, cachée et sans marqueur.
|
||||||
|
*/
|
||||||
public Case() {
|
public Case() {
|
||||||
this.minee = false;
|
this.minee = false;
|
||||||
this.revelee = false;
|
this.revelee = false;
|
||||||
this.marqueur = 0;
|
this.marqueur = MARQUEUR_VIDE;
|
||||||
this.nbMinesVoisines = 0;
|
this.nbMinesVoisines = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si la case contient une mine.
|
||||||
|
* @return true si minée, false sinon.
|
||||||
|
*/
|
||||||
public boolean isMinee() { return this.minee; }
|
public boolean isMinee() { return this.minee; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si la case est révélée.
|
||||||
|
* @return true si révélée, false sinon.
|
||||||
|
*/
|
||||||
public boolean isRevelee() { return this.revelee; }
|
public boolean isRevelee() { return this.revelee; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le marqueur actuel de la case.
|
||||||
|
* @return l'entier correspondant au type de marqueur.
|
||||||
|
*/
|
||||||
public int getMarqueur() { return this.marqueur; }
|
public int getMarqueur() { return this.marqueur; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le nombre de mines autour de cette case.
|
||||||
|
* @return le nombre de mines voisines.
|
||||||
|
*/
|
||||||
public int getNbMinesVoisines() { return this.nbMinesVoisines; }
|
public int getNbMinesVoisines() { return this.nbMinesVoisines; }
|
||||||
|
|
||||||
public void setMinee(boolean m) { this.minee = m; }
|
/**
|
||||||
public void setRevelee(boolean r) { this.revelee = r; }
|
* Définit si la case doit contenir une mine ou non.
|
||||||
public void setNbMinesVoisines(int n) { this.nbMinesVoisines = n; }
|
* @param estMinee true pour placer une mine, false pour l'enlever.
|
||||||
|
*/
|
||||||
|
public void setMinee(boolean estMinee) { this.minee = estMinee; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifie l'état de révélation de la case.
|
||||||
|
* @param estRevelee true pour révéler la case, false pour la cacher.
|
||||||
|
*/
|
||||||
|
public void setRevelee(boolean estRevelee) { this.revelee = estRevelee; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enregistre le nombre de mines calculé autour de cette case.
|
||||||
|
* @param nombre le nombre de mines voisines (de 0 à 8).
|
||||||
|
*/
|
||||||
|
public void setNbMinesVoisines(int nombre) { this.nbMinesVoisines = nombre; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alterne le marqueur de la case dans l'ordre : Vide -> Étoile -> Interrogation -> Vide.
|
||||||
|
*/
|
||||||
public void changerMarqueur() {
|
public void changerMarqueur() {
|
||||||
this.marqueur = (this.marqueur + 1) % 3;
|
this.marqueur = (this.marqueur + 1) % NOMBRE_ETATS_MARQUEUR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+111
-42
@@ -2,61 +2,130 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observateur gérant les interactions de la souris sur la grille de jeu.
|
||||||
|
*/
|
||||||
public class ControleurGrille implements MouseListener {
|
public class ControleurGrille implements MouseListener {
|
||||||
|
|
||||||
|
/** Constante pour le bouton gauche de la souris (Capture 26). */
|
||||||
|
private static final int BOUTON_GAUCHE = MouseEvent.BUTTON1;
|
||||||
|
|
||||||
|
/** Constante pour le bouton droit de la souris (Capture 26). */
|
||||||
|
private static final int BOUTON_DROIT = MouseEvent.BUTTON3;
|
||||||
|
|
||||||
|
/** Message affiché en cas de défaite. */
|
||||||
|
private static final String MESSAGE_DEFAITE = "BOUM ! Vous avez sauté sur une mine !";
|
||||||
|
|
||||||
|
/** Message affiché en cas de victoire. */
|
||||||
|
private static final String MESSAGE_VICTOIRE = "Félicitations ! Vous avez trouvé toutes les mines !";
|
||||||
|
|
||||||
|
/** Référence à la fenêtre affichant la grille. */
|
||||||
private FenetreJeu fenetre;
|
private FenetreJeu fenetre;
|
||||||
|
|
||||||
|
/** Référence au modèle contenant la logique du plateau. */
|
||||||
private Grille grille;
|
private Grille grille;
|
||||||
|
|
||||||
public ControleurGrille(FenetreJeu f, Grille g) {
|
/**
|
||||||
this.fenetre = f;
|
* Constructeur de l'écouteur de la grille.
|
||||||
this.grille = g;
|
* @param fenetreJeu la fenêtre contenant les boutons.
|
||||||
|
* @param grilleJeu la grille contenant les données.
|
||||||
|
*/
|
||||||
|
public ControleurGrille(FenetreJeu fenetreJeu, Grille grilleJeu) {
|
||||||
|
this.fenetre = fenetreJeu;
|
||||||
|
this.grille = grilleJeu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Méthode appelée lors d'un clic sur n'importe quel bouton de la grille.
|
||||||
|
* @param evenement les informations du clic souris.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent evenement) {
|
||||||
if (this.grille.isPartieTerminee()) return;
|
if (this.grille.isPartieTerminee()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < grille.getLignes(); i++) {
|
for (int ligne = 0; ligne < grille.getLignes(); ligne++) {
|
||||||
for (int j = 0; j < grille.getColonnes(); j++) {
|
for (int colonne = 0; colonne < grille.getColonnes(); colonne++) {
|
||||||
|
if (evenement.getSource() == fenetre.getBouton(ligne, colonne)) {
|
||||||
if (e.getSource() == fenetre.getBouton(i, j)) {
|
traiterClicSurCase(evenement, ligne, colonne);
|
||||||
Case caseCliquee = grille.getCase(i, j);
|
|
||||||
|
|
||||||
if (caseCliquee.isRevelee()) return;
|
|
||||||
|
|
||||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
|
||||||
if (caseCliquee.getMarqueur() != 1) {
|
|
||||||
boolean aExplose = grille.revelerCase(i, j);
|
|
||||||
if (aExplose) {
|
|
||||||
grille.setPartieTerminee(true);
|
|
||||||
fenetre.afficherDefaite(i, j);
|
|
||||||
JOptionPane.showMessageDialog(fenetre, "BOUM ! Vous avez sauté sur une mine !");
|
|
||||||
// NOUVEAU : On ne quitte plus de force, on change juste le bouton
|
|
||||||
fenetre.changerBoutonEnRetourMenu();
|
|
||||||
} else {
|
|
||||||
fenetre.rafraichirTouteLaGrille();
|
|
||||||
if (grille.estGagnee()) {
|
|
||||||
grille.setPartieTerminee(true);
|
|
||||||
JOptionPane.showMessageDialog(fenetre, "Félicitations ! Vous avez trouvé toutes les mines !");
|
|
||||||
// NOUVEAU : Idem pour la victoire
|
|
||||||
fenetre.changerBoutonEnRetourMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (e.getButton() == MouseEvent.BUTTON3) {
|
|
||||||
caseCliquee.changerMarqueur();
|
|
||||||
fenetre.rafraichirCase(i, j);
|
|
||||||
fenetre.mettreAJourCompteur();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mousePressed(MouseEvent e) {}
|
/**
|
||||||
public void mouseReleased(MouseEvent e) {}
|
* Identifie le type de clic (gauche ou droit) et appelle la méthode correspondante.
|
||||||
public void mouseEntered(MouseEvent e) {}
|
* @param evenement l'action de la souris.
|
||||||
public void mouseExited(MouseEvent e) {}
|
* @param ligne la ligne de la case cliquée.
|
||||||
|
* @param colonne la colonne de la case cliquée.
|
||||||
|
*/
|
||||||
|
private void traiterClicSurCase(MouseEvent evenement, int ligne, int colonne) {
|
||||||
|
Case caseCliquee = grille.getCase(ligne, colonne);
|
||||||
|
if (caseCliquee.isRevelee()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evenement.getButton() == BOUTON_GAUCHE) {
|
||||||
|
gererClicGauche(caseCliquee, ligne, colonne);
|
||||||
|
} else if (evenement.getButton() == BOUTON_DROIT) {
|
||||||
|
gererClicDroit(caseCliquee, ligne, colonne);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tente de révéler une case et gère l'éventuelle explosion.
|
||||||
|
* @param caseCliquee la case logique ciblée.
|
||||||
|
* @param ligne index de ligne.
|
||||||
|
* @param colonne index de colonne.
|
||||||
|
*/
|
||||||
|
private void gererClicGauche(Case caseCliquee, int ligne, int colonne) {
|
||||||
|
if (caseCliquee.getMarqueur() == Case.MARQUEUR_ETOILE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean aExplose = grille.revelerCase(ligne, colonne);
|
||||||
|
if (aExplose) {
|
||||||
|
grille.setPartieTerminee(true);
|
||||||
|
fenetre.afficherDefaite(ligne, colonne);
|
||||||
|
JOptionPane.showMessageDialog(fenetre, MESSAGE_DEFAITE);
|
||||||
|
fenetre.changerBoutonEnRetourMenu();
|
||||||
|
} else {
|
||||||
|
fenetre.rafraichirTouteLaGrille();
|
||||||
|
verifierVictoire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teste si toutes les cases saines ont été révélées et annonce la victoire.
|
||||||
|
*/
|
||||||
|
private void verifierVictoire() {
|
||||||
|
if (grille.estGagnee()) {
|
||||||
|
grille.setPartieTerminee(true);
|
||||||
|
JOptionPane.showMessageDialog(fenetre, MESSAGE_VICTOIRE);
|
||||||
|
fenetre.changerBoutonEnRetourMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pose, change ou retire un marqueur sur la case ciblée.
|
||||||
|
* @param caseCliquee la case logique ciblée.
|
||||||
|
* @param ligne index de ligne.
|
||||||
|
* @param colonne index de colonne.
|
||||||
|
*/
|
||||||
|
private void gererClicDroit(Case caseCliquee, int ligne, int colonne) {
|
||||||
|
caseCliquee.changerMarqueur();
|
||||||
|
fenetre.rafraichirCase(ligne, colonne);
|
||||||
|
fenetre.mettreAJourCompteur();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param evenement l'événement souris. */
|
||||||
|
public void mousePressed(MouseEvent evenement) {}
|
||||||
|
/** @param evenement l'événement souris. */
|
||||||
|
public void mouseReleased(MouseEvent evenement) {}
|
||||||
|
/** @param evenement l'événement souris. */
|
||||||
|
public void mouseEntered(MouseEvent evenement) {}
|
||||||
|
/** @param evenement l'événement souris. */
|
||||||
|
public void mouseExited(MouseEvent evenement) {}
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-8
@@ -1,26 +1,70 @@
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observateur gérant le bouton situé en bas de la fenêtre de jeu.
|
||||||
|
*/
|
||||||
public class ControleurJeu implements ActionListener {
|
public class ControleurJeu implements ActionListener {
|
||||||
|
|
||||||
|
/** Constante définissant le nom du fichier de sauvegarde. */
|
||||||
|
private static final String NOM_FICHIER_SAUVEGARDE = "sauvegarde.dat";
|
||||||
|
|
||||||
|
/** Constante définissant le code de sortie normal de l'application. */
|
||||||
|
private static final int CODE_SORTIE_NORMALE = 0;
|
||||||
|
|
||||||
|
/** Référence à la fenêtre de jeu actuellement ouverte. */
|
||||||
private FenetreJeu fenetre;
|
private FenetreJeu fenetre;
|
||||||
|
|
||||||
public ControleurJeu(FenetreJeu f) {
|
/**
|
||||||
this.fenetre = f;
|
* Constructeur de l'écouteur du bouton de jeu.
|
||||||
|
* @param fenetreJeu la fenêtre à observer.
|
||||||
|
*/
|
||||||
|
public ControleurJeu(FenetreJeu fenetreJeu) {
|
||||||
|
this.fenetre = fenetreJeu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agit selon l'état de la partie : retourne au menu ou sauvegarde la progression.
|
||||||
|
* @param evenement le clic sur le bouton.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent evenement) {
|
||||||
// Si la partie est finie, le bouton sert à retourner au menu
|
|
||||||
if (fenetre.getGrille().isPartieTerminee()) {
|
if (fenetre.getGrille().isPartieTerminee()) {
|
||||||
|
retournerAuMenu();
|
||||||
|
} else {
|
||||||
|
sauvegarderEtQuitter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ferme la partie et rouvre une nouvelle instance du menu principal.
|
||||||
|
*/
|
||||||
|
private void retournerAuMenu() {
|
||||||
this.fenetre.dispose();
|
this.fenetre.dispose();
|
||||||
MenuPrincipal nouveauMenu = new MenuPrincipal();
|
MenuPrincipal nouveauMenu = new MenuPrincipal();
|
||||||
nouveauMenu.setVisible(true);
|
nouveauMenu.setVisible(true);
|
||||||
}
|
}
|
||||||
// Sinon (partie en cours), c'est bien le bouton "Sauver et Quitter"
|
|
||||||
else {
|
/**
|
||||||
System.out.println("Sauvegarde de la partie en cours... (Bientôt implémenté)");
|
* Grave la grille en mémoire dans un fichier et ferme l'application.
|
||||||
System.exit(0);
|
*/
|
||||||
|
private void sauvegarderEtQuitter() {
|
||||||
|
try {
|
||||||
|
FileOutputStream fluxSortie = new FileOutputStream(NOM_FICHIER_SAUVEGARDE);
|
||||||
|
ObjectOutputStream graveurObjet = new ObjectOutputStream(fluxSortie);
|
||||||
|
|
||||||
|
graveurObjet.writeObject(fenetre.getGrille());
|
||||||
|
|
||||||
|
graveurObjet.close();
|
||||||
|
fluxSortie.close();
|
||||||
|
System.exit(CODE_SORTIE_NORMALE);
|
||||||
|
|
||||||
|
} catch (IOException exception) {
|
||||||
|
JOptionPane.showMessageDialog(fenetre, "Erreur lors de la sauvegarde !");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+119
-34
@@ -1,50 +1,135 @@
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observateur gérant les interactions avec les boutons du menu principal.
|
||||||
|
*/
|
||||||
public class ControleurMenu implements ActionListener {
|
public class ControleurMenu implements ActionListener {
|
||||||
|
|
||||||
|
/** Constante définissant la valeur de retour en cas d'annulation ou d'erreur de saisie. */
|
||||||
|
private static final int ERREUR_SAISIE = -1;
|
||||||
|
|
||||||
|
/** Constante définissant le nombre minimum de lignes ou colonnes. */
|
||||||
|
private static final int DIMENSION_MIN = 4;
|
||||||
|
|
||||||
|
/** Constante définissant le nombre maximum de lignes ou colonnes. */
|
||||||
|
private static final int DIMENSION_MAX = 30;
|
||||||
|
|
||||||
|
/** Référence vers la fenêtre du menu. */
|
||||||
private MenuPrincipal menu;
|
private MenuPrincipal menu;
|
||||||
|
|
||||||
|
/**
|
||||||
public ControleurMenu(MenuPrincipal m) {
|
* Constructeur de l'écouteur du menu.
|
||||||
this.menu = m;
|
* @param menuPrincipal la fenêtre du menu principal.
|
||||||
|
*/
|
||||||
|
public ControleurMenu(MenuPrincipal menuPrincipal) {
|
||||||
|
this.menu = menuPrincipal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Déclenchée lors d'un clic sur l'un des boutons du menu.
|
||||||
|
* @param evenement l'action générée par le clic.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent evenement) {
|
public void actionPerformed(ActionEvent evenement) {
|
||||||
if (evenement.getSource() == this.menu.getBtnNouvellePartie()) {
|
if (evenement.getSource() == this.menu.getBoutonNouvellePartie()) {
|
||||||
try {
|
lancerNouvellePartie();
|
||||||
String strL = JOptionPane.showInputDialog(menu, "Nombre de lignes (entre 4 et 30) :");
|
} else if (evenement.getSource() == this.menu.getBoutonReprendre()) {
|
||||||
if (strL == null) return;
|
chargerPartieSauvegardee();
|
||||||
int l = Integer.parseInt(strL);
|
} else if (evenement.getSource() == this.menu.getBoutonQuitter()) {
|
||||||
|
|
||||||
String strC = JOptionPane.showInputDialog(menu, "Nombre de colonnes (entre 4 et 30) :");
|
|
||||||
if (strC == null) return;
|
|
||||||
int c = Integer.parseInt(strC);
|
|
||||||
|
|
||||||
String strM = JOptionPane.showInputDialog(menu, "Nombre total de mines :");
|
|
||||||
if (strM == null) return;
|
|
||||||
int m = Integer.parseInt(strM);
|
|
||||||
|
|
||||||
if (l < 4 || l > 30 || c < 4 || c > 30 || m >= (l * c)) {
|
|
||||||
JOptionPane.showMessageDialog(menu, "Valeurs incorrectes ! Les dimensions doivent être entre 4 et 30, et les mines inférieures au nombre de cases.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Grille nouvelleGrille = new Grille(l, c, m);
|
|
||||||
FenetreJeu fenetre = new FenetreJeu(nouvelleGrille);
|
|
||||||
fenetre.setVisible(true);
|
|
||||||
this.menu.dispose();
|
|
||||||
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
JOptionPane.showMessageDialog(menu, "Erreur : Vous devez entrer des nombres entiers !");
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (evenement.getSource() == this.menu.getBtnReprendre()) {
|
|
||||||
System.out.println("Chargement de la sauvegarde...");
|
|
||||||
} else if (evenement.getSource() == this.menu.getBtnQuitter()) {
|
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demande les dimensions et génère une nouvelle partie si les saisies sont valides.
|
||||||
|
*/
|
||||||
|
private void lancerNouvellePartie() {
|
||||||
|
try {
|
||||||
|
int nbLignes = demanderValeur("Nombre de lignes (entre 4 et 30) :");
|
||||||
|
if (nbLignes == ERREUR_SAISIE) return;
|
||||||
|
|
||||||
|
int nbColonnes = demanderValeur("Nombre de colonnes (entre 4 et 30) :");
|
||||||
|
if (nbColonnes == ERREUR_SAISIE) return;
|
||||||
|
|
||||||
|
int nbMines = demanderValeur("Nombre total de mines :");
|
||||||
|
if (nbMines == ERREUR_SAISIE) return;
|
||||||
|
|
||||||
|
if (!sontDimensionsValides(nbLignes, nbColonnes, nbMines)) {
|
||||||
|
JOptionPane.showMessageDialog(menu, "Valeurs incorrectes !");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
demarrerJeu(nbLignes, nbColonnes, nbMines);
|
||||||
|
|
||||||
|
} catch (NumberFormatException exception) {
|
||||||
|
JOptionPane.showMessageDialog(menu, "Erreur : Vous devez entrer des nombres entiers !");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si les dimensions entrées par l'utilisateur respectent les limites.
|
||||||
|
* @param lignes le nombre de lignes choisi.
|
||||||
|
* @param colonnes le nombre de colonnes choisi.
|
||||||
|
* @param mines le nombre de mines choisi.
|
||||||
|
* @return true si tout est correct, false sinon.
|
||||||
|
*/
|
||||||
|
private boolean sontDimensionsValides(int lignes, int colonnes, int mines) {
|
||||||
|
boolean lignesInvalides = lignes < DIMENSION_MIN || lignes > DIMENSION_MAX;
|
||||||
|
boolean colonnesInvalides = colonnes < DIMENSION_MIN || colonnes > DIMENSION_MAX;
|
||||||
|
boolean minesInvalides = mines >= (lignes * colonnes) || mines <= 0;
|
||||||
|
|
||||||
|
return !lignesInvalides && !colonnesInvalides && !minesInvalides;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise la fenêtre de jeu avec les paramètres validés et ferme le menu.
|
||||||
|
* @param lignes le nombre de lignes.
|
||||||
|
* @param colonnes le nombre de colonnes.
|
||||||
|
* @param mines le nombre de mines.
|
||||||
|
*/
|
||||||
|
private void demarrerJeu(int lignes, int colonnes, int mines) {
|
||||||
|
Grille nouvelleGrille = new Grille(lignes, colonnes, mines);
|
||||||
|
FenetreJeu fenetre = new FenetreJeu(nouvelleGrille);
|
||||||
|
fenetre.setVisible(true);
|
||||||
|
this.menu.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ouvre une boîte de dialogue pour demander un entier à l'utilisateur.
|
||||||
|
* @param message le texte à afficher.
|
||||||
|
* @return le nombre entier saisi, ou ERREUR_SAISIE en cas d'annulation.
|
||||||
|
*/
|
||||||
|
private int demanderValeur(String message) {
|
||||||
|
String saisie = JOptionPane.showInputDialog(menu, message);
|
||||||
|
if (saisie == null || saisie.trim().isEmpty()) return ERREUR_SAISIE;
|
||||||
|
return Integer.parseInt(saisie);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tente de lire le fichier de sauvegarde et restaure la partie si possible.
|
||||||
|
*/
|
||||||
|
private void chargerPartieSauvegardee() {
|
||||||
|
try {
|
||||||
|
FileInputStream fluxEntree = new FileInputStream("sauvegarde.dat");
|
||||||
|
ObjectInputStream lecteurObjet = new ObjectInputStream(fluxEntree);
|
||||||
|
|
||||||
|
Grille grilleChargee = (Grille) lecteurObjet.readObject();
|
||||||
|
|
||||||
|
lecteurObjet.close();
|
||||||
|
fluxEntree.close();
|
||||||
|
|
||||||
|
FenetreJeu fenetre = new FenetreJeu(grilleChargee);
|
||||||
|
fenetre.rafraichirTouteLaGrille();
|
||||||
|
fenetre.setVisible(true);
|
||||||
|
this.menu.dispose();
|
||||||
|
|
||||||
|
} catch (IOException | ClassNotFoundException exception) {
|
||||||
|
JOptionPane.showMessageDialog(menu, "Aucune sauvegarde trouvée ou fichier corrompu !");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+169
-59
@@ -1,112 +1,222 @@
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fenêtre affichant la partie en cours.
|
||||||
|
* Gère l'affichage de la grille, du compteur de mines et du bouton de sauvegarde.
|
||||||
|
*/
|
||||||
public class FenetreJeu extends JFrame {
|
public class FenetreJeu extends JFrame {
|
||||||
|
|
||||||
|
/** Position X initiale de la fenêtre de jeu sur l'écran. */
|
||||||
|
private static final int POSITION_X = 100;
|
||||||
|
|
||||||
|
/** Position Y initiale de la fenêtre de jeu sur l'écran. */
|
||||||
|
private static final int POSITION_Y = 100;
|
||||||
|
|
||||||
|
/** Estimation de la taille en pixels d'une case pour la largeur de la fenêtre. */
|
||||||
|
private static final int LARGEUR_CASE_PIXELS = 50;
|
||||||
|
|
||||||
|
/** Estimation de la taille en pixels d'une case pour la hauteur de la fenêtre. */
|
||||||
|
private static final int HAUTEUR_CASE_PIXELS = 50;
|
||||||
|
|
||||||
|
/** Espace supplémentaire en hauteur pour afficher l'en-tête et le pied de page. */
|
||||||
|
private static final int MARGE_HAUTEUR_PIXELS = 100;
|
||||||
|
|
||||||
|
/** Référence vers le modèle logique du jeu. */
|
||||||
private Grille grille;
|
private Grille grille;
|
||||||
|
|
||||||
|
/** Tableau à deux dimensions stockant les composants graphiques des cases. */
|
||||||
private JButton[][] boutons;
|
private JButton[][] boutons;
|
||||||
|
|
||||||
|
/** Étiquette textuelle affichant le nombre de mines restantes à trouver. */
|
||||||
private JLabel labelCompteur;
|
private JLabel labelCompteur;
|
||||||
private JButton btnActionBas; // NOUVEAU : On garde le bouton en mémoire
|
|
||||||
|
|
||||||
public FenetreJeu(Grille g) {
|
/** Bouton dynamique en bas de la fenêtre (Sauvegarder ou Retour Menu). */
|
||||||
|
private JButton boutonActionBas;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructeur de la fenêtre de jeu.
|
||||||
|
* @param grilleJeu la grille logique initialisée à afficher.
|
||||||
|
*/
|
||||||
|
public FenetreJeu(Grille grilleJeu) {
|
||||||
super("Démineur - Partie en cours");
|
super("Démineur - Partie en cours");
|
||||||
this.grille = g;
|
this.grille = grilleJeu;
|
||||||
this.boutons = new JButton[g.getLignes()][g.getColonnes()];
|
this.boutons = new JButton[grilleJeu.getLignes()][grilleJeu.getColonnes()];
|
||||||
|
|
||||||
this.setSize(600, 600);
|
|
||||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
this.setLayout(new BorderLayout());
|
|
||||||
|
|
||||||
this.labelCompteur = new JLabel("Mines restantes : " + g.getMinesRestantes());
|
int largeurFenetre = grilleJeu.getColonnes() * LARGEUR_CASE_PIXELS;
|
||||||
this.labelCompteur.setHorizontalAlignment(SwingConstants.CENTER);
|
int hauteurFenetre = (grilleJeu.getLignes() * HAUTEUR_CASE_PIXELS) + MARGE_HAUTEUR_PIXELS;
|
||||||
this.labelCompteur.setFont(new Font("Arial", Font.BOLD, 16));
|
|
||||||
|
this.setSize(largeurFenetre, hauteurFenetre);
|
||||||
|
this.setLocation(POSITION_X, POSITION_Y);
|
||||||
|
|
||||||
|
initialiserEntete();
|
||||||
|
initialiserPanneauGrille();
|
||||||
|
initialiserBoutonBas();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prépare et ajoute le compteur de mines en haut de la fenêtre.
|
||||||
|
*/
|
||||||
|
private void initialiserEntete() {
|
||||||
|
this.labelCompteur = new JLabel("Mines restantes : " + this.grille.getMinesRestantes());
|
||||||
|
this.labelCompteur.setHorizontalAlignment(JLabel.CENTER);
|
||||||
this.add(this.labelCompteur, BorderLayout.NORTH);
|
this.add(this.labelCompteur, BorderLayout.NORTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prépare et ajoute la zone centrale contenant tous les boutons cliquables.
|
||||||
|
*/
|
||||||
|
private void initialiserPanneauGrille() {
|
||||||
JPanel panneauGrille = new JPanel();
|
JPanel panneauGrille = new JPanel();
|
||||||
panneauGrille.setLayout(new GridLayout(g.getLignes(), g.getColonnes()));
|
panneauGrille.setLayout(new GridLayout(this.grille.getLignes(), this.grille.getColonnes()));
|
||||||
|
|
||||||
ControleurGrille controleurSouris = new ControleurGrille(this, this.grille);
|
ControleurGrille controleurSouris = new ControleurGrille(this, this.grille);
|
||||||
|
|
||||||
for (int i = 0; i < g.getLignes(); i++) {
|
for (int ligne = 0; ligne < this.grille.getLignes(); ligne++) {
|
||||||
for (int j = 0; j < g.getColonnes(); j++) {
|
for (int colonne = 0; colonne < this.grille.getColonnes(); colonne++) {
|
||||||
JButton boutonCase = new JButton("");
|
JButton boutonCase = new JButton("");
|
||||||
this.boutons[i][j] = boutonCase;
|
|
||||||
|
this.boutons[ligne][colonne] = boutonCase;
|
||||||
panneauGrille.add(boutonCase);
|
panneauGrille.add(boutonCase);
|
||||||
boutonCase.addMouseListener(controleurSouris);
|
boutonCase.addMouseListener(controleurSouris);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.add(panneauGrille, BorderLayout.CENTER);
|
this.add(panneauGrille, BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
// --- MODIFICATION DU BOUTON DU BAS ---
|
/**
|
||||||
this.btnActionBas = new JButton("Sauver et Quitter");
|
* Prépare et ajoute le bouton de contrôle général en bas de la fenêtre.
|
||||||
this.add(this.btnActionBas, BorderLayout.SOUTH);
|
*/
|
||||||
|
private void initialiserBoutonBas() {
|
||||||
|
this.boutonActionBas = new JButton("Sauver et Quitter");
|
||||||
|
this.add(this.boutonActionBas, BorderLayout.SOUTH);
|
||||||
ControleurJeu controleurQuitter = new ControleurJeu(this);
|
ControleurJeu controleurQuitter = new ControleurJeu(this);
|
||||||
this.btnActionBas.addActionListener(controleurQuitter);
|
this.boutonActionBas.addActionListener(controleurQuitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- NOUVEAUX OUTILS ---
|
/**
|
||||||
public JButton getBouton(int l, int c) { return this.boutons[l][c]; }
|
* Récupère un bouton graphique spécifique de la grille.
|
||||||
public Grille getGrille() { return this.grille; } // Permet au contrôleur de lire la grille
|
* @param indexLigne la ligne du bouton.
|
||||||
|
* @param indexColonne la colonne du bouton.
|
||||||
|
* @return le bouton correspondant.
|
||||||
|
*/
|
||||||
|
public JButton getBouton(int indexLigne, int indexColonne) { return this.boutons[indexLigne][indexColonne]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accède à l'instance de la grille logique.
|
||||||
|
* @return la grille du jeu.
|
||||||
|
*/
|
||||||
|
public Grille getGrille() { return this.grille; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforme le bouton de sauvegarde en bouton de retour au menu.
|
||||||
|
*/
|
||||||
public void changerBoutonEnRetourMenu() {
|
public void changerBoutonEnRetourMenu() {
|
||||||
this.btnActionBas.setText("Retour au Menu");
|
this.boutonActionBas.setText("Retour au Menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interroge la grille et rafraîchit le texte du compteur de mines.
|
||||||
|
*/
|
||||||
public void mettreAJourCompteur() {
|
public void mettreAJourCompteur() {
|
||||||
this.labelCompteur.setText("Mines restantes : " + this.grille.getMinesRestantes());
|
this.labelCompteur.setText("Mines restantes : " + this.grille.getMinesRestantes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void rafraichirCase(int l, int c) {
|
/**
|
||||||
Case caseLogique = this.grille.getCase(l, c);
|
* Met à jour l'apparence visuelle d'un bouton selon son état logique.
|
||||||
JButton btn = this.boutons[l][c];
|
* @param ligne la ligne du bouton à rafraîchir.
|
||||||
|
* @param colonne la colonne du bouton à rafraîchir.
|
||||||
|
*/
|
||||||
|
public void rafraichirCase(int ligne, int colonne) {
|
||||||
|
Case caseLogique = this.grille.getCase(ligne, colonne);
|
||||||
|
JButton bouton = this.boutons[ligne][colonne];
|
||||||
|
|
||||||
if (caseLogique.isRevelee()) {
|
if (caseLogique.isRevelee()) {
|
||||||
btn.setEnabled(false);
|
appliquerStyleCaseRevelee(caseLogique, bouton);
|
||||||
|
} else {
|
||||||
|
appliquerStyleCaseCachee(caseLogique, bouton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique les couleurs et le texte d'une case qui vient d'être révélée.
|
||||||
|
* @param caseLogique la donnée de la case.
|
||||||
|
* @param bouton le composant graphique associé.
|
||||||
|
*/
|
||||||
|
private void appliquerStyleCaseRevelee(Case caseLogique, JButton bouton) {
|
||||||
if (caseLogique.isMinee()) {
|
if (caseLogique.isMinee()) {
|
||||||
btn.setText("M");
|
bouton.setText("M");
|
||||||
btn.setBackground(Color.RED);
|
bouton.setBackground(Color.RED);
|
||||||
} else if (caseLogique.getNbMinesVoisines() > 0) {
|
} else if (caseLogique.getNbMinesVoisines() > 0) {
|
||||||
btn.setText("" + caseLogique.getNbMinesVoisines());
|
bouton.setText("" + caseLogique.getNbMinesVoisines());
|
||||||
|
bouton.setBackground(Color.WHITE);
|
||||||
} else {
|
} else {
|
||||||
btn.setText("");
|
bouton.setText("");
|
||||||
}
|
bouton.setBackground(Color.LIGHT_GRAY);
|
||||||
} else {
|
|
||||||
if (caseLogique.getMarqueur() == 1) {
|
|
||||||
btn.setText("★");
|
|
||||||
} else if (caseLogique.getMarqueur() == 2) {
|
|
||||||
btn.setText("?");
|
|
||||||
} else {
|
|
||||||
btn.setText("");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applique les symboles d'une case qui est encore masquée (marqueurs).
|
||||||
|
* @param caseLogique la donnée de la case.
|
||||||
|
* @param bouton le composant graphique associé.
|
||||||
|
*/
|
||||||
|
private void appliquerStyleCaseCachee(Case caseLogique, JButton bouton) {
|
||||||
|
if (caseLogique.getMarqueur() == Case.MARQUEUR_ETOILE) {
|
||||||
|
bouton.setText("*");
|
||||||
|
} else if (caseLogique.getMarqueur() == Case.MARQUEUR_INTERROGATION) {
|
||||||
|
bouton.setText("?");
|
||||||
|
} else {
|
||||||
|
bouton.setText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force la mise à jour visuelle de toutes les cases de la grille.
|
||||||
|
*/
|
||||||
public void rafraichirTouteLaGrille() {
|
public void rafraichirTouteLaGrille() {
|
||||||
for (int i = 0; i < grille.getLignes(); i++) {
|
for (int ligne = 0; ligne < grille.getLignes(); ligne++) {
|
||||||
for (int j = 0; j < grille.getColonnes(); j++) {
|
for (int colonne = 0; colonne < grille.getColonnes(); colonne++) {
|
||||||
rafraichirCase(i, j);
|
rafraichirCase(ligne, colonne);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void afficherDefaite(int lExplose, int cExplose) {
|
/**
|
||||||
for (int i = 0; i < grille.getLignes(); i++) {
|
* Affiche l'état de défaite en révélant les erreurs et les mines manquantes.
|
||||||
for (int j = 0; j < grille.getColonnes(); j++) {
|
* @param ligneExplosion la ligne de la mine déclenchée.
|
||||||
Case c = grille.getCase(i, j);
|
* @param colonneExplosion la colonne de la mine déclenchée.
|
||||||
JButton btn = boutons[i][j];
|
*/
|
||||||
btn.setEnabled(false);
|
public void afficherDefaite(int ligneExplosion, int colonneExplosion) {
|
||||||
|
for (int ligne = 0; ligne < grille.getLignes(); ligne++) {
|
||||||
|
for (int colonne = 0; colonne < grille.getColonnes(); colonne++) {
|
||||||
|
Case caseActuelle = grille.getCase(ligne, colonne);
|
||||||
|
JButton bouton = boutons[ligne][colonne];
|
||||||
|
appliquerStyleDefaite(caseActuelle, bouton, ligne, colonne, ligneExplosion, colonneExplosion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (i == lExplose && j == cExplose) {
|
/**
|
||||||
btn.setText("BOUM");
|
* Détermine la couleur spécifique d'une case en fin de partie (erreur, oubli, ou explosion).
|
||||||
btn.setBackground(Color.RED);
|
* @param caseActuelle la donnée de la case.
|
||||||
} else if (c.isMinee() && c.getMarqueur() != 1) {
|
* @param bouton le composant graphique.
|
||||||
btn.setText("M");
|
* @param ligne la ligne en cours de parcours.
|
||||||
btn.setBackground(Color.ORANGE);
|
* @param colonne la colonne en cours de parcours.
|
||||||
} else if (!c.isMinee() && c.getMarqueur() == 1) {
|
* @param ligneExplosion la ligne de la mine fatale.
|
||||||
btn.setText("X");
|
* @param colonneExplosion la colonne de la mine fatale.
|
||||||
btn.setBackground(Color.YELLOW);
|
*/
|
||||||
}
|
private void appliquerStyleDefaite(Case caseActuelle, JButton bouton, int ligne, int colonne, int ligneExplosion, int colonneExplosion) {
|
||||||
}
|
if (ligne == ligneExplosion && colonne == colonneExplosion) {
|
||||||
|
bouton.setText("BOUM");
|
||||||
|
bouton.setBackground(Color.RED);
|
||||||
|
} else if (caseActuelle.isMinee() && caseActuelle.getMarqueur() != Case.MARQUEUR_ETOILE) {
|
||||||
|
bouton.setText("M");
|
||||||
|
bouton.setBackground(Color.ORANGE);
|
||||||
|
} else if (!caseActuelle.isMinee() && caseActuelle.getMarqueur() == Case.MARQUEUR_ETOILE) {
|
||||||
|
bouton.setText("X");
|
||||||
|
bouton.setBackground(Color.YELLOW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+107
-40
@@ -1,24 +1,44 @@
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gère la logique principale du Démineur (le plateau de jeu).
|
||||||
|
* Initialise les cases, place les mines et contient l'algorithme de révélation.
|
||||||
|
*/
|
||||||
public class Grille implements Serializable {
|
public class Grille implements Serializable {
|
||||||
|
|
||||||
|
/** Tableau à deux dimensions contenant toutes les cases du jeu. */
|
||||||
private Case[][] plateau;
|
private Case[][] plateau;
|
||||||
|
|
||||||
|
/** Nombre total de lignes de la grille. */
|
||||||
private int lignes;
|
private int lignes;
|
||||||
|
|
||||||
|
/** Nombre total de colonnes de la grille. */
|
||||||
private int colonnes;
|
private int colonnes;
|
||||||
|
|
||||||
|
/** Nombre total de mines cachées dans la grille. */
|
||||||
private int mines;
|
private int mines;
|
||||||
|
|
||||||
|
/** Indique si la partie est bloquée (gagnée ou perdue). */
|
||||||
private boolean partieTerminee;
|
private boolean partieTerminee;
|
||||||
|
|
||||||
public Grille(int l, int c, int m) {
|
/**
|
||||||
this.lignes = l;
|
* Constructeur de la grille.
|
||||||
this.colonnes = c;
|
* Crée le plateau, place les mines aléatoirement et calcule les voisins.
|
||||||
this.mines = m;
|
* @param nbLignes le nombre de lignes souhaité.
|
||||||
|
* @param nbColonnes le nombre de colonnes souhaité.
|
||||||
|
* @param nbMines le nombre de mines à placer.
|
||||||
|
*/
|
||||||
|
public Grille(int nbLignes, int nbColonnes, int nbMines) {
|
||||||
|
this.lignes = nbLignes;
|
||||||
|
this.colonnes = nbColonnes;
|
||||||
|
this.mines = nbMines;
|
||||||
this.partieTerminee = false;
|
this.partieTerminee = false;
|
||||||
|
|
||||||
this.plateau = new Case[this.lignes][this.colonnes];
|
this.plateau = new Case[this.lignes][this.colonnes];
|
||||||
|
|
||||||
for (int i = 0; i < this.lignes; i++) {
|
for (int ligne = 0; ligne < this.lignes; ligne++) {
|
||||||
for (int j = 0; j < this.colonnes; j++) {
|
for (int colonne = 0; colonne < this.colonnes; colonne++) {
|
||||||
this.plateau[i][j] = new Case();
|
this.plateau[ligne][colonne] = new Case();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,18 +46,47 @@ public class Grille implements Serializable {
|
|||||||
calculerVoisins();
|
calculerVoisins();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le nombre de lignes de la grille.
|
||||||
|
* @return le nombre de lignes.
|
||||||
|
*/
|
||||||
public int getLignes() { return this.lignes; }
|
public int getLignes() { return this.lignes; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Récupère le nombre de colonnes de la grille.
|
||||||
|
* @return le nombre de colonnes.
|
||||||
|
*/
|
||||||
public int getColonnes() { return this.colonnes; }
|
public int getColonnes() { return this.colonnes; }
|
||||||
public Case getCase(int l, int c) { return this.plateau[l][c]; }
|
|
||||||
|
/**
|
||||||
|
* Accède à une case spécifique du plateau.
|
||||||
|
* @param indexLigne la ligne de la case cible.
|
||||||
|
* @param indexColonne la colonne de la case cible.
|
||||||
|
* @return l'objet Case correspondant.
|
||||||
|
*/
|
||||||
|
public Case getCase(int indexLigne, int indexColonne) { return this.plateau[indexLigne][indexColonne]; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si la partie est verrouillée en état de fin.
|
||||||
|
* @return true si terminée, false si en cours.
|
||||||
|
*/
|
||||||
public boolean isPartieTerminee() { return this.partieTerminee; }
|
public boolean isPartieTerminee() { return this.partieTerminee; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifie l'état de fin de partie (verrouillage de la grille).
|
||||||
|
* @param terminee true pour bloquer le jeu, false pour le débloquer.
|
||||||
|
*/
|
||||||
public void setPartieTerminee(boolean terminee) { this.partieTerminee = terminee; }
|
public void setPartieTerminee(boolean terminee) { this.partieTerminee = terminee; }
|
||||||
|
|
||||||
// --- NOUVELLE MÉTHODE : Calcul des mines restantes ---
|
/**
|
||||||
|
* Calcule le nombre de mines restantes à trouver selon les drapeaux posés.
|
||||||
|
* @return la soustraction des mines totales par le nombre d'étoiles posées.
|
||||||
|
*/
|
||||||
public int getMinesRestantes() {
|
public int getMinesRestantes() {
|
||||||
int drapeaux = 0;
|
int drapeaux = 0;
|
||||||
for (int i = 0; i < this.lignes; i++) {
|
for (int ligne = 0; ligne < this.lignes; ligne++) {
|
||||||
for (int j = 0; j < this.colonnes; j++) {
|
for (int colonne = 0; colonne < this.colonnes; colonne++) {
|
||||||
if (this.plateau[i][j].getMarqueur() == 1) { // 1 = étoile
|
if (this.plateau[ligne][colonne].getMarqueur() == Case.MARQUEUR_ETOILE) {
|
||||||
drapeaux++;
|
drapeaux++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,54 +94,68 @@ public class Grille implements Serializable {
|
|||||||
return this.mines - drapeaux;
|
return this.mines - drapeaux;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Place aléatoirement le nombre défini de mines sur le plateau vide.
|
||||||
|
*/
|
||||||
private void placerMines() {
|
private void placerMines() {
|
||||||
Random rand = new Random();
|
|
||||||
int minesPlacees = 0;
|
int minesPlacees = 0;
|
||||||
while (minesPlacees < this.mines) {
|
while (minesPlacees < this.mines) {
|
||||||
int l = rand.nextInt(this.lignes);
|
int ligneAleatoire = (int) (Math.random() * this.lignes);
|
||||||
int c = rand.nextInt(this.colonnes);
|
int colonneAleatoire = (int) (Math.random() * this.colonnes);
|
||||||
if (!this.plateau[l][c].isMinee()) {
|
|
||||||
this.plateau[l][c].setMinee(true);
|
if (!this.plateau[ligneAleatoire][colonneAleatoire].isMinee()) {
|
||||||
|
this.plateau[ligneAleatoire][colonneAleatoire].setMinee(true);
|
||||||
minesPlacees++;
|
minesPlacees++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parcourt la grille pour calculer le nombre de mines autour de chaque case non-minée.
|
||||||
|
*/
|
||||||
private void calculerVoisins() {
|
private void calculerVoisins() {
|
||||||
for (int i = 0; i < this.lignes; i++) {
|
for (int ligne = 0; ligne < this.lignes; ligne++) {
|
||||||
for (int j = 0; j < this.colonnes; j++) {
|
for (int colonne = 0; colonne < this.colonnes; colonne++) {
|
||||||
if (!this.plateau[i][j].isMinee()) {
|
if (!this.plateau[ligne][colonne].isMinee()) {
|
||||||
int compteMines = 0;
|
int compteMines = 0;
|
||||||
for (int di = -1; di <= 1; di++) {
|
for (int decalageLigne = -1; decalageLigne <= 1; decalageLigne++) {
|
||||||
for (int dj = -1; dj <= 1; dj++) {
|
for (int decalageColonne = -1; decalageColonne <= 1; decalageColonne++) {
|
||||||
int voisinI = i + di;
|
int voisinLigne = ligne + decalageLigne;
|
||||||
int voisinJ = j + dj;
|
int voisinColonne = colonne + decalageColonne;
|
||||||
if (voisinI >= 0 && voisinI < this.lignes && voisinJ >= 0 && voisinJ < this.colonnes) {
|
if (voisinLigne >= 0 && voisinLigne < this.lignes && voisinColonne >= 0 && voisinColonne < this.colonnes) {
|
||||||
if (this.plateau[voisinI][voisinJ].isMinee()) {
|
if (this.plateau[voisinLigne][voisinColonne].isMinee()) {
|
||||||
compteMines++;
|
compteMines++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.plateau[i][j].setNbMinesVoisines(compteMines);
|
this.plateau[ligne][colonne].setNbMinesVoisines(compteMines);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean revelerCase(int l, int c) {
|
/**
|
||||||
Case caseCliquee = this.plateau[l][c];
|
* Révèle une case et déclenche une réaction en chaîne récursive si elle vaut 0.
|
||||||
if (caseCliquee.isRevelee() || caseCliquee.getMarqueur() == 1) return false;
|
* @param cibleLigne la ligne de la case à révéler.
|
||||||
|
* @param cibleColonne la colonne de la case à révéler.
|
||||||
|
* @return true si une mine a explosé, false sinon.
|
||||||
|
*/
|
||||||
|
public boolean revelerCase(int cibleLigne, int cibleColonne) {
|
||||||
|
Case caseCliquee = this.plateau[cibleLigne][cibleColonne];
|
||||||
|
|
||||||
|
if (caseCliquee.isRevelee() || caseCliquee.getMarqueur() == Case.MARQUEUR_ETOILE) return false;
|
||||||
|
|
||||||
caseCliquee.setRevelee(true);
|
caseCliquee.setRevelee(true);
|
||||||
if (caseCliquee.isMinee()) return true;
|
if (caseCliquee.isMinee()) return true;
|
||||||
|
|
||||||
if (caseCliquee.getNbMinesVoisines() == 0) {
|
if (caseCliquee.getNbMinesVoisines() == 0) {
|
||||||
for (int di = -1; di <= 1; di++) {
|
for (int decalageLigne = -1; decalageLigne <= 1; decalageLigne++) {
|
||||||
for (int dj = -1; dj <= 1; dj++) {
|
for (int decalageColonne = -1; decalageColonne <= 1; decalageColonne++) {
|
||||||
int voisinL = l + di;
|
int voisinLigne = cibleLigne + decalageLigne;
|
||||||
int voisinC = c + dj;
|
int voisinColonne = cibleColonne + decalageColonne;
|
||||||
if (voisinL >= 0 && voisinL < this.lignes && voisinC >= 0 && voisinC < this.colonnes) {
|
if (voisinLigne >= 0 && voisinLigne < this.lignes && voisinColonne >= 0 && voisinColonne < this.colonnes) {
|
||||||
revelerCase(voisinL, voisinC);
|
revelerCase(voisinLigne, voisinColonne);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,11 +163,15 @@ public class Grille implements Serializable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si le joueur a révélé toutes les cases non-minées du plateau.
|
||||||
|
* @return true si la partie est gagnée, false si elle est toujours en cours.
|
||||||
|
*/
|
||||||
public boolean estGagnee() {
|
public boolean estGagnee() {
|
||||||
int casesRevelees = 0;
|
int casesRevelees = 0;
|
||||||
for (int i = 0; i < this.lignes; i++) {
|
for (int ligne = 0; ligne < this.lignes; ligne++) {
|
||||||
for (int j = 0; j < this.colonnes; j++) {
|
for (int colonne = 0; colonne < this.colonnes; colonne++) {
|
||||||
if (this.plateau[i][j].isRevelee()) casesRevelees++;
|
if (this.plateau[ligne][colonne].isRevelee()) casesRevelees++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return casesRevelees == ((this.lignes * this.colonnes) - this.mines);
|
return casesRevelees == ((this.lignes * this.colonnes) - this.mines);
|
||||||
|
|||||||
+97
-46
@@ -1,69 +1,120 @@
|
|||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fenêtre principale affichant le menu du Démineur.
|
* Fenêtre du menu principal s'affichant au lancement de l'application.
|
||||||
|
* Permet de démarrer, reprendre ou quitter une partie.
|
||||||
*/
|
*/
|
||||||
public class MenuPrincipal extends JFrame {
|
public class MenuPrincipal extends JFrame {
|
||||||
|
|
||||||
private JButton btnNouvellePartie;
|
/** Largeur de la fenêtre en pixels. */
|
||||||
private JButton btnReprendre;
|
private static final int LARGEUR_FENETRE = 400;
|
||||||
private JButton btnQuitter;
|
|
||||||
|
/** Hauteur de la fenêtre en pixels. */
|
||||||
|
private static final int HAUTEUR_FENETRE = 300;
|
||||||
|
|
||||||
|
/** Position X initiale de la fenêtre sur l'écran. */
|
||||||
|
private static final int POSITION_X = 100;
|
||||||
|
|
||||||
|
/** Position Y initiale de la fenêtre sur l'écran. */
|
||||||
|
private static final int POSITION_Y = 100;
|
||||||
|
|
||||||
|
/** Nombre de lignes pour la disposition des boutons. */
|
||||||
|
private static final int LIGNES_GRILLE = 3;
|
||||||
|
|
||||||
|
/** Nombre de colonnes pour la disposition des boutons. */
|
||||||
|
private static final int COLONNES_GRILLE = 1;
|
||||||
|
|
||||||
|
/** Bouton permettant de lancer une nouvelle partie personnalisée. */
|
||||||
|
private JButton boutonNouvellePartie;
|
||||||
|
|
||||||
|
/** Bouton permettant de recharger la dernière sauvegarde trouvée. */
|
||||||
|
private JButton boutonReprendre;
|
||||||
|
|
||||||
|
/** Bouton permettant de fermer complètement l'application. */
|
||||||
|
private JButton boutonQuitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructeur du menu principal.
|
* Constructeur du menu principal.
|
||||||
|
* Initialise l'interface graphique en respectant les méthodes du cours.
|
||||||
*/
|
*/
|
||||||
public MenuPrincipal() {
|
public MenuPrincipal() {
|
||||||
// Configuration de la fenêtre (héritée de JFrame)
|
|
||||||
super("Démineur - Menu Principal");
|
super("Démineur - Menu Principal");
|
||||||
this.setSize(300, 250);
|
this.setSize(LARGEUR_FENETRE, HAUTEUR_FENETRE);
|
||||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
this.setLocation(POSITION_X, POSITION_Y);
|
||||||
|
|
||||||
// Mise en page principale
|
initialiserPanneauBoutons();
|
||||||
this.setLayout(new BorderLayout());
|
|
||||||
|
|
||||||
// Titre en haut
|
|
||||||
JLabel etiquetteTitre = new JLabel("DÉMINEUR");
|
|
||||||
etiquetteTitre.setHorizontalAlignment(JLabel.CENTER);
|
|
||||||
this.add(etiquetteTitre, BorderLayout.NORTH);
|
|
||||||
|
|
||||||
// Panneau pour les boutons au centre
|
|
||||||
JPanel panneauBoutons = new JPanel();
|
|
||||||
panneauBoutons.setLayout(new GridLayout(3, 1)); // 3 lignes, 1 colonne
|
|
||||||
|
|
||||||
// Création des boutons
|
|
||||||
this.btnNouvellePartie = new JButton("Commencer une nouvelle partie");
|
|
||||||
this.btnReprendre = new JButton("Reprendre la partie précédente");
|
|
||||||
this.btnQuitter = new JButton("Quitter");
|
|
||||||
|
|
||||||
// Désactivation du bouton "Reprendre" par défaut (comme demandé dans le sujet)
|
|
||||||
this.btnReprendre.setEnabled(false);
|
|
||||||
|
|
||||||
// Ajout des boutons au panneau
|
|
||||||
panneauBoutons.add(this.btnNouvellePartie);
|
|
||||||
panneauBoutons.add(this.btnReprendre);
|
|
||||||
panneauBoutons.add(this.btnQuitter);
|
|
||||||
|
|
||||||
// Ajout du panneau au centre de la fenêtre
|
|
||||||
this.add(panneauBoutons, BorderLayout.CENTER);
|
|
||||||
|
|
||||||
// Connexion avec l'observateur (le contrôleur)
|
|
||||||
ControleurMenu observateur = new ControleurMenu(this);
|
|
||||||
this.btnNouvellePartie.addActionListener(observateur);
|
|
||||||
this.btnReprendre.addActionListener(observateur);
|
|
||||||
this.btnQuitter.addActionListener(observateur);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Accesseurs pour que l'observateur puisse identifier les boutons ---
|
/**
|
||||||
public JButton getBtnNouvellePartie() { return this.btnNouvellePartie; }
|
* Crée le panneau, instancie les boutons et les ajoute à l'interface.
|
||||||
public JButton getBtnReprendre() { return this.btnReprendre; }
|
*/
|
||||||
public JButton getBtnQuitter() { return this.btnQuitter; }
|
private void initialiserPanneauBoutons() {
|
||||||
|
JPanel panneauBoutons = new JPanel();
|
||||||
|
panneauBoutons.setLayout(new GridLayout(LIGNES_GRILLE, COLONNES_GRILLE));
|
||||||
|
|
||||||
|
this.boutonNouvellePartie = new JButton("Nouvelle Partie");
|
||||||
|
this.boutonReprendre = new JButton("Reprendre une partie");
|
||||||
|
this.boutonQuitter = new JButton("Quitter");
|
||||||
|
|
||||||
|
panneauBoutons.add(this.boutonNouvellePartie);
|
||||||
|
ajouterBoutonReprendreSiSauvegarde(panneauBoutons);
|
||||||
|
panneauBoutons.add(this.boutonQuitter);
|
||||||
|
|
||||||
|
this.add(panneauBoutons, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
connecterControleur();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Méthode principale pour lancer le programme.
|
* Tente de lire le fichier de sauvegarde. Si le fichier existe,
|
||||||
* @param args arguments de la ligne de commande (inutilisés)
|
* le bouton "Reprendre" est ajouté au panneau. Sinon, l'erreur est ignorée.
|
||||||
|
* @param panneau le panneau graphique qui contiendra le bouton.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
private void ajouterBoutonReprendreSiSauvegarde(JPanel panneau) {
|
||||||
|
try {
|
||||||
|
FileInputStream testLecture = new FileInputStream("sauvegarde.dat");
|
||||||
|
testLecture.close();
|
||||||
|
panneau.add(this.boutonReprendre);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instancie le contrôleur et l'attache aux différents boutons.
|
||||||
|
*/
|
||||||
|
private void connecterControleur() {
|
||||||
|
ControleurMenu controleur = new ControleurMenu(this);
|
||||||
|
this.boutonNouvellePartie.addActionListener(controleur);
|
||||||
|
this.boutonReprendre.addActionListener(controleur);
|
||||||
|
this.boutonQuitter.addActionListener(controleur);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accède au bouton de nouvelle partie.
|
||||||
|
* @return le bouton "Nouvelle Partie".
|
||||||
|
*/
|
||||||
|
public JButton getBoutonNouvellePartie() { return this.boutonNouvellePartie; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accède au bouton de reprise de partie.
|
||||||
|
* @return le bouton "Reprendre une partie".
|
||||||
|
*/
|
||||||
|
public JButton getBoutonReprendre() { return this.boutonReprendre; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accède au bouton pour quitter le jeu.
|
||||||
|
* @return le bouton "Quitter".
|
||||||
|
*/
|
||||||
|
public JButton getBoutonQuitter() { return this.boutonQuitter; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Point d'entrée principal du programme.
|
||||||
|
* @param arguments les arguments passés en ligne de commande.
|
||||||
|
*/
|
||||||
|
public static void main(String[] arguments) {
|
||||||
MenuPrincipal menu = new MenuPrincipal();
|
MenuPrincipal menu = new MenuPrincipal();
|
||||||
menu.setVisible(true);
|
menu.setVisible(true);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user