From 7f96fd15a6d7a857646ec17f44593c27a561fbd6 Mon Sep 17 00:00:00 2001 From: val Date: Sat, 7 Mar 2026 23:47:29 +0100 Subject: [PATCH] Ajout du menu + MAJ de l'arborescence --- .../sae/chuzzle/ExampleInstrumentedTest.java | 26 + src/main/AndroidManifest.xml | 36 ++ src/main/java/sae/chuzzle/EtatJeu.java | 483 ++++++++++++++++++ src/main/java/sae/chuzzle/MainActivity.java | 124 +++++ src/main/java/sae/chuzzle/MenuActivity.java | 44 ++ src/main/java/sae/chuzzle/VueGrille.java | 146 ++++++ .../drawable-v24/ic_launcher_foreground.xml | 30 ++ .../res/drawable/ic_launcher_background.xml | 170 ++++++ src/main/res/layout/activity_main.xml | 86 ++++ src/main/res/layout/activity_menu.xml | 38 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 1404 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2898 bytes src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 982 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1772 bytes src/main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1900 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3918 bytes src/main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 2884 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5914 bytes src/main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 3844 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7778 bytes src/main/res/values-night/themes.xml | 4 + src/main/res/values/colors.xml | 5 + src/main/res/values/strings.xml | 3 + src/main/res/values/themes.xml | 4 + src/main/res/xml/backup_rules.xml | 13 + src/main/res/xml/data_extraction_rules.xml | 19 + .../java/sae/chuzzle/ExampleUnitTest.java | 17 + 29 files changed, 1260 insertions(+) create mode 100644 src/androidTest/java/sae/chuzzle/ExampleInstrumentedTest.java create mode 100644 src/main/AndroidManifest.xml create mode 100644 src/main/java/sae/chuzzle/EtatJeu.java create mode 100644 src/main/java/sae/chuzzle/MainActivity.java create mode 100644 src/main/java/sae/chuzzle/MenuActivity.java create mode 100644 src/main/java/sae/chuzzle/VueGrille.java create mode 100644 src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 src/main/res/drawable/ic_launcher_background.xml create mode 100644 src/main/res/layout/activity_main.xml create mode 100644 src/main/res/layout/activity_menu.xml create mode 100644 src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 src/main/res/values-night/themes.xml create mode 100644 src/main/res/values/colors.xml create mode 100644 src/main/res/values/strings.xml create mode 100644 src/main/res/values/themes.xml create mode 100644 src/main/res/xml/backup_rules.xml create mode 100644 src/main/res/xml/data_extraction_rules.xml create mode 100644 src/test/java/sae/chuzzle/ExampleUnitTest.java diff --git a/src/androidTest/java/sae/chuzzle/ExampleInstrumentedTest.java b/src/androidTest/java/sae/chuzzle/ExampleInstrumentedTest.java new file mode 100644 index 0000000..dc7075e --- /dev/null +++ b/src/androidTest/java/sae/chuzzle/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package sae.chuzzle; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("sae.chuzzle", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b3f7692 --- /dev/null +++ b/src/main/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/sae/chuzzle/EtatJeu.java b/src/main/java/sae/chuzzle/EtatJeu.java new file mode 100644 index 0000000..838f8be --- /dev/null +++ b/src/main/java/sae/chuzzle/EtatJeu.java @@ -0,0 +1,483 @@ +package sae.chuzzle; + +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; + + // ========================================================= + // 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 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; + } + + if (hardMode && 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++; + score += resoudreEtRemplir(); + + 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 pointsTotal = 0; + int vague = 0; + + List series = trouverSeries(); + + while (!series.isEmpty()) { + + int pointsBase = calculerPointsBase(series); + double multiplicateur = 1.0 + vague * 0.5; + pointsTotal += (int) (pointsBase * multiplicateur); + + 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--; + } + } + + vague++; + series = trouverSeries(); + } + + return pointsTotal; + } + + 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 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() { + for (int i = 0; i < NB_LIGNES; i++) { + if (coupCreeSerie(true, i, +1)) { + return true; + } + if (coupCreeSerie(true, i, -1)) { + return true; + } + } + + for (int j = 0; j < NB_COLONNES; j++) { + if (coupCreeSerie(false, j, +1)) { + return true; + } + if (coupCreeSerie(false, j, -1)) { + return true; + } + } + + return false; + } + + 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/sae/chuzzle/MainActivity.java b/src/main/java/sae/chuzzle/MainActivity.java new file mode 100644 index 0000000..a5e5f21 --- /dev/null +++ b/src/main/java/sae/chuzzle/MainActivity.java @@ -0,0 +1,124 @@ +package sae.chuzzle; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.RadioButton; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import sae.chuzzle.EtatJeu; +import sae.chuzzle.VueGrille; + +public class MainActivity extends Activity implements View.OnClickListener { + + private EtatJeu etatJeu; + private VueGrille vueGrille; + private TextView tvScore; + private TextView tvCoups; + private RadioButton rbLigne; + private RadioButton rbDroite; + private Spinner spinnerIndex; + private Button btnJouer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // - Lire la graine et les options + long graine = getIntent().getLongExtra("graine", System.currentTimeMillis()); + boolean daltonien = getSharedPreferences("chuzzle_prefs", MODE_PRIVATE) + .getBoolean("daltonien", false); + boolean hardMode = getSharedPreferences("chuzzle_prefs", MODE_PRIVATE) + .getBoolean("hard_mode", false); + + // - Modèle + etatJeu = new EtatJeu(graine, hardMode); + + // - Récupérer les vues depuis le XML + tvScore = findViewById(R.id.tvScore); + tvCoups = findViewById(R.id.tvCoups); + vueGrille = findViewById(R.id.vueGrille); + rbLigne = findViewById(R.id.rbLigne); + rbDroite = findViewById(R.id.rbDroite); + spinnerIndex = findViewById(R.id.spinnerIndex); + btnJouer = findViewById(R.id.btnJouer); + + // - Configurer la vue + vueGrille.definirGrille(etatJeu.obtenirGrille()); + vueGrille.definirVerrous(etatJeu.obtenirVerrous()); + vueGrille.definirModeDaltonien(daltonien); + + // - Configurer le spinner + String[] indices = {"0", "1", "2", "3", "4", "5"}; + ArrayAdapter adaptateur = new ArrayAdapter<>( + this, + android.R.layout.simple_spinner_item, + indices + ); + adaptateur.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerIndex.setAdapter(adaptateur); + + // --- Bouton jouer --- + btnJouer.setOnClickListener(this); + } + + + // GESTION DES CLICS + + + @Override + public void onClick(View v) { + if (v == btnJouer) { + gererCoupJoueur(); + } + } + + + // COORDINATION + + + private void gererCoupJoueur() { + boolean estLigne = rbLigne.isChecked(); + int index = spinnerIndex.getSelectedItemPosition(); + + int sens; + if (rbDroite.isChecked()) { + sens = +1; + } else { + sens = -1; + } + + boolean accepte = etatJeu.appliquerCoup(estLigne, index, sens); + + if (!accepte) { + Toast.makeText( + this, + "Coup invalide : aucune serie creee !", + Toast.LENGTH_SHORT + ).show(); + } + + rafraichirAffichage(); + + if (etatJeu.estTerminee()) { + Toast.makeText( + this, + "Partie terminee ! Score final : " + etatJeu.obtenirScore(), + Toast.LENGTH_LONG + ).show(); + btnJouer.setEnabled(false); + } + } + + private void rafraichirAffichage() { + tvScore.setText("Score : " + etatJeu.obtenirScore()); + tvCoups.setText("Coups : " + etatJeu.obtenirNbCoups()); + vueGrille.definirGrille(etatJeu.obtenirGrille()); + vueGrille.definirVerrous(etatJeu.obtenirVerrous()); + } +} \ No newline at end of file diff --git a/src/main/java/sae/chuzzle/MenuActivity.java b/src/main/java/sae/chuzzle/MenuActivity.java new file mode 100644 index 0000000..c1ac3e8 --- /dev/null +++ b/src/main/java/sae/chuzzle/MenuActivity.java @@ -0,0 +1,44 @@ +package sae.chuzzle; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; + +public class MenuActivity extends Activity implements View.OnClickListener { + + private Button btnNouvellePartie; + private Button btnPartieGraine; + private Button btnOptions; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_menu); + + btnNouvellePartie = findViewById(R.id.btnNouvellePartie); + btnPartieGraine = findViewById(R.id.btnPartieGraine); + btnOptions = findViewById(R.id.btnOptions); + + btnNouvellePartie.setOnClickListener(this); + btnPartieGraine.setOnClickListener(this); + btnOptions.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + if (v == btnNouvellePartie) { + long graine = System.currentTimeMillis(); + Intent intent = new Intent(this, MainActivity.class); + intent.putExtra("graine", graine); + startActivity(intent); + } else if (v == btnPartieGraine) { + Intent intent = new Intent(this, SeedActivity.class); + startActivity(intent); + } else if (v == btnOptions) { + Intent intent = new Intent(this, OptionsActivity.class); + startActivity(intent); + } + } +} \ No newline at end of file diff --git a/src/main/java/sae/chuzzle/VueGrille.java b/src/main/java/sae/chuzzle/VueGrille.java new file mode 100644 index 0000000..7e9ef85 --- /dev/null +++ b/src/main/java/sae/chuzzle/VueGrille.java @@ -0,0 +1,146 @@ +package sae.chuzzle; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.view.View; + +import androidx.annotation.NonNull; + +public class VueGrille extends View { + private boolean[][] verrous = new boolean[NB_LIGNES][NB_COLONNES]; + + private static final int NB_LIGNES = 6; + private static final int NB_COLONNES = 6; + private static final int NB_TYPES = 7; + + // Symboles pour le mode daltonien, un par type + private static final String[] SYMBOLES = {"●", "■", "▲", "✚", "★", "♦", "✿"}; + + // La VueGrille ne reçoit qu'un tableau int[][] brut, pas un EtatJeu + private int[][] grille = new int[NB_LIGNES][NB_COLONNES]; + private boolean modeDaltonien = false; + + private final Paint pinceauCase = new Paint(); + private final Paint pinceauSymbole = new Paint(); + + // ========================================================= + // CONSTRUCTEUR + // ========================================================= + + public VueGrille(Context contexte) { + super(contexte); + + pinceauCase.setAntiAlias(true); + pinceauCase.setStyle(Paint.Style.FILL); + + pinceauSymbole.setAntiAlias(true); + pinceauSymbole.setColor(0xFF000000); + pinceauSymbole.setTextAlign(Paint.Align.CENTER); + } + + // ========================================================= + // API PUBLIQUE (appelée par MainActivity uniquement) + // ========================================================= + + /** + * 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); + } + invalidate(); + } + + public void definirModeDaltonien(boolean actif) { + this.modeDaltonien = actif; + invalidate(); + } + + // ========================================================= + // DESSIN + // ========================================================= + + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + + int largeur = getWidth(); + int hauteur = getHeight(); + + float tailleCase = Math.min( + largeur / (float) NB_COLONNES, + hauteur / (float) NB_LIGNES + ); + + float margeGauche = (largeur - tailleCase * NB_COLONNES) / 2f; + float margeHaut = (hauteur - tailleCase * NB_LIGNES) / 2f; + + pinceauSymbole.setTextSize(tailleCase * 0.4f); + + for (int ligne = 0; ligne < NB_LIGNES; ligne++) { + for (int colonne = 0; colonne < NB_COLONNES; colonne++) { + + int type = grille[ligne][colonne]; + + float x1 = margeGauche + colonne * tailleCase + 6; + float y1 = margeHaut + ligne * tailleCase + 6; + float x2 = margeGauche + (colonne + 1) * tailleCase - 6; + float y2 = margeHaut + (ligne + 1) * tailleCase - 6; + + // Dessiner la case colorée + // Dessiner la case colorée + definirCouleur(type); + canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauCase); + +// Assombrir la case si elle est verrouillée + if (verrous[ligne][colonne]) { + pinceauCase.setARGB(120, 0, 0, 0); + canvas.drawRoundRect(x1, y1, x2, y2, 20, 20, pinceauCase); + } + +// Dessiner le symbole daltonien + if (modeDaltonien) { + float cx = (x1 + x2) / 2f; + float cy = (y1 + y2) / 2f + - (pinceauSymbole.descent() + pinceauSymbole.ascent()) / 2f; + canvas.drawText(SYMBOLES[type % NB_TYPES], cx, cy, pinceauSymbole); + } + +// Dessiner le cadenas par-dessus si verrouillée + if (verrous[ligne][colonne]) { + float cx = (x1 + x2) / 2f; + float cy = (y1 + y2) / 2f + - (pinceauSymbole.descent() + pinceauSymbole.ascent()) / 2f; + canvas.drawText("🔒", cx, cy, pinceauSymbole); + } + } + } + } + + // ========================================================= + // UTILITAIRE PRIVE + // ========================================================= + + private void definirCouleur(int type) { + switch (type % NB_TYPES) { + case 0: pinceauCase.setARGB(255, 200, 200, 200); break; + case 1: pinceauCase.setARGB(255, 255, 105, 180); break; + case 2: pinceauCase.setARGB(255, 90, 230, 200); break; + case 3: pinceauCase.setARGB(255, 100, 170, 255); break; + case 4: pinceauCase.setARGB(255, 255, 220, 90); break; + case 5: pinceauCase.setARGB(255, 255, 140, 90); break; + case 6: pinceauCase.setARGB(255, 255, 90, 90); break; + } + } + public void definirVerrous(boolean[][] nouveauxVerrous) { + for (int l = 0; l < NB_LIGNES; l++) { + System.arraycopy(nouveauxVerrous[l], 0, verrous[l], 0, NB_COLONNES); + } + invalidate(); + } + +} \ No newline at end of file diff --git a/src/main/res/drawable-v24/ic_launcher_foreground.xml b/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/ic_launcher_background.xml b/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/res/layout/activity_main.xml b/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..2092eaa --- /dev/null +++ b/src/main/res/layout/activity_main.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +