package sudoku; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.Scanner; import sudoku.core.Console; import sudoku.gui.ColorGenerator; public class Grille { private final int taille; private final Case[][] cases; private final ArrayList blocs; private ArrayList symbolesPossibles; private final Sudoku sudoku; // Référence à Sudoku private Multidoku multidoku; // Référence à Multidoku private List generatedColors; 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; initColors(); // 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 setMultidoku(Multidoku multidoku) { this.multidoku = multidoku; } /** * Génère une palette de couleurs en fonction du nombre de blocs attendus. * Pour un sudoku classique, la grille est de taille n², et il y a n² blocs, * on génère donc 'taille' couleurs. */ private void initColors() { // nombre de couleurs = nombre de blocs (pour sudoku classique) List colors = ColorGenerator.greatScheme(taille); generatedColors = new ArrayList<>(); for (ColorGenerator.Color color : colors) { generatedColors.add(convertToAnsi(color)); } } private String convertToAnsi(ColorGenerator.Color color) { int r = Math.round(color.r * 255); int g = Math.round(color.g * 255); int b = Math.round(color.b * 255); return String.format("\u001B[38;2;%d;%d;%dm", r, g, b); } public void setCase(int ligne, int colonne, Symbole symbole) { try { if (symbole != null && !symbolesPossibles.contains(symbole)) { throw new IllegalArgumentException("Symbole non autorisé : " + symbole); } Case currentCase = cases[ligne][colonne]; Symbole ancienSymbole = currentCase.getSymbole(); currentCase.setSymbole(symbole); if (!sudoku.verifierToutesContraintes() || (multidoku != null && !multidoku.verifierContraintesPartagees())) { currentCase.setSymbole(ancienSymbole); throw new IllegalArgumentException( "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]; } /** * Crée un bloc personnalisé à partir des positions spécifiées. * La couleur du bloc est choisie de façon cyclique dans la palette générée. * * Exemple d'utilisation : * sudoku.getGrille().creerBlocPersonnalise(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 } * )); */ 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] + ")"); } } // Choisir la couleur suivante en fonction du nombre de blocs déjà créés setCouleurBloc(positions); } catch (IllegalArgumentException e) { Console.errorln(e.getMessage()); } } private void setCouleurBloc(List positions) { int couleurIndex = blocs.size() % generatedColors.size(); Bloc bloc = new Bloc(generatedColors.get(couleurIndex), couleurIndex); for (int[] pos : positions) { bloc.ajouterCase(cases[pos[0]][pos[1]]); } blocs.add(bloc); } /** * Crée des blocs carrés (par exemple pour un sudoku classique). */ public void creerBlocCarre() { int blocSize = (int) Math.sqrt(taille); if (blocSize * blocSize != taille) { throw new IllegalArgumentException("La taille de la grille doit être un carré parfait."); } // Création des blocs en motif (par exemple 3x3 pour un sudoku 9x9) for (int blocRow = 0; blocRow < blocSize; blocRow++) { for (int blocCol = 0; blocCol < blocSize; blocCol++) { List positions = new ArrayList<>(); // Ajouter toutes les positions pour le bloc courant for (int i = 0; i < blocSize; i++) { for (int j = 0; j < blocSize; j++) { positions.add(new int[] { blocRow * blocSize + i, blocCol * blocSize + j }); } } setCouleurBloc(positions); } } } /** * Crée des blocs rectangulaires automatiquement à partir de la taille de la * grille. * Ne fonctionne pas pour les grilles dont la taille est un carré parfait. */ public void creerBlocRectangulaire(int blocHeight, int blocWidth) { 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; // Création des blocs en motif rectangulaire for (int blocRow = 0; blocRow < blocsParColonne; blocRow++) { for (int blocCol = 0; blocCol < blocsParLigne; blocCol++) { List positions = new ArrayList<>(); // Ajouter toutes les positions pour le bloc courant for (int i = 0; i < blocHeight; i++) { for (int j = 0; j < blocWidth; j++) { positions.add(new int[] { blocRow * blocHeight + i, blocCol * blocWidth + j }); } } setCouleurBloc(positions); } } } public void printBlocs() { for (Bloc bloc : blocs) { System.out.println(bloc.toString()); } } public static int choisirTypeSymbole(Scanner scanner) { while (true) { System.out.println("Choisissez le type de symbole :"); System.out.println("1 : Entiers"); System.out.println("2 : Lettres"); System.out.println("3 : Chaînes de caractères / Emoji"); String input = scanner.nextLine(); try { int choix = Integer.parseInt(input); if (choix >= 1 && choix <= 3) { return choix; } else { System.out.println("Choix invalide. Veuillez entrer 1, 2 ou 3."); } } catch (NumberFormatException e) { System.out.println("Entrée invalide. Veuillez entrer un nombre."); } } } public void askSetSymbolesPossibles(int choix, Scanner scanner) { try { 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 try { Symbole intTemp = Symbole.of(Integer.parseInt(input)); if (symbolesPossibles.contains(intTemp)) { Console.errorln("Ce symbole existe déjà, veuillez entrer un autre symbole"); i--; } else { symbolesPossibles.add(intTemp); } } catch (NumberFormatException e) { 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 if (Objects.equals(input, "ESC")) { Console.errorln("Ce symbole est interdit, veuillez entrer un autre symbole"); i--; } else { symbolesPossibles.add(stringTemp); } break; default: Console.errorln("Type non supporté"); return; } } } catch (Exception e) { Console.errorln("Une erreur est survenue : " + e.getMessage()); } } 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); } public int getTaille() { return taille; } 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 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 maxLen = getLongueurSymboleLePlusLong(); // longueur maximale des symboles for (int i = 0; i < taille; i++) { for (int j = 0; j < taille; j++) { Case currentCase = cases[i][j]; String cellStr = currentCase.toString(); // Calculer le nombre d'espaces à ajouter pour atteindre maxLen int pad = maxLen - cellStr.length(); String padding = " ".repeat(pad); Bloc bloc = findBlocForCase(currentCase); if (bloc != null) { sb.append(bloc.getCouleur()) // couleur du bloc .append(cellStr) // symbole .append(padding) // padding pour aligner les symboles .append("\u001B[0m ") // réinitialiser la couleur .append(" "); } else { // pas de bloc, afficher normalement sb.append(cellStr) .append(padding) .append(" "); } } sb.append("\n"); // nouvelle ligne } return sb.toString(); } }