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 partieTerminee = false;
private boolean hardMode = 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 // CONSTRUCTEURS
@@ -52,6 +55,10 @@ public class EtatJeu {
return partieTerminee; return partieTerminee;
} }
public int[] getSeriesParCouleurDernierCoup() {
return seriesParCouleurDernierCoup;
}
// - // -
// SAUVEGARDE ET RESTAURATION // SAUVEGARDE ET RESTAURATION
@@ -214,6 +221,10 @@ public class EtatJeu {
} }
nbCoups++; nbCoups++;
// Reset des séries du dernier coup
for (int i = 0; i < NB_TYPES; i++) seriesParCouleurDernierCoup[i] = 0;
score += resoudreEtRemplir(); score += resoudreEtRemplir();
// Verrou après chaque coup ; 2 verrous en hard mode (plus difficile) // Verrou après chaque coup ; 2 verrous en hard mode (plus difficile)
@@ -297,7 +308,7 @@ public class EtatJeu {
public int resoudreEtRemplir() { public int resoudreEtRemplir() {
int baseTotal = 0; int baseTotal = 0;
int nbSeries = 0; int nbSeriesTotal = 0;
List<int[]> series = trouverSeries(); 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 // Accumuler les points de base et le nombre de séries sur toutes les vagues
baseTotal += calculerPointsBase(series); 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]; boolean[][] aSupprimer = new boolean[NB_LIGNES][NB_COLONNES];
@@ -342,13 +357,45 @@ public class EtatJeu {
series = trouverSeries(); 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É) // 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); 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) { private int calculerPointsBase(List<int[]> series) {
boolean[][] masque = new boolean[NB_LIGNES][NB_COLONNES]; boolean[][] masque = new boolean[NB_LIGNES][NB_COLONNES];
+91 -115
View File
@@ -1,8 +1,11 @@
package sae.chuzzle; package sae.chuzzle;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
@@ -10,9 +13,7 @@ import androidx.annotation.NonNull;
public class VueGrille extends View { public class VueGrille extends View {
// =========================================================
// Constantes // Constantes
// =========================================================
private static final int NB_LIGNES = 6; private static final int NB_LIGNES = 6;
private static final int NB_COLONNES = 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. */ /** Symboles pour le mode daltonien, un par type. */
private static final String[] SYMBOLES = {"", "", "", "", "", "", ""}; private static final String[] SYMBOLES = {"", "", "", "", "", "", ""};
// =========================================================
// Données métier // Données métier
// =========================================================
/** Grille reçue depuis MainActivity (tableau brut). */
private int[][] grille = new int[NB_LIGNES][NB_COLONNES]; 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[][] verrous = new boolean[NB_LIGNES][NB_COLONNES];
private boolean modeDaltonien = false; 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; private Boolean animEstLigne = null;
/** Index de la ligne ou colonne en cours de glissement. */
private int animIndex = 0; private int animIndex = 0;
/**
* Décalage courant en pixels (positif = droite/bas,
* négatif = gauche/haut).
*/
private float animDecalagePx = 0f; private float animDecalagePx = 0f;
// =========================================================
// Outils de dessin // Outils de dessin
// =========================================================
private final Paint pinceauCase = new Paint(); private final Paint pinceauCase = new Paint();
private final Paint pinceauSymbole = new Paint(); private final Paint pinceauSymbole = new Paint();
private final Paint pinceauSelection = new Paint();
private Bitmap imageChaine;
// =========================================================
// Constructeurs // Constructeurs
// =========================================================
public VueGrille(Context contexte) { public VueGrille(Context contexte) {
super(contexte); super(contexte);
@@ -81,17 +64,18 @@ public class VueGrille extends View {
pinceauSymbole.setAntiAlias(true); pinceauSymbole.setAntiAlias(true);
pinceauSymbole.setColor(0xFF000000); pinceauSymbole.setColor(0xFF000000);
pinceauSymbole.setTextAlign(Paint.Align.CENTER); 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 // 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) { public void definirGrille(int[][] nouvelleGrille) {
for (int l = 0; l < NB_LIGNES; l++) { for (int l = 0; l < NB_LIGNES; l++) {
System.arraycopy(nouvelleGrille[l], 0, grille[l], 0, NB_COLONNES); System.arraycopy(nouvelleGrille[l], 0, grille[l], 0, NB_COLONNES);
@@ -111,17 +95,9 @@ public class VueGrille extends View {
invalidate(); invalidate();
} }
// =========================================================
// API animation de glissement (appelée par GestionnaireTactile)
// =========================================================
/** // 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)
*/
public void definirGlissement(boolean estLigne, int index, float decalagePx) { public void definirGlissement(boolean estLigne, int index, float decalagePx) {
this.animEstLigne = estLigne; this.animEstLigne = estLigne;
this.animIndex = index; this.animIndex = index;
@@ -129,22 +105,43 @@ public class VueGrille extends View {
invalidate(); invalidate();
} }
/**
* Annule l'animation de glissement et revient à l'affichage normal.
*/
public void annulerGlissement() { public void annulerGlissement() {
this.animEstLigne = null; this.animEstLigne = null;
invalidate(); 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 // Dessin
// =========================================================
@Override @Override
protected void onDraw(@NonNull Canvas canvas) { protected void onDraw(@NonNull Canvas canvas) {
super.onDraw(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 largeur = getWidth();
int hauteur = getHeight(); int hauteur = getHeight();
@@ -156,50 +153,41 @@ public class VueGrille extends View {
float margeGauche = (largeur - tailleCase * NB_COLONNES) / 2f; float margeGauche = (largeur - tailleCase * NB_COLONNES) / 2f;
float margeHaut = (hauteur - tailleCase * NB_LIGNES) / 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); pinceauSymbole.setTextSize(tailleCase * 0.4f);
for (int ligne = 0; ligne < NB_LIGNES; ligne++) { for (int ligne = 0; ligne < NB_LIGNES; ligne++) {
for (int colonne = 0; colonne < NB_COLONNES; colonne++) { for (int colonne = 0; colonne < NB_COLONNES; colonne++) {
// --------------------------------------------------
// Calcul de l'offset d'animation pour cette case
// --------------------------------------------------
float offsetX = 0f; float offsetX = 0f;
float offsetY = 0f; float offsetY = 0f;
if (animEstLigne != null) { if (animEstLigne != null) {
int nbCases = Math.round(animDecalagePx / tailleCase);
if (animEstLigne && ligne == animIndex) { if (animEstLigne && ligne == animIndex) {
// Décalage horizontal de toute la ligne offsetX = nbCases * tailleCase;
offsetX = animDecalagePx;
} else if (!animEstLigne && colonne == animIndex) { } else if (!animEstLigne && colonne == animIndex) {
// Décalage vertical de toute la colonne offsetY = nbCases * tailleCase;
offsetY = animDecalagePx;
} }
} }
// --------------------------------------------------
// Position de base de la case
// --------------------------------------------------
float x1 = margeGauche + colonne * tailleCase + 6 + offsetX; float x1 = margeGauche + colonne * tailleCase + 6 + offsetX;
float y1 = margeHaut + ligne * tailleCase + 6 + offsetY; float y1 = margeHaut + ligne * tailleCase + 6 + offsetY;
float x2 = margeGauche + (colonne + 1) * tailleCase - 6 + offsetX; float x2 = margeGauche + (colonne + 1) * tailleCase - 6 + offsetX;
float y2 = margeHaut + (ligne + 1) * tailleCase - 6 + offsetY; 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, dessinerCase(canvas, ligne, colonne, x1, y1, x2, y2,
tailleCase, margeGauche, margeHaut, offsetX, offsetY); 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, private void dessinerCase(Canvas canvas,
int ligne, int colonne, int ligne, int colonne,
float x1, float y1, float x2, float y2, float x1, float y1, float x2, float y2,
@@ -208,66 +196,50 @@ public class VueGrille extends View {
float offsetX, float offsetY) { float offsetX, float offsetY) {
int type = grille[ligne][colonne]; 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 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); 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) { if (offsetX != 0f) {
float wrapX1 = x1, wrapX2 = x2; float bordD = margeGauche + largeurGrille;
float bordG = margeGauche;
if (x2 > bordDroiteGrille) { if (x2 > bordD) {
// La case déborde à droite → réapparaît à gauche dessinerRectCase(canvas, type, ligne, colonne, x1 - largeurGrille, y1, x2 - largeurGrille, y2);
wrapX1 = x1 - largeurGrille; } else if (x1 < bordG) {
wrapX2 = x2 - largeurGrille; dessinerRectCase(canvas, type, ligne, colonne, x1 + largeurGrille, y1, x2 + largeurGrille, y2);
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);
} }
} }
// Wrap-around vertical (pour les colonnes)
if (offsetY != 0f) { if (offsetY != 0f) {
float wrapY1 = y1, wrapY2 = y2; float bordB = margeHaut + hauteurGrille;
float bordH = margeHaut;
if (y2 > bordBasGrille) { if (y2 > bordB) {
// La case déborde en bas → réapparaît en haut dessinerRectCase(canvas, type, ligne, colonne, x1, y1 - hauteurGrille, x2, y2 - hauteurGrille);
wrapY1 = y1 - hauteurGrille; } else if (y1 < bordH) {
wrapY2 = y2 - hauteurGrille; dessinerRectCase(canvas, type, ligne, colonne, x1, y1 + hauteurGrille, x2, 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);
} }
} }
} }
/** /**
* Dessine le rectangle coloré d'une case + verrou + symbole daltonien. * 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, private void dessinerRectCase(Canvas canvas, int type,
int ligne, int colonne, int ligne, int colonne,
float x1, float y1, float x2, float y2) { float x1, float y1, float x2, float y2) {
// Couleur de fond
definirCouleur(type); definirCouleur(type);
canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauCase); 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 // Assombrir si verrouillée
if (verrous[ligne][colonne]) { if (verrous[ligne][colonne]) {
pinceauCase.setARGB(120, 0, 0, 0); pinceauCase.setARGB(120, 0, 0, 0);
@@ -275,24 +247,28 @@ public class VueGrille extends View {
} }
float cx = (x1 + x2) / 2f; float cx = (x1 + x2) / 2f;
float cy = (y1 + y2) / 2f float cy = (y1 + y2) / 2f - (pinceauSymbole.descent() + pinceauSymbole.ascent()) / 2f;
- (pinceauSymbole.descent() + pinceauSymbole.ascent()) / 2f;
// Symbole daltonien
if (modeDaltonien) { if (modeDaltonien) {
canvas.drawText(SYMBOLES[type % NB_TYPES], cx, cy, pinceauSymbole); canvas.drawText(SYMBOLES[type % NB_TYPES], cx, cy, pinceauSymbole);
} }
// Cadenas par-dessus si verrouillée // Dessin de l'image de la chaine si verrouillée
if (verrous[ligne][colonne]) { 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); canvas.drawText("🔒", cx, cy, pinceauSymbole);
} }
} }
// =========================================================
// Utilitaire privé
// =========================================================
private void definirCouleur(int type) { private void definirCouleur(int type) {
switch (type % NB_TYPES) { switch (type % NB_TYPES) {
case 0: pinceauCase.setARGB(255, 200, 200, 200); break; case 0: pinceauCase.setARGB(255, 200, 200, 200); break;
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB