import java.io.Serializable; /** * 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 { /** Tableau à deux dimensions contenant toutes les cases du jeu. */ private Case[][] plateau; /** Nombre total de lignes de la grille. */ private int lignes; /** Nombre total de colonnes de la grille. */ private int colonnes; /** Nombre total de mines cachées dans la grille. */ private int mines; /** Indique si la partie est bloquée (gagnée ou perdue). */ private boolean partieTerminee; /** * Constructeur de la grille. * Crée le plateau, place les mines aléatoirement et calcule les voisins. * @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.plateau = new Case[this.lignes][this.colonnes]; for (int ligne = 0; ligne < this.lignes; ligne++) { for (int colonne = 0; colonne < this.colonnes; colonne++) { this.plateau[ligne][colonne] = new Case(); } } placerMines(); calculerVoisins(); } /** * Récupère le nombre de lignes de la grille. * @return le nombre de 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; } /** * 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; } /** * 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; } /** * 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() { int drapeaux = 0; for (int ligne = 0; ligne < this.lignes; ligne++) { for (int colonne = 0; colonne < this.colonnes; colonne++) { if (this.plateau[ligne][colonne].getMarqueur() == Case.MARQUEUR_ETOILE) { drapeaux++; } } } return this.mines - drapeaux; } /** * Place aléatoirement le nombre défini de mines sur le plateau vide. */ private void placerMines() { int minesPlacees = 0; while (minesPlacees < this.mines) { int ligneAleatoire = (int) (Math.random() * this.lignes); int colonneAleatoire = (int) (Math.random() * this.colonnes); if (!this.plateau[ligneAleatoire][colonneAleatoire].isMinee()) { this.plateau[ligneAleatoire][colonneAleatoire].setMinee(true); minesPlacees++; } } } /** * Parcourt la grille pour calculer le nombre de mines autour de chaque case non-minée. */ private void calculerVoisins() { for (int ligne = 0; ligne < this.lignes; ligne++) { for (int colonne = 0; colonne < this.colonnes; colonne++) { if (!this.plateau[ligne][colonne].isMinee()) { int compteMines = 0; for (int decalageLigne = -1; decalageLigne <= 1; decalageLigne++) { for (int decalageColonne = -1; decalageColonne <= 1; decalageColonne++) { int voisinLigne = ligne + decalageLigne; int voisinColonne = colonne + decalageColonne; if (voisinLigne >= 0 && voisinLigne < this.lignes && voisinColonne >= 0 && voisinColonne < this.colonnes) { if (this.plateau[voisinLigne][voisinColonne].isMinee()) { compteMines++; } } } } this.plateau[ligne][colonne].setNbMinesVoisines(compteMines); } } } } /** * Révèle une case et déclenche une réaction en chaîne récursive si elle vaut 0. * @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); if (caseCliquee.isMinee()) return true; if (caseCliquee.getNbMinesVoisines() == 0) { for (int decalageLigne = -1; decalageLigne <= 1; decalageLigne++) { for (int decalageColonne = -1; decalageColonne <= 1; decalageColonne++) { int voisinLigne = cibleLigne + decalageLigne; int voisinColonne = cibleColonne + decalageColonne; if (voisinLigne >= 0 && voisinLigne < this.lignes && voisinColonne >= 0 && voisinColonne < this.colonnes) { revelerCase(voisinLigne, voisinColonne); } } } } 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() { int casesRevelees = 0; for (int ligne = 0; ligne < this.lignes; ligne++) { for (int colonne = 0; colonne < this.colonnes; colonne++) { if (this.plateau[ligne][colonne].isRevelee()) casesRevelees++; } } return casesRevelees == ((this.lignes * this.colonnes) - this.mines); } }