import javax.swing.*; 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 { /** 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; /** Tableau à deux dimensions stockant les composants graphiques des cases. */ private JButton[][] boutons; /** Étiquette textuelle affichant le nombre de mines restantes à trouver. */ private JLabel labelCompteur; /** 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"); this.grille = grilleJeu; this.boutons = new JButton[grilleJeu.getLignes()][grilleJeu.getColonnes()]; this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); int largeurFenetre = grilleJeu.getColonnes() * LARGEUR_CASE_PIXELS; int hauteurFenetre = (grilleJeu.getLignes() * HAUTEUR_CASE_PIXELS) + MARGE_HAUTEUR_PIXELS; 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); } /** * Prépare et ajoute la zone centrale contenant tous les boutons cliquables. */ private void initialiserPanneauGrille() { JPanel panneauGrille = new JPanel(); panneauGrille.setLayout(new GridLayout(this.grille.getLignes(), this.grille.getColonnes())); ControleurGrille controleurSouris = new ControleurGrille(this, this.grille); for (int ligne = 0; ligne < this.grille.getLignes(); ligne++) { for (int colonne = 0; colonne < this.grille.getColonnes(); colonne++) { JButton boutonCase = new JButton(""); this.boutons[ligne][colonne] = boutonCase; panneauGrille.add(boutonCase); boutonCase.addMouseListener(controleurSouris); } } this.add(panneauGrille, BorderLayout.CENTER); } /** * Prépare et ajoute le bouton de contrôle général en bas de la fenêtre. */ private void initialiserBoutonBas() { this.boutonActionBas = new JButton("Sauver et Quitter"); this.add(this.boutonActionBas, BorderLayout.SOUTH); ControleurJeu controleurQuitter = new ControleurJeu(this); this.boutonActionBas.addActionListener(controleurQuitter); } /** * Récupère un bouton graphique spécifique de 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() { this.boutonActionBas.setText("Retour au Menu"); } /** * Interroge la grille et rafraîchit le texte du compteur de mines. */ public void mettreAJourCompteur() { this.labelCompteur.setText("Mines restantes : " + this.grille.getMinesRestantes()); } /** * Met à jour l'apparence visuelle d'un bouton selon son état logique. * @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()) { 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()) { bouton.setText("M"); bouton.setBackground(Color.RED); } else if (caseLogique.getNbMinesVoisines() > 0) { bouton.setText("" + caseLogique.getNbMinesVoisines()); bouton.setBackground(Color.WHITE); } else { bouton.setText(""); bouton.setBackground(Color.LIGHT_GRAY); } } /** * 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() { for (int ligne = 0; ligne < grille.getLignes(); ligne++) { for (int colonne = 0; colonne < grille.getColonnes(); colonne++) { rafraichirCase(ligne, colonne); } } } /** * Affiche l'état de défaite en révélant les erreurs et les mines manquantes. * @param ligneExplosion la ligne de la mine déclenchée. * @param colonneExplosion la colonne de la mine déclenchée. */ 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); } } } /** * Détermine la couleur spécifique d'une case en fin de partie (erreur, oubli, ou explosion). * @param caseActuelle la donnée de la case. * @param bouton le composant graphique. * @param ligne la ligne en cours de parcours. * @param colonne la colonne en cours de parcours. * @param ligneExplosion la ligne de la mine fatale. * @param colonneExplosion la colonne de la mine fatale. */ 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); } } }