package sae.chuzzle; import android.os.Bundle; import java.util.ArrayList; import java.util.List; import java.util.Random; public class EtatJeu { public static final int NB_LIGNES = 6; public static final int NB_COLONNES = 6; public static final int NB_TYPES = 7; private final int[][] grille = new int[NB_LIGNES][NB_COLONNES]; private final boolean[][] verrous = new boolean[NB_LIGNES][NB_COLONNES]; private final Random aleatoire; private int score = 0; private int nbCoups = 0; private boolean partieTerminee = false; private boolean hardMode = false; // Ajout pour les objectifs : comptage des séries par couleur au dernier coup private final int[] seriesParCouleurDernierCoup = new int[NB_TYPES]; //- // CONSTRUCTEURS public EtatJeu() { aleatoire = new Random(); initialiserGrilleSansTriples(); } public EtatJeu(long graine, boolean hardMode) { aleatoire = new Random(graine); this.hardMode = hardMode; initialiserGrilleSansTriples(); } // - // GETTERS public int obtenirScore() { return score; } public int obtenirNbCoups() { return nbCoups; } public boolean estTerminee() { return partieTerminee; } public int[] getSeriesParCouleurDernierCoup() { return seriesParCouleurDernierCoup; } // - // SAUVEGARDE ET RESTAURATION public void sauvegarderEtat(Bundle bundle) { int[] flatGrille = new int[NB_LIGNES * NB_COLONNES]; boolean[] flatVerrous = new boolean[NB_LIGNES * NB_COLONNES]; for (int l = 0; l < NB_LIGNES; l++) { for (int c = 0; c < NB_COLONNES; c++) { flatGrille[l * NB_COLONNES + c] = grille[l][c]; flatVerrous[l * NB_COLONNES + c] = verrous[l][c]; } } bundle.putIntArray("grille", flatGrille); bundle.putBooleanArray("verrous", flatVerrous); bundle.putInt("score", score); bundle.putInt("nbCoups", nbCoups); bundle.putBoolean("partieTerminee", partieTerminee); } public void restaurerEtat(Bundle bundle) { if (!bundle.containsKey("grille")) return; int[] flatGrille = bundle.getIntArray("grille"); boolean[] flatVerrous = bundle.getBooleanArray("verrous"); if (flatGrille != null && flatVerrous != null) { for (int l = 0; l < NB_LIGNES; l++) { for (int c = 0; c < NB_COLONNES; c++) { grille[l][c] = flatGrille[l * NB_COLONNES + c]; verrous[l][c] = flatVerrous[l * NB_COLONNES + c]; } } } score = bundle.getInt("score"); nbCoups = bundle.getInt("nbCoups"); partieTerminee = bundle.getBoolean("partieTerminee"); } public int obtenirCase(int ligne, int colonne) { return grille[ligne][colonne]; } public int[][] obtenirGrille() { int[][] copie = new int[NB_LIGNES][NB_COLONNES]; for (int l = 0; l < NB_LIGNES; l++) { System.arraycopy(grille[l], 0, copie[l], 0, NB_COLONNES); } return copie; } public boolean[][] obtenirVerrous() { boolean[][] copie = new boolean[NB_LIGNES][NB_COLONNES]; for (int l = 0; l < NB_LIGNES; l++) { System.arraycopy(verrous[l], 0, copie[l], 0, NB_COLONNES); } return copie; } // - // INITIALISATION SANS TRIPLES // private void initialiserGrilleSansTriples() { for (int ligne = 0; ligne < NB_LIGNES; ligne++) { for (int colonne = 0; colonne < NB_COLONNES; colonne++) { int valeur; do { valeur = aleatoire.nextInt(NB_TYPES); } while (creeTriple(ligne, colonne, valeur)); grille[ligne][colonne] = valeur; } } } private boolean creeTriple(int ligne, int colonne, int valeur) { if (colonne >= 2) { if (grille[ligne][colonne - 1] == valeur && grille[ligne][colonne - 2] == valeur) { return true; } } if (ligne >= 2) { if (grille[ligne - 1][colonne] == valeur && grille[ligne - 2][colonne] == valeur) { return true; } } return false; } // - // DECALAGE CIRCULAIRE public void decalerLigne(int ligne, int sens) { int s = ((sens % NB_COLONNES) + NB_COLONNES) % NB_COLONNES; for (int etape = 0; etape < s; etape++) { int tmp = grille[ligne][NB_COLONNES - 1]; for (int c = NB_COLONNES - 1; c > 0; c--) { grille[ligne][c] = grille[ligne][c - 1]; } grille[ligne][0] = tmp; } } public void decalerColonne(int colonne, int sens) { int s = ((sens % NB_LIGNES) + NB_LIGNES) % NB_LIGNES; for (int etape = 0; etape < s; etape++) { int tmp = grille[NB_LIGNES - 1][colonne]; for (int l = NB_LIGNES - 1; l > 0; l--) { grille[l][colonne] = grille[l - 1][colonne]; } grille[0][colonne] = tmp; } } // - // APPLIQUER UN COUP public boolean appliquerCoup(boolean estLigne, int index, int sens) { if (partieTerminee) { return false; } // Les verrous bloquent toujours, quel que soit le mode if (estBloque(estLigne, index)) { return false; } int[][] sauvegarde = copierGrille(); if (estLigne) { decalerLigne(index, sens); } else { decalerColonne(index, sens); } if (trouverSeries().isEmpty()) { restaurerGrille(sauvegarde); return false; } nbCoups++; // Reset des séries du dernier coup for (int i = 0; i < NB_TYPES; i++) seriesParCouleurDernierCoup[i] = 0; score += resoudreEtRemplir(); // Verrou après chaque coup ; 2 verrous en hard mode (plus difficile) ajouterVerrou(); if (hardMode) { ajouterVerrou(); } if (!aUnCoupValide()) { partieTerminee = true; } return true; } // - // TROUVER LES SERIES public List trouverSeries() { boolean[][] aSupprimer = new boolean[NB_LIGNES][NB_COLONNES]; for (int l = 0; l < NB_LIGNES; l++) { int c = 0; while (c < NB_COLONNES) { int type = grille[l][c]; int fin = c + 1; while (fin < NB_COLONNES && grille[l][fin] == type) { fin++; } if (fin - c >= 3) { for (int k = c; k < fin; k++) { aSupprimer[l][k] = true; } } c = fin; } } for (int col = 0; col < NB_COLONNES; col++) { int l = 0; while (l < NB_LIGNES) { int type = grille[l][col]; int fin = l + 1; while (fin < NB_LIGNES && grille[fin][col] == type) { fin++; } if (fin - l >= 3) { for (int k = l; k < fin; k++) { aSupprimer[k][col] = true; } } l = fin; } } List positions = new ArrayList<>(); for (int l = 0; l < NB_LIGNES; l++) { for (int col = 0; col < NB_COLONNES; col++) { if (aSupprimer[l][col]) { positions.add(new int[]{l, col}); } } } return positions; } // - // RESOLUTION AVEC CASCADE public int resoudreEtRemplir() { int baseTotal = 0; int nbSeriesTotal = 0; List series = trouverSeries(); while (!series.isEmpty()) { // Accumuler les points de base et le nombre de séries sur toutes les vagues baseTotal += calculerPointsBase(series); // On compte les séries par couleur spécifiquement compterSeriesParCouleur(series); nbSeriesTotal += compterNbSeries(series); boolean[][] aSupprimer = new boolean[NB_LIGNES][NB_COLONNES]; for (int[] pos : series) { aSupprimer[pos[0]][pos[1]] = true; } // Libérer les verrous des cases supprimées libererVerrous(aSupprimer); for (int col = 0; col < NB_COLONNES; col++) { List survivants = new ArrayList<>(); for (int l = NB_LIGNES - 1; l >= 0; l--) { if (!aSupprimer[l][col]) { survivants.add(grille[l][col]); } } int li = NB_LIGNES - 1; for (int val : survivants) { grille[li][col] = val; li--; } while (li >= 0) { grille[li][col] = aleatoire.nextInt(NB_TYPES); li--; } } series = trouverSeries(); } if (nbSeriesTotal == 0) return 0; // Bonus : +50% par série supplémentaire après la première (spec SAÉ) double bonus = 1.0 + (nbSeriesTotal - 1) * 0.5; return (int) (baseTotal * bonus); } private void compterSeriesParCouleur(List series) { boolean[][] masque = new boolean[NB_LIGNES][NB_COLONNES]; for (int[] pos : series) masque[pos[0]][pos[1]] = true; // Horizontales for (int l = 0; l < NB_LIGNES; l++) { int c = 0; while (c < NB_COLONNES) { if (masque[l][c]) { int type = grille[l][c]; int fin = c + 1; while (fin < NB_COLONNES && masque[l][fin] && grille[l][fin] == type) fin++; if (fin - c >= 3) seriesParCouleurDernierCoup[type]++; c = fin; } else c++; } } // Verticales for (int col = 0; col < NB_COLONNES; col++) { int l = 0; while (l < NB_LIGNES) { if (masque[l][col]) { int type = grille[l][col]; int fin = l + 1; while (fin < NB_LIGNES && masque[fin][col] && grille[fin][col] == type) fin++; if (fin - l >= 3) seriesParCouleurDernierCoup[type]++; l = fin; } else l++; } } } private int calculerPointsBase(List series) { boolean[][] masque = new boolean[NB_LIGNES][NB_COLONNES]; for (int[] pos : series) { masque[pos[0]][pos[1]] = true; } int total = 0; for (int l = 0; l < NB_LIGNES; l++) { int c = 0; while (c < NB_COLONNES) { if (masque[l][c]) { int fin = c + 1; while (fin < NB_COLONNES && masque[l][fin]) { fin++; } total += pointsPourLongueur(fin - c); c = fin; } else { c++; } } } for (int col = 0; col < NB_COLONNES; col++) { int l = 0; while (l < NB_LIGNES) { if (masque[l][col]) { int fin = l + 1; while (fin < NB_LIGNES && masque[fin][col]) { fin++; } total += pointsPourLongueur(fin - l); l = fin; } else { l++; } } } return total; } private int compterNbSeries(List series) { boolean[][] masque = new boolean[NB_LIGNES][NB_COLONNES]; for (int[] pos : series) { masque[pos[0]][pos[1]] = true; } int count = 0; // Séries horizontales for (int l = 0; l < NB_LIGNES; l++) { int c = 0; while (c < NB_COLONNES) { if (masque[l][c]) { int fin = c + 1; while (fin < NB_COLONNES && masque[l][fin]) { fin++; } count++; c = fin; } else { c++; } } } // Séries verticales for (int col = 0; col < NB_COLONNES; col++) { int l = 0; while (l < NB_LIGNES) { if (masque[l][col]) { int fin = l + 1; while (fin < NB_LIGNES && masque[fin][col]) { fin++; } count++; l = fin; } else { l++; } } } return count; } private int pointsPourLongueur(int longueur) { if (longueur == 3) { return 8; } else if (longueur == 4) { return 16; } else if (longueur == 5) { return 32; } else { return 64; } } // - // VERROUS (hard mode) private boolean estBloque(boolean estLigne, int index) { if (estLigne) { for (int col = 0; col < NB_COLONNES; col++) { if (verrous[index][col]) { return true; } } } else { for (int lig = 0; lig < NB_LIGNES; lig++) { if (verrous[lig][index]) { return true; } } } return false; } private void ajouterVerrou() { int intervalle = Math.max(1, 5 - nbCoups / 10); if (nbCoups % intervalle != 0) { return; } List casesLibres = new ArrayList<>(); for (int l = 0; l < NB_LIGNES; l++) { for (int col = 0; col < NB_COLONNES; col++) { if (!verrous[l][col]) { casesLibres.add(new int[]{l, col}); } } } if (casesLibres.isEmpty()) { return; } int[] caseChoisie = casesLibres.get(aleatoire.nextInt(casesLibres.size())); verrous[caseChoisie[0]][caseChoisie[1]] = true; } private void libererVerrous(boolean[][] aSupprimer) { for (int l = 0; l < NB_LIGNES; l++) { for (int col = 0; col < NB_COLONNES; col++) { if (aSupprimer[l][col]) { verrous[l][col] = false; } } } } // - // DETECTION FIN DE PARTIE public boolean aUnCoupValide() { // Teste tous les décalages possibles (1 à N-1) pour les lignes for (int i = 0; i < NB_LIGNES; i++) { // AJOUT : Si la ligne est bloquée par un verrou, on ne peut pas la bouger if (estBloque(true, i)) continue; for (int s = 1; s < NB_COLONNES; s++) { if (coupCreeSerie(true, i, s)) { return true; } } } // Teste tous les décalages possibles (1 à N-1) pour les colonnes for (int j = 0; j < NB_COLONNES; j++) { // AJOUT : Si la colonne est bloquée par un verrou, on ne peut pas la bouger if (estBloque(false, j)) continue; for (int s = 1; s < NB_LIGNES; s++) { if (coupCreeSerie(false, j, s)) { return true; } } } return false; } public void forcerFinDePartie() { this.partieTerminee = true; } private boolean coupCreeSerie(boolean estLigne, int index, int sens) { int[][] sauvegarde = copierGrille(); if (estLigne) { decalerLigne(index, sens); } else { decalerColonne(index, sens); } boolean resultat = !trouverSeries().isEmpty(); restaurerGrille(sauvegarde); return resultat; } // - // UTILITAIRES PRIVES private int[][] copierGrille() { int[][] copie = new int[NB_LIGNES][NB_COLONNES]; for (int l = 0; l < NB_LIGNES; l++) { System.arraycopy(grille[l], 0, copie[l], 0, NB_COLONNES); } return copie; } private void restaurerGrille(int[][] sauvegarde) { for (int l = 0; l < NB_LIGNES; l++) { System.arraycopy(sauvegarde[l], 0, grille[l], 0, NB_COLONNES); } } }