package sudoku; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import sudoku.core.Console; public class Grille { private final int taille; private final Case[][] cases; private final ArrayList blocs; private ArrayList symbolesPossibles; private final Sudoku sudoku; public Grille(int taille, Sudoku sudoku) { this.taille = taille; this.cases = new Case[taille][taille]; this.blocs = new ArrayList<>(); this.symbolesPossibles = new ArrayList<>(); this.sudoku = sudoku; // Initialiser les cases for (int i = 0; i < taille; i++) { for (int j = 0; j < taille; j++) { cases[i][j] = new Case(i, j, null); } } } public void setCase(int ligne, int colonne, Symbole symbole) { try { if (symbole != null && !symbolesPossibles.contains(symbole)) { throw new IllegalArgumentException("Symbole non autorisé : " + symbole); } // Save ancien symbole Symbole ancienSymbole = cases[ligne][colonne].getSymbole(); // Set nouveau symbole cases[ligne][colonne].setSymbole(symbole); // Vérifier les contraintes if (!sudoku.verifierToutesContraintes()) { // Revert to ancien symbole cases[ligne][colonne].setSymbole(ancienSymbole); throw new IllegalArgumentException("SET CASE: Les contraintes ne sont pas respectées pour la case (" + ligne + ", " + colonne + ")"); } } catch (Exception e) { Console.errorln(e.getMessage()); } } public Case getCase(int ligne, int colonne) { 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, int blocHeight, int blocWidth) { List couleursUtilisees = new ArrayList<>(); int blocsParLigne = taille / blocWidth; int blocsParColonne = taille / blocHeight; // 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 < blocsParColonne && voisinCol >= 0 && voisinCol < blocsParLigne) { int blockIndex = voisinRow * blocsParLigne + 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().creerBloc(Arrays.asList( * new int[] { 0, 0 }, * new int[] { 0, 1 }, * new int[] { 0, 2 }, * new int[] { 1, 0 }, * new int[] { 1, 1 }, * new int[] { 1, 2 }, * new int[] { 2, 0 }, * new int[] { 2, 1 }, * new int[] { 2, 2 })); * * @param positions * @return */ public void creerBlocPersonnalise(List positions) { try { // Validation des positions for (int[] pos : positions) { if (pos[0] < 0 || pos[0] >= taille || pos[1] < 0 || pos[1] >= taille) { throw new IllegalArgumentException("Position invalide : (" + pos[0] + ", " + pos[1] + ")"); } } // Collecter les couleurs des blocs voisins List couleursVoisines = new ArrayList<>(); for (int[] pos : positions) { // Vérifier les cases adjacentes int[][] directions = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; for (int[] dir : directions) { int neighborRow = pos[0] + dir[0]; int neighborCol = pos[1] + dir[1]; if (neighborRow >= 0 && neighborRow < taille && neighborCol >= 0 && neighborCol < taille) { Case neighborCase = cases[neighborRow][neighborCol]; Bloc neighborBloc = findBlocForCase(neighborCase); if (neighborBloc != null) { couleursVoisines.add(neighborBloc.getCouleurIndex()); } } } } // Trouver une couleur disponible int couleurIndex = -1; for (int c = 0; c < QUATRE_COULEURS.length; c++) { if (!couleursVoisines.contains(c)) { couleurIndex = c; break; } } if (couleurIndex == -1) { couleurIndex = 0; // Fallback } // Créer et ajouter le bloc 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) { Console.errorln(e.getMessage()); } } 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."); } // Create blocks in 3x3 pattern for (int blocRow = 0; blocRow < blocSize; blocRow++) { for (int blocCol = 0; blocCol < blocSize; blocCol++) { List positions = new ArrayList<>(); // 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 }); } } int couleurIndex = getCouleurDisponible(blocRow, blocCol, blocSize, blocSize); 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) { Console.errorln(e.getMessage()); } } /** * Crée des blocs rectangles automatiquement à partir de la taille de la grille * Ne fonctionne pas pour les tailles de grilles qui sont des carrés parfaits * * @param blocRow * @param blocCol * @param blocHeight * @param blocWidth */ public void creerBlocRectangulaire(int blocHeight, int blocWidth) { try { if (taille % blocHeight != 0 || taille % blocWidth != 0) { throw new IllegalArgumentException( "La taille de la grille doit être divisible par les dimensions des blocs."); } int blocsParLigne = taille / blocWidth; int blocsParColonne = taille / blocHeight; // Create blocks in rectangular pattern for (int blocRow = 0; blocRow < blocsParColonne; blocRow++) { for (int blocCol = 0; blocCol < blocsParLigne; blocCol++) { List positions = new ArrayList<>(); // Add all positions for current block for (int i = 0; i < blocHeight; i++) { for (int j = 0; j < blocWidth; j++) { positions.add(new int[] { blocRow * blocHeight + i, blocCol * blocWidth + j }); } } int couleurIndex = getCouleurDisponible(blocRow, blocCol, blocHeight, blocWidth); 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) { Console.errorln(e.getMessage()); } } public void printBlocs() { for (Bloc bloc : blocs) { System.out.println(bloc.toString()); } } public void askSetSymbolesPossibles() { Scanner scanner = new Scanner(System.in); try { Console.infoln("Choisissez le type de symboles :"); Console.infoln("1. Nombres"); Console.infoln("2. Lettres"); Console.infoln("3. Texte/Emoji"); int choix = 0; try { choix = Integer.parseInt(scanner.nextLine()); if (choix < 1 || choix > 3) { throw new NumberFormatException("Choix invalide"); } } catch (NumberFormatException e) { Console.errorln("Choix invalide"); return; } for (int i = 0; i < taille; i++) { System.out.println(("Entrez le symbole " + (i + 1) + "/" + taille + " :")); String input = scanner.nextLine(); switch (choix) { case 1: // Nombres Symbole intTemp = Symbole.of(input); if (intTemp.isInt()) { if (symbolesPossibles.contains(intTemp)) { Console.errorln("Ce symbole existe déjà, veuillez entrer un autre symbole"); i--; } else { symbolesPossibles.add(intTemp); } } else { Console.errorln("Veuillez entrer un nombre valide"); i--; } break; case 2: // Lettres if (input.length() == 1 && Symbole.of(input.charAt(0)).isLetter()) { Symbole charTemp = Symbole.of(input.charAt(0)); if (symbolesPossibles.contains(charTemp)) { Console.errorln("Ce symbole existe déjà, veuillez entrer un autre symbole"); i--; } else { symbolesPossibles.add(charTemp); } } else { Console.errorln("Veuillez entrer une lettre valide"); i--; } break; case 3: // Texte/Emoji Symbole stringTemp = Symbole.of(input); if (symbolesPossibles.contains(stringTemp)) { Console.errorln("Ce symbole existe déjà, veuillez entrer un autre symbole"); i--; } else { symbolesPossibles.add(stringTemp); } break; default: Console.errorln("Type non supporté"); return; } } } catch (Exception e) { System.out.println("Une erreur est survenue : " + e.getMessage()); } finally { scanner.close(); } } public List getSymbolesPossibles() { return symbolesPossibles; } public void setSymbolesPossibles(ArrayList symbolesPossibles) { this.symbolesPossibles = symbolesPossibles; } public void printSymbolesPossibles() { StringBuilder sb = new StringBuilder(); for (Symbole symbole : symbolesPossibles) { sb.append(symbole.toString()).append(" "); } 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( "GRILLE: Contrainte non respectée à la position : ligne=" + i + ", colonne=" + j); return false; } } } } } return true; } public Case getCaseLibre() { for (int i = 0; i < taille; i++) { for (int j = 0; j < taille; j++) { if (cases[i][j].getSymbole() == null) { return cases[i][j]; } } } return null; } public int getLongueurSymboleLePlusLong() { int max = 0; for (Symbole symbole : symbolesPossibles) { if (symbole.toString().length() > max) { max = symbole.toString().length(); } } return max; } @Override // public String toString() { // StringBuilder sb = new StringBuilder(); // int plusLongSymbole = this.getLongueurSymboleLePlusLong(); // for (int i = 0; i < taille; i++) { // for (int j = 0; j < taille; j++) { // Case currentCase = cases[i][j]; // Bloc bloc = findBlocForCase(currentCase); // if (bloc != null) { // sb.append(bloc.getCouleur()) // Couleur du bloc // .append(" ".repeat(plusLongSymbole - currentCase.toString().length())) // // Alignement // .append(currentCase.toString()) // .append("\u001B[0m ") // Réinitialiser la couleur // .append(" "); // } else { // sb.append(currentCase.toString()).append(" "); // } // } // sb.append("\n"); // } // return sb.toString(); // } public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < taille; i++) { for (int j = 0; j < taille; j++) { 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(); } }