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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/layout/activity_menu.xml b/src/main/res/layout/activity_menu.xml
new file mode 100644
index 0000000..a962559
--- /dev/null
+++ b/src/main/res/layout/activity_menu.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/mipmap-hdpi/ic_launcher.webp b/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/src/main/res/mipmap-mdpi/ic_launcher.webp b/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/src/main/res/mipmap-xhdpi/ic_launcher.webp b/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/src/main/res/values-night/themes.xml b/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..af00a57
--- /dev/null
+++ b/src/main/res/values-night/themes.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml
new file mode 100644
index 0000000..c8524cd
--- /dev/null
+++ b/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
new file mode 100644
index 0000000..4b06d98
--- /dev/null
+++ b/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Chuzzle
+
\ No newline at end of file
diff --git a/src/main/res/values/themes.xml b/src/main/res/values/themes.xml
new file mode 100644
index 0000000..af00a57
--- /dev/null
+++ b/src/main/res/values/themes.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/xml/backup_rules.xml b/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..4df9255
--- /dev/null
+++ b/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/xml/data_extraction_rules.xml b/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/test/java/sae/chuzzle/ExampleUnitTest.java b/src/test/java/sae/chuzzle/ExampleUnitTest.java
new file mode 100644
index 0000000..72be210
--- /dev/null
+++ b/src/test/java/sae/chuzzle/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package sae.chuzzle;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file