diff --git a/app/build.gradle b/app/build.gradle index 864291c..7ca8c69 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -19,9 +19,7 @@ repositories { dependencies { // Use JUnit Jupiter for testing. testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1' - - // This dependency is used by the application. - implementation 'com.google.guava:guava:31.1-jre' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.1' } application { diff --git a/app/src/main/java/sudoku/App.java b/app/src/main/java/sudoku/App.java index f0b1a3d..19eefc3 100644 --- a/app/src/main/java/sudoku/App.java +++ b/app/src/main/java/sudoku/App.java @@ -34,7 +34,7 @@ public class App { sudoku.getGrille().setCase(0, 7, Symbole.of(8)); sudoku.getGrille().setCase(4, 8, Symbole.of(9)); - sudoku.getGrille().createSquareBlocs(); + sudoku.getGrille().creerBlocCarre(); System.out.println("Sudoku :"); System.out.println(sudoku.getGrille().toString()); diff --git a/app/src/main/java/sudoku/Bloc.java b/app/src/main/java/sudoku/Bloc.java index 72cddcc..0bbf568 100644 --- a/app/src/main/java/sudoku/Bloc.java +++ b/app/src/main/java/sudoku/Bloc.java @@ -5,12 +5,16 @@ import java.util.List; public class Bloc { private final List cases; + private final String couleur; + private final int couleurIndex; - public Bloc() { + public Bloc(String couleur, int couleurIndex) { this.cases = new ArrayList<>(); + this.couleur = couleur; + this.couleurIndex = couleurIndex; } - public void addCase(Case c) { + public void ajouterCase(Case c) { cases.add(c); } @@ -23,23 +27,17 @@ public class Bloc { return false; } - // TO MOVE TO FUTUR CONSTRAINTS - // /** - // * Check if the bloc is valid - // * A bloc is valid if it contains no duplicate symbols - // * - // * @return - // */ - // public boolean isValid() { - // for (int i = 0; i < cases.size(); i++) { - // for (int j = i + 1; j < cases.size(); j++) { - // if (cases.get(i).getSymbole().equals(cases.get(j).getSymbole())) { - // return false; - // } - // } - // } - // return true; - // } + public String getCouleur() { + return couleur; + } + + public int getCouleurIndex() { + return couleurIndex; + } + + public List getCases() { + return cases; + } @Override public String toString() { diff --git a/app/src/main/java/sudoku/Case.java b/app/src/main/java/sudoku/Case.java index 53e96a8..a2bd8f1 100644 --- a/app/src/main/java/sudoku/Case.java +++ b/app/src/main/java/sudoku/Case.java @@ -4,27 +4,27 @@ public class Case { private final int ligne; private final int colonne; private Symbole symbole; - - public Case(int ligne, int colonne, Symbole symbole) { - this.ligne = ligne; - this.colonne = colonne; - this.symbole = symbole; - } - - public int getLigne() { - return ligne; - } - - public int getColonne() { - return colonne; - } - - public Symbole getSymbole() { - return symbole; - } - - public void setSymbole(Symbole symbole) { - this.symbole = symbole; + + public Case(int ligne, int colonne, Symbole symbole) { + this.ligne = ligne; + this.colonne = colonne; + this.symbole = symbole; + } + + public int getLigne() { + return ligne; + } + + public int getColonne() { + return colonne; + } + + public Symbole getSymbole() { + return symbole; + } + + public void setSymbole(Symbole symbole) { + this.symbole = symbole; } @Override diff --git a/app/src/main/java/sudoku/Contrainte.java b/app/src/main/java/sudoku/Contrainte.java new file mode 100644 index 0000000..141263b --- /dev/null +++ b/app/src/main/java/sudoku/Contrainte.java @@ -0,0 +1,5 @@ +package sudoku; + +public interface Contrainte { + boolean estRespectee(Grille grille, Case c); +} diff --git a/app/src/main/java/sudoku/ContrainteBloc.java b/app/src/main/java/sudoku/ContrainteBloc.java new file mode 100644 index 0000000..a0c27f9 --- /dev/null +++ b/app/src/main/java/sudoku/ContrainteBloc.java @@ -0,0 +1,24 @@ +package sudoku; + +import sudoku.core.Console; + +public class ContrainteBloc implements Contrainte { + @Override + public boolean estRespectee(Grille grille, Case c) { + int blocSize = (int) Math.sqrt(grille.getTaille()); + int startRow = (c.getLigne() / blocSize) * blocSize; + int startCol = (c.getColonne() / blocSize) * blocSize; + Symbole symbole = c.getSymbole(); + + for (int i = 0; i < blocSize; i++) { + for (int j = 0; j < blocSize; j++) { + Case currentCase = grille.getCase(startRow + i, startCol + j); + if (currentCase != c && currentCase.getSymbole() != null && currentCase.getSymbole().equals(symbole)) { + Console.errorln("La contrainte de bloc n'est pas respectee: ligne=" + (startRow + i) + ", col=" + (startCol + j) + ", symbole=" + symbole); + return false; + } + } + } + return true; + } +} \ No newline at end of file diff --git a/app/src/main/java/sudoku/ContrainteColonne.java b/app/src/main/java/sudoku/ContrainteColonne.java new file mode 100644 index 0000000..c0744cd --- /dev/null +++ b/app/src/main/java/sudoku/ContrainteColonne.java @@ -0,0 +1,19 @@ +package sudoku; + +import sudoku.core.Console; + +public class ContrainteColonne implements Contrainte { + @Override + public boolean estRespectee(Grille grille, Case c) { + int colonne = c.getColonne(); + Symbole symbole = c.getSymbole(); + for (int ligne = 0; ligne < grille.getTaille(); ligne++) { + Case currentCase = grille.getCase(ligne, colonne); + if (currentCase != c && currentCase.getSymbole() != null && currentCase.getSymbole().equals(symbole)) { + Console.errorln("La contrainte de colonne n'est pas respectee: ligne=" + ligne + ", col=" + colonne + ", symbole=" + symbole); + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/app/src/main/java/sudoku/ContrainteLigne.java b/app/src/main/java/sudoku/ContrainteLigne.java new file mode 100644 index 0000000..4a3ca55 --- /dev/null +++ b/app/src/main/java/sudoku/ContrainteLigne.java @@ -0,0 +1,19 @@ +package sudoku; + +import sudoku.core.Console; + +public class ContrainteLigne implements Contrainte { + @Override + public boolean estRespectee(Grille grille, Case c) { + int ligne = c.getLigne(); + Symbole symbole = c.getSymbole(); + for (int col = 0; col < grille.getTaille(); col++) { + Case currentCase = grille.getCase(ligne, col); + if (currentCase != c && currentCase.getSymbole() != null && currentCase.getSymbole().equals(symbole)) { + Console.errorln("La contrainte de ligne n'est pas respectee: ligne=" + ligne + ", col=" + col + ", symbole=" + symbole); + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/app/src/main/java/sudoku/Grille.java b/app/src/main/java/sudoku/Grille.java index d8610b3..288c679 100644 --- a/app/src/main/java/sudoku/Grille.java +++ b/app/src/main/java/sudoku/Grille.java @@ -42,11 +42,53 @@ public class Grille { return cases[ligne][colonne]; } + private static final String[] QUATRE_COULEURS = { + "\u001B[31m", // Rouge + "\u001B[32m", // Vert + "\u001B[34m", // Bleu + "\u001B[33m" // Jaune + }; + + private int getCouleurDisponible(int blocRow, int blocCol) { + List couleursUtilisees = new ArrayList<>(); + int blocSize = (int) Math.sqrt(taille); + + // Parcourir les voisins (haut, bas, gauche, droite, et diagonaux) + for (int dRow = -1; dRow <= 1; dRow++) { + for (int dCol = -1; dCol <= 1; dCol++) { + if (dRow == 0 && dCol == 0) + continue; // Ignorer le bloc courant + + int voisinRow = blocRow + dRow; + int voisinCol = blocCol + dCol; + + // Vérifier si le voisin est dans les limites + if (voisinRow >= 0 && voisinRow < taille / blocSize && + voisinCol >= 0 && voisinCol < taille / blocSize) { + int blockIndex = voisinRow * (taille / blocSize) + voisinCol; + if (blockIndex < blocs.size()) { + couleursUtilisees.add(blocs.get(blockIndex).getCouleurIndex()); + } + } + } + } + + // Trouver une couleur non utilisée + for (int c = 0; c < QUATRE_COULEURS.length; c++) { + if (!couleursUtilisees.contains(c)) { + return c; + } + } + + // Retourner une couleur par défaut (ne devrait pas arriver avec 4 couleurs) + return 0; + } + /** * Crée un bloc à partir des positions spécifiées * * Exemple : - * sudoku.getGrille().createBloc(Arrays.asList( + * sudoku.getGrille().creerBloc(Arrays.asList( * new int[] { 0, 0 }, * new int[] { 0, 1 }, * new int[] { 0, 2 }, @@ -60,30 +102,49 @@ public class Grille { * @param positions * @return */ - public Bloc createBloc(List positions) { - Bloc bloc = new Bloc(); + public Bloc creerBloc(List positions) { + int i = positions.get(0)[0]; + int j = positions.get(0)[1]; + int blocSize = (int) Math.sqrt(taille); + int couleurIndex = getCouleurDisponible(i / blocSize, j / blocSize); + + Bloc bloc = new Bloc(QUATRE_COULEURS[couleurIndex], couleurIndex); for (int[] pos : positions) { - bloc.addCase(cases[pos[0]][pos[1]]); + bloc.ajouterCase(cases[pos[0]][pos[1]]); } blocs.add(bloc); return bloc; } - public void createSquareBlocs() { + public void creerBlocCarre() { try { int blocSize = (int) Math.sqrt(taille); if (blocSize * blocSize != taille) { throw new IllegalArgumentException("La taille de la grille doit être un carré parfait."); } - for (int i = 0; i < taille; i += blocSize) { - for (int j = 0; j < taille; j += blocSize) { + + // Create blocks in 3x3 pattern + for (int blocRow = 0; blocRow < blocSize; blocRow++) { + for (int blocCol = 0; blocCol < blocSize; blocCol++) { List positions = new ArrayList<>(); - for (int k = 0; k < blocSize; k++) { - for (int l = 0; l < blocSize; l++) { - positions.add(new int[] { i + k, j + l }); + + // Add all positions for current block + for (int i = 0; i < blocSize; i++) { + for (int j = 0; j < blocSize; j++) { + positions.add(new int[] { + blocRow * blocSize + i, + blocCol * blocSize + j + }); } } - createBloc(positions); + + int couleurIndex = getCouleurDisponible(blocRow, blocCol); + Bloc bloc = new Bloc(QUATRE_COULEURS[couleurIndex], couleurIndex); + + for (int[] pos : positions) { + bloc.ajouterCase(cases[pos[0]][pos[1]]); + } + blocs.add(bloc); } } } catch (IllegalArgumentException e) { @@ -91,16 +152,6 @@ public class Grille { } } - // TO MOVE TO FUTUR CONSTRAINTS - // public boolean isValid() { - // for (Bloc bloc : blocs) { - // if (!bloc.isValid()) { - // return false; - // } - // } - // return true; - // } - public void printBlocs() { for (Bloc bloc : blocs) { System.out.println(bloc.toString()); @@ -199,15 +250,66 @@ public class Grille { System.out.println(sb.toString()); } + public int getTaille() { + return taille; + } + + private Bloc findBlocForCase(Case target) { + for (Bloc bloc : blocs) { + if (bloc.getCases().contains(target)) { + return bloc; + } + } + return null; // Ne devrait jamais arriver si la grille est bien construite + } + + /** + * Vérifie si toutes les contraintes sont respectées + * S'arrête dès qu'une contrainte n'est pas respectée + * @param contraintes + * @return + */ + public boolean verifierToutesContraintes(List contraintes) { + // Vérifier chaque case de la grille + for (int i = 0; i < taille; i++) { + for (int j = 0; j < taille; j++) { + Case currentCase = cases[i][j]; + // Ne vérifier que les cases qui ont un symbole + if (currentCase.getSymbole() != null) { + // Vérifier toutes les contraintes pour cette case + for (Contrainte contrainte : contraintes) { + if (!contrainte.estRespectee(this, currentCase)) { + Console.errorln("Contrainte non respectée à la position : ligne=" + i + ", colonne=" + j); + return false; + } + } + } + } + } + return true; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); + for (int i = 0; i < taille; i++) { for (int j = 0; j < taille; j++) { - sb.append(cases[i][j].toString()).append(" "); + Case currentCase = cases[i][j]; + Bloc bloc = findBlocForCase(currentCase); + + if (bloc != null) { + sb.append(bloc.getCouleur()) // Couleur du bloc + .append(currentCase.toString()) // Contenu de la case + .append("\u001B[0m ") // Réinitialiser la couleur + .append(" "); + } else { + sb.append(currentCase.toString()).append(" "); + } } sb.append("\n"); } + return sb.toString(); } } diff --git a/app/src/main/java/sudoku/Sudoku.java b/app/src/main/java/sudoku/Sudoku.java index 40f2fef..73e5fa5 100644 --- a/app/src/main/java/sudoku/Sudoku.java +++ b/app/src/main/java/sudoku/Sudoku.java @@ -1,10 +1,36 @@ package sudoku; +import java.util.ArrayList; +import java.util.List; + public class Sudoku { private final Grille grille; + private final List contraintes; public Sudoku(int taille) { this.grille = new Grille(taille); + this.contraintes = new ArrayList<>(); + } + + public void ajouterContrainte(Contrainte contrainte) { + contraintes.add(contrainte); + } + + public void creerBloc(List positions) { + grille.creerBloc(positions); + } + + public boolean estValide(Case c) { + for (Contrainte contrainte : contraintes) { + if (!contrainte.estRespectee(grille, c)) { + return false; + } + } + return true; + } + + public boolean verifierToutesContraintes() { + return grille.verifierToutesContraintes(contraintes); } public Grille getGrille() { diff --git a/app/src/main/java/sudoku/core/Console.java b/app/src/main/java/sudoku/core/Console.java index ccbff89..db9bd6f 100644 --- a/app/src/main/java/sudoku/core/Console.java +++ b/app/src/main/java/sudoku/core/Console.java @@ -1,7 +1,7 @@ package sudoku.core; /** - * Console class to print messages in different colors + * Console classe pour afficher des messages en couleur dans la console */ public class Console { private static final String ANSI_RESET = "\u001B[0m"; diff --git a/app/src/test/java/sudoku/AppTest.java b/app/src/test/java/sudoku/AppTest.java index 0ce7908..7c3a60a 100644 --- a/app/src/test/java/sudoku/AppTest.java +++ b/app/src/test/java/sudoku/AppTest.java @@ -2,6 +2,8 @@ package sudoku; import org.junit.jupiter.api.Test; +import sudoku.core.Console; + import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Arrays; @@ -9,14 +11,19 @@ import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; public class AppTest { + + /** + * Test the creation of a Sudoku grid with a size of 16 + */ @Test public void testExample() { - // Simulate user input - String simulatedInput = "1\n1\n2\n3\n4\n5\n6\n7\n8\n9\n"; + // Simulate user input with proper line endings + // First 1 is to select the int type of Symboles and after, Symboles are entered (1 to 16) + String simulatedInput = "1\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n"; System.setIn(new ByteArrayInputStream(simulatedInput.getBytes())); // Create a new Sudoku - Sudoku sudoku = new Sudoku(9); + Sudoku sudoku = new Sudoku(16); sudoku.getGrille().askSetSymbolesPossibles(); // Verify the symbols @@ -29,7 +36,91 @@ public class AppTest { Symbole.of(6), Symbole.of(7), Symbole.of(8), - Symbole.of(9))); + Symbole.of(9), + Symbole.of(10), + Symbole.of(11), + Symbole.of(12), + Symbole.of(13), + Symbole.of(14), + Symbole.of(15), + Symbole.of(16))); assertEquals(expectedSymbols, sudoku.getGrille().getSymbolesPossibles()); + + // Cleanup + System.setIn(System.in); + } + + @Test + public void testContraintes() { + // Create a new Sudoku + Sudoku sudoku = new Sudoku(16); + sudoku.getGrille().setSymbolesPossibles(new ArrayList<>(Arrays.asList( + Symbole.of(1), + Symbole.of(2), + Symbole.of(3), + Symbole.of(4), + Symbole.of(5), + Symbole.of(6), + Symbole.of(7), + Symbole.of(8), + Symbole.of(9), + Symbole.of(10), + Symbole.of(11), + Symbole.of(11), + Symbole.of(12), + Symbole.of(13), + Symbole.of(14), + Symbole.of(15), + Symbole.of(16)))); + + // Add constraints + sudoku.ajouterContrainte(new ContrainteLigne()); + sudoku.ajouterContrainte(new ContrainteColonne()); + // sudoku.ajouterContrainte(new ContrainteBloc()); + + // Fill the grid with valid symbols + sudoku.getGrille().setCase(0, 0, Symbole.of(1)); + sudoku.getGrille().setCase(0, 1, Symbole.of(2)); + sudoku.getGrille().setCase(0, 2, Symbole.of(3)); + sudoku.getGrille().setCase(1, 0, Symbole.of(4)); + sudoku.getGrille().setCase(1, 1, Symbole.of(5)); + sudoku.getGrille().setCase(1, 2, Symbole.of(6)); + sudoku.getGrille().setCase(2, 0, Symbole.of(7)); + sudoku.getGrille().setCase(2, 1, Symbole.of(8)); + sudoku.getGrille().setCase(2, 2, Symbole.of(9)); + sudoku.getGrille().setCase(2, 8, Symbole.of(9)); + sudoku.getGrille().setCase(14, 2, Symbole.of(6)); + sudoku.getGrille().setCase(3, 3, Symbole.of(5)); + // sudoku.getGrille().setCase(2, 2, Symbole.of(13)); + // sudoku.getGrille().setCase(2, 2, Symbole.of(14)); + // sudoku.getGrille().setCase(2, 2, Symbole.of(15)); + + // Create square blocks + sudoku.getGrille().creerBlocCarre(); + + System.out.println(sudoku.getGrille().toString()); + + // Verify that the constraints are respected + // assertTrue(sudoku.estValide(sudoku.getGrille().getCase(0, 0))); + // assertTrue(sudoku.estValide(sudoku.getGrille().getCase(1, 1))); + // assertTrue(sudoku.estValide(sudoku.getGrille().getCase(2, 2))); + + if (sudoku.verifierToutesContraintes()) { + Console.successln("Toutes les contraintes sont respectées !"); + } else { + Console.errorln("Au moins une contrainte n'est pas respectée."); + } + + // Add a duplicate symbol in the same row + // sudoku.getGrille().setCase(0, 3, Symbole.of(1)); + // assertFalse(sudoku.estValide(sudoku.getGrille().getCase(0, 3))); + + // Add a duplicate symbol in the same column + // sudoku.getGrille().setCase(3, 0, Symbole.of(1)); + // assertFalse(sudoku.estValide(sudoku.getGrille().getCase(3, 0))); + + // Add a duplicate symbol in the same block + // sudoku.getGrille().setCase(1, 1, Symbole.of(1)); + // assertFalse(sudoku.estValide(sudoku.getGrille().getCase(1, 1))); } } \ No newline at end of file diff --git a/app/src/test/java/sudoku/TestBloc.java b/app/src/test/java/sudoku/TestBloc.java index 72571a4..b6f3910 100644 --- a/app/src/test/java/sudoku/TestBloc.java +++ b/app/src/test/java/sudoku/TestBloc.java @@ -2,6 +2,8 @@ package sudoku; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; + import java.util.ArrayList; import java.util.Arrays; @@ -23,6 +25,9 @@ public class TestBloc { Symbole.of(8), Symbole.of(9)))); + // Add constraints + sudoku.ajouterContrainte(new ContrainteBloc()); + sudoku.getGrille().setCase(0, 0, Symbole.of(1)); sudoku.getGrille().setCase(6, 1, Symbole.of(2)); sudoku.getGrille().setCase(2, 2, Symbole.of(3)); @@ -34,8 +39,9 @@ public class TestBloc { sudoku.getGrille().setCase(4, 8, Symbole.of(9)); //doublon bloc sudoku.getGrille().setCase(1, 1, Symbole.of(1)); + assertFalse(sudoku.estValide(sudoku.getGrille().getCase(1, 1))); - sudoku.getGrille().createSquareBlocs(); + sudoku.getGrille().creerBlocCarre(); System.out.println("Sudoku :"); System.out.println(sudoku.getGrille().toString()); diff --git a/app/src/test/java/sudoku/TestColonne.java b/app/src/test/java/sudoku/TestColonne.java index dfda201..fa6e201 100644 --- a/app/src/test/java/sudoku/TestColonne.java +++ b/app/src/test/java/sudoku/TestColonne.java @@ -2,6 +2,8 @@ package sudoku; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; + import java.util.ArrayList; import java.util.Arrays; @@ -23,6 +25,8 @@ public class TestColonne { Symbole.of(8), Symbole.of(9)))); + sudoku.ajouterContrainte(new ContrainteColonne()); + sudoku.getGrille().setCase(0, 0, Symbole.of(1)); sudoku.getGrille().setCase(6, 1, Symbole.of(2)); sudoku.getGrille().setCase(2, 2, Symbole.of(3)); @@ -34,8 +38,9 @@ public class TestColonne { sudoku.getGrille().setCase(4, 8, Symbole.of(9)); // doublon colonne sudoku.getGrille().setCase(4, 0, Symbole.of(1)); + assertFalse(sudoku.estValide(sudoku.getGrille().getCase(4, 0))); - sudoku.getGrille().createSquareBlocs(); + sudoku.getGrille().creerBlocCarre(); System.out.println("Sudoku :"); System.out.println(sudoku.getGrille().toString()); diff --git a/app/src/test/java/sudoku/TestLigne.java b/app/src/test/java/sudoku/TestLigne.java index 826bd46..6309caf 100644 --- a/app/src/test/java/sudoku/TestLigne.java +++ b/app/src/test/java/sudoku/TestLigne.java @@ -2,6 +2,8 @@ package sudoku; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; + import java.util.ArrayList; import java.util.Arrays; @@ -23,6 +25,8 @@ public class TestLigne { Symbole.of(8), Symbole.of(9)))); + sudoku.ajouterContrainte(new ContrainteLigne()); + sudoku.getGrille().setCase(0, 0, Symbole.of(1)); sudoku.getGrille().setCase(6, 1, Symbole.of(2)); sudoku.getGrille().setCase(2, 2, Symbole.of(3)); @@ -34,8 +38,9 @@ public class TestLigne { sudoku.getGrille().setCase(4, 8, Symbole.of(9)); //doublon ligne sudoku.getGrille().setCase(0, 4, Symbole.of(1)); + assertFalse(sudoku.estValide(sudoku.getGrille().getCase(0, 4))); - sudoku.getGrille().createSquareBlocs(); + sudoku.getGrille().creerBlocCarre(); System.out.println("Sudoku :"); System.out.println(sudoku.getGrille().toString());