180 lines
6.7 KiB
Java
180 lines
6.7 KiB
Java
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);
|
|
}
|
|
}
|