MAJ - affichage verrous & EtatJeu

This commit is contained in:
2026-03-23 12:03:12 +01:00
parent 800f038cd3
commit fa9e587a7e
3 changed files with 143 additions and 120 deletions
+51 -4
View File
@@ -21,6 +21,9 @@ public class EtatJeu {
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
@@ -52,6 +55,10 @@ public class EtatJeu {
return partieTerminee;
}
public int[] getSeriesParCouleurDernierCoup() {
return seriesParCouleurDernierCoup;
}
// -
// SAUVEGARDE ET RESTAURATION
@@ -214,6 +221,10 @@ public class EtatJeu {
}
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)
@@ -297,7 +308,7 @@ public class EtatJeu {
public int resoudreEtRemplir() {
int baseTotal = 0;
int nbSeries = 0;
int nbSeriesTotal = 0;
List<int[]> series = trouverSeries();
@@ -305,7 +316,11 @@ public class EtatJeu {
// Accumuler les points de base et le nombre de séries sur toutes les vagues
baseTotal += calculerPointsBase(series);
nbSeries += compterNbSeries(series);
// On compte les séries par couleur spécifiquement
compterSeriesParCouleur(series);
nbSeriesTotal += compterNbSeries(series);
boolean[][] aSupprimer = new boolean[NB_LIGNES][NB_COLONNES];
@@ -342,13 +357,45 @@ public class EtatJeu {
series = trouverSeries();
}
if (nbSeries == 0) return 0;
if (nbSeriesTotal == 0) return 0;
// Bonus : +50% par série supplémentaire après la première (spec SAÉ)
double bonus = 1.0 + (nbSeries - 1) * 0.5;
double bonus = 1.0 + (nbSeriesTotal - 1) * 0.5;
return (int) (baseTotal * bonus);
}
private void compterSeriesParCouleur(List<int[]> 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<int[]> series) {
boolean[][] masque = new boolean[NB_LIGNES][NB_COLONNES];
+92 -116
View File
@@ -1,8 +1,11 @@
package sae.chuzzle;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
@@ -10,9 +13,7 @@ import androidx.annotation.NonNull;
public class VueGrille extends View {
// =========================================================
// Constantes
// =========================================================
private static final int NB_LIGNES = 6;
private static final int NB_COLONNES = 6;
@@ -21,48 +22,30 @@ public class VueGrille extends View {
/** Symboles pour le mode daltonien, un par type. */
private static final String[] SYMBOLES = {"", "", "", "", "", "", ""};
// =========================================================
// Données métier
// =========================================================
/** Grille reçue depuis MainActivity (tableau brut). */
private int[][] grille = new int[NB_LIGNES][NB_COLONNES];
/** Verrous reçus depuis MainActivity. */
private boolean[][] verrous = new boolean[NB_LIGNES][NB_COLONNES];
private boolean modeDaltonien = false;
// =========================================================
// État de l'animation de glissement
// État du glissement
// =========================================================
/**
* true = on anime une ligne,
* false = on anime une colonne,
* null = pas d'animation en cours.
*/
private Boolean animEstLigne = null;
/** Index de la ligne ou colonne en cours de glissement. */
private int animIndex = 0;
/**
* Décalage courant en pixels (positif = droite/bas,
* négatif = gauche/haut).
*/
private float animDecalagePx = 0f;
// =========================================================
// Outils de dessin
// =========================================================
private final Paint pinceauCase = new Paint();
private final Paint pinceauSymbole = new Paint();
private final Paint pinceauCase = new Paint();
private final Paint pinceauSymbole = new Paint();
private final Paint pinceauSelection = new Paint();
private Bitmap imageChaine;
// =========================================================
// Constructeurs
// =========================================================
public VueGrille(Context contexte) {
super(contexte);
@@ -81,17 +64,18 @@ public class VueGrille extends View {
pinceauSymbole.setAntiAlias(true);
pinceauSymbole.setColor(0xFF000000);
pinceauSymbole.setTextAlign(Paint.Align.CENTER);
pinceauSelection.setAntiAlias(true);
pinceauSelection.setStyle(Paint.Style.STROKE);
pinceauSelection.setStrokeWidth(12f);
pinceauSelection.setColor(0xFFFFFFFF);
// Chargement de l'image de la chaine
imageChaine = BitmapFactory.decodeResource(getResources(), R.drawable.chaine);
}
// =========================================================
// API publique
// =========================================================
/**
* Reçoit une copie de la grille depuis MainActivity.
* VueGrille ne sait pas d'où viennent ces données,
* elle sait juste les dessiner.
*/
public void definirGrille(int[][] nouvelleGrille) {
for (int l = 0; l < NB_LIGNES; l++) {
System.arraycopy(nouvelleGrille[l], 0, grille[l], 0, NB_COLONNES);
@@ -111,17 +95,9 @@ public class VueGrille extends View {
invalidate();
}
// =========================================================
// API animation de glissement (appelée par GestionnaireTactile)
// =========================================================
/**
* Déclenche l'animation de glissement en temps réel.
*
* @param estLigne true = on déplace une ligne, false = une colonne
* @param index index de la ligne ou colonne (0-based)
* @param decalagePx décalage courant en pixels (peut être négatif)
*/
// API animation de glissement (appelée par GestionnaireTactile)
public void definirGlissement(boolean estLigne, int index, float decalagePx) {
this.animEstLigne = estLigne;
this.animIndex = index;
@@ -129,22 +105,43 @@ public class VueGrille extends View {
invalidate();
}
/**
* Annule l'animation de glissement et revient à l'affichage normal.
*/
public void annulerGlissement() {
this.animEstLigne = null;
invalidate();
}
// =========================================================
/**
* Vérifie si la sélection actuelle (ligne ou colonne) contient au moins un verrou.
*/
private boolean estSelectionBloquee() {
if (animEstLigne == null) return false;
if (animEstLigne) {
for (int c = 0; c < NB_COLONNES; c++) {
if (verrous[animIndex][c]) return true;
}
} else {
for (int l = 0; l < NB_LIGNES; l++) {
if (verrous[l][animIndex]) return true;
}
}
return false;
}
// Dessin
// =========================================================
@Override
protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(canvas);
// Mise à jour de la couleur du contour de sélection si bloqué
if (animEstLigne != null) {
if (estSelectionBloquee()) {
pinceauSelection.setColor(0xFF000000); // Noir
} else {
pinceauSelection.setColor(0xFFFFFFFF); // Blanc
}
}
int largeur = getWidth();
int hauteur = getHeight();
@@ -156,50 +153,41 @@ public class VueGrille extends View {
float margeGauche = (largeur - tailleCase * NB_COLONNES) / 2f;
float margeHaut = (hauteur - tailleCase * NB_LIGNES) / 2f;
// - On limite le dessin à la zone de la grille
canvas.save();
canvas.clipRect(margeGauche, margeHaut,
margeGauche + NB_COLONNES * tailleCase,
margeHaut + NB_LIGNES * tailleCase);
pinceauSymbole.setTextSize(tailleCase * 0.4f);
for (int ligne = 0; ligne < NB_LIGNES; ligne++) {
for (int colonne = 0; colonne < NB_COLONNES; colonne++) {
// --------------------------------------------------
// Calcul de l'offset d'animation pour cette case
// --------------------------------------------------
float offsetX = 0f;
float offsetY = 0f;
if (animEstLigne != null) {
int nbCases = Math.round(animDecalagePx / tailleCase);
if (animEstLigne && ligne == animIndex) {
// Décalage horizontal de toute la ligne
offsetX = animDecalagePx;
offsetX = nbCases * tailleCase;
} else if (!animEstLigne && colonne == animIndex) {
// Décalage vertical de toute la colonne
offsetY = animDecalagePx;
offsetY = nbCases * tailleCase;
}
}
// --------------------------------------------------
// Position de base de la case
// --------------------------------------------------
float x1 = margeGauche + colonne * tailleCase + 6 + offsetX;
float y1 = margeHaut + ligne * tailleCase + 6 + offsetY;
float x2 = margeGauche + (colonne + 1) * tailleCase - 6 + offsetX;
float y2 = margeHaut + (ligne + 1) * tailleCase - 6 + offsetY;
// --------------------------------------------------
// Dessin avec wrap-around (la case qui sort d'un
// côté réapparaît de l'autre côté)
// --------------------------------------------------
dessinerCase(canvas, ligne, colonne, x1, y1, x2, y2,
tailleCase, margeGauche, margeHaut, offsetX, offsetY);
}
}
canvas.restore();
}
/**
* Dessine une case à la position donnée.
* Si la case sort des bords (wrap-around), elle est aussi dessinée
* du côté opposé de la grille.
*/
private void dessinerCase(Canvas canvas,
int ligne, int colonne,
float x1, float y1, float x2, float y2,
@@ -208,66 +196,50 @@ public class VueGrille extends View {
float offsetX, float offsetY) {
int type = grille[ligne][colonne];
// Bornage de la grille (en pixels)
float borneGaucheGrille = margeGauche;
float bordDroiteGrille = margeGauche + NB_COLONNES * tailleCase;
float borneHautGrille = margeHaut;
float bordBasGrille = margeHaut + NB_LIGNES * tailleCase;
// Largeur totale de la ligne / hauteur totale de la colonne en pixels
float largeurGrille = NB_COLONNES * tailleCase;
float hauteurGrille = NB_LIGNES * tailleCase;
float hauteurGrille = NB_LIGNES * tailleCase;
// Dessine la case principale
// Dessin case principale
dessinerRectCase(canvas, type, ligne, colonne, x1, y1, x2, y2);
// Wrap-around horizontal (pour les lignes)
// --- Wrap-around (réapparition de l'autre côté) ---
if (offsetX != 0f) {
float wrapX1 = x1, wrapX2 = x2;
if (x2 > bordDroiteGrille) {
// La case déborde à droite → réapparaît à gauche
wrapX1 = x1 - largeurGrille;
wrapX2 = x2 - largeurGrille;
dessinerRectCase(canvas, type, ligne, colonne, wrapX1, y1, wrapX2, y2);
} else if (x1 < borneGaucheGrille) {
// La case déborde à gauche → réapparaît à droite
wrapX1 = x1 + largeurGrille;
wrapX2 = x2 + largeurGrille;
dessinerRectCase(canvas, type, ligne, colonne, wrapX1, y1, wrapX2, y2);
float bordD = margeGauche + largeurGrille;
float bordG = margeGauche;
if (x2 > bordD) {
dessinerRectCase(canvas, type, ligne, colonne, x1 - largeurGrille, y1, x2 - largeurGrille, y2);
} else if (x1 < bordG) {
dessinerRectCase(canvas, type, ligne, colonne, x1 + largeurGrille, y1, x2 + largeurGrille, y2);
}
}
// Wrap-around vertical (pour les colonnes)
if (offsetY != 0f) {
float wrapY1 = y1, wrapY2 = y2;
if (y2 > bordBasGrille) {
// La case déborde en bas → réapparaît en haut
wrapY1 = y1 - hauteurGrille;
wrapY2 = y2 - hauteurGrille;
dessinerRectCase(canvas, type, ligne, colonne, x1, wrapY1, x2, wrapY2);
} else if (y1 < borneHautGrille) {
// La case déborde en haut → réapparaît en bas
wrapY1 = y1 + hauteurGrille;
wrapY2 = y2 + hauteurGrille;
dessinerRectCase(canvas, type, ligne, colonne, x1, wrapY1, x2, wrapY2);
float bordB = margeHaut + hauteurGrille;
float bordH = margeHaut;
if (y2 > bordB) {
dessinerRectCase(canvas, type, ligne, colonne, x1, y1 - hauteurGrille, x2, y2 - hauteurGrille);
} else if (y1 < bordH) {
dessinerRectCase(canvas, type, ligne, colonne, x1, y1 + hauteurGrille, x2, y2 + hauteurGrille);
}
}
}
/**
* Dessine le rectangle coloré d'une case + verrou + symbole daltonien.
* Ajoute un contour gras si la case est sélectionnée.
*/
private void dessinerRectCase(Canvas canvas, int type,
int ligne, int colonne,
float x1, float y1, float x2, float y2) {
// Couleur de fond
definirCouleur(type);
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauCase);
if (animEstLigne != null) {
if ((animEstLigne && ligne == animIndex) || (!animEstLigne && colonne == animIndex)) {
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauSelection);
}
}
// Assombrir si verrouillée
if (verrous[ligne][colonne]) {
pinceauCase.setARGB(120, 0, 0, 0);
@@ -275,24 +247,28 @@ public class VueGrille extends View {
}
float cx = (x1 + x2) / 2f;
float cy = (y1 + y2) / 2f
- (pinceauSymbole.descent() + pinceauSymbole.ascent()) / 2f;
float cy = (y1 + y2) / 2f - (pinceauSymbole.descent() + pinceauSymbole.ascent()) / 2f;
// Symbole daltonien
if (modeDaltonien) {
canvas.drawText(SYMBOLES[type % NB_TYPES], cx, cy, pinceauSymbole);
}
// Cadenas par-dessus si verrouillée
if (verrous[ligne][colonne]) {
// Dessin de l'image de la chaine si verrouillée
if (verrous[ligne][colonne] && imageChaine != null) {
float size = (x2 - x1);
float chainSize = size * 0.55f; // Taille de chaque morceau de chaine
// Dessin d'une chaine en haut à gauche
canvas.drawBitmap(imageChaine, null, new RectF(x1, y1, x1 + chainSize, y1 + chainSize), null);
// Dessin d'une chaine en bas à droite
canvas.drawBitmap(imageChaine, null, new RectF(x2 - chainSize, y2 - chainSize, x2, y2), null);
} else if (verrous[ligne][colonne]) {
// Fallback si l'image n'est pas trouvée
canvas.drawText("🔒", cx, cy, pinceauSymbole);
}
}
// =========================================================
// Utilitaire privé
// =========================================================
private void definirCouleur(int type) {
switch (type % NB_TYPES) {
case 0: pinceauCase.setARGB(255, 200, 200, 200); break;
@@ -304,4 +280,4 @@ public class VueGrille extends View {
case 6: pinceauCase.setARGB(255, 255, 90, 90); break;
}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB