package sudoku; import java.util.ArrayList; import java.util.List; public class Multidoku { private List sudokus; private List placements = new ArrayList<>(); private List> casesPartagees; private List contraintesPartagees; // Pour cet exemple, nous allons associer à chaque sudoku un offset [offsetY, // offsetX]. // Par exemple, le premier sudoku est affiché à (0,0) et le second à (ligne, // colonne) donnée. public Multidoku() { this.sudokus = new ArrayList<>(); this.casesPartagees = new ArrayList<>(); this.contraintesPartagees = new ArrayList<>(); } public void ajouterSudoku(Sudoku sudoku, int offsetLigne, int offsetColonne) { placements.add(new SudokuPlacement(sudoku, offsetLigne, offsetColonne)); } public String toStringCombined() { // 1. Déterminer la taille globale de la grille combinée et la largeur maximale int maxLigne = 0, maxColonne = 0; int globalMaxLen = 0; for (SudokuPlacement sp : placements) { int taille = sp.getSudoku().getGrille().getTaille(); maxLigne = Math.max(maxLigne, sp.getOffsetLigne() + taille); maxColonne = Math.max(maxColonne, sp.getOffsetColonne() + taille); globalMaxLen = Math.max(globalMaxLen, sp.getSudoku().getGrille().getLongueurSymboleLePlusLong()); } // On ajoute un espace supplémentaire pour séparer les colonnes int cellWidth = globalMaxLen + 1; // 2. Création et initialisation de la matrice globale String[][] global = new String[maxLigne][maxColonne]; for (int i = 0; i < maxLigne; i++) { for (int j = 0; j < maxColonne; j++) { global[i][j] = " ".repeat(cellWidth); // case vide affichée par défaut } } // 3. Pour chaque sudoku, placer l'affichage de chacune de ses cases dans la // grille globale for (SudokuPlacement sp : placements) { Grille grille = sp.getSudoku().getGrille(); int taille = grille.getTaille(); for (int i = 0; i < taille; i++) { for (int j = 0; j < taille; j++) { // Coordonnées globales calculées à partir de l'offset int globalLigne = sp.getOffsetLigne() + i; int globalColonne = sp.getOffsetColonne() + j; Case currentCase = grille.getCase(i, j); String cellStr = currentCase.toString(); int pad = globalMaxLen - cellStr.length(); String padding = " ".repeat(pad); // Récupérer le bloc associé à la case afin d'obtenir sa couleur Bloc bloc = grille.findBlocForCase(currentCase); String cellDisplay; // Si la case est partagée, on force l'affichage en blanc if (isSharedCase(currentCase)) { cellDisplay = "\u001B[37m" + cellStr + padding + "\u001B[0m"; } else { if (bloc != null) { cellDisplay = bloc.getCouleur() + cellStr + padding + "\u001B[0m"; } else { cellDisplay = cellStr + padding; } } // Insertion dans la matrice globale (ajout d'un espace pour séparer les // colonnes) global[globalLigne][globalColonne] = cellDisplay + " "; } } } // 4. Construction de la chaîne d'affichage finale StringBuilder sb = new StringBuilder(); for (int i = 0; i < maxLigne; i++) { for (int j = 0; j < maxColonne; j++) { sb.append(global[i][j]); } sb.append("\n"); } return sb.toString(); } /** * Méthode utilitaire qui vérifie si une case est partagée. * On parcourt la liste des groupes de cases partagées et si la case y figure, * on renvoie true. */ private boolean isSharedCase(Case c) { for (List groupe : casesPartagees) { if (groupe.contains(c)) { return true; } } return false; } public void ajouterCasesPartagees(List cases) { casesPartagees.add(new ArrayList<>(cases)); ajouterContraintePartagee(new ContrainteCasePartagee(cases)); } public void ajouterContraintePartagee(Contrainte contrainte) { contraintesPartagees.add(contrainte); } public boolean verifierContraintesPartagees() { return contraintesPartagees.stream() .allMatch(c -> c.estRespectee(null, null)); // Adaptation nécessaire selon votre logique } // Classe interne pour la contrainte de cases partagées public static class ContrainteCasePartagee implements Contrainte { private List casesLiees; public ContrainteCasePartagee(List cases) { this.casesLiees = new ArrayList<>(cases); } @Override public boolean estRespectee(Grille grille, Case caseActuelle) { Symbole reference = null; for (Case c : casesLiees) { if (c.getSymbole() != null) { if (reference == null) { reference = c.getSymbole(); } else if (!c.getSymbole().equals(reference)) { return false; } } } return true; } } public List getSudokus() { return sudokus; } public List> getCasesPartagees() { return casesPartagees; } public boolean estValide(Case c) { for (Contrainte contraintePartagee : contraintesPartagees) { if (!contraintePartagee.estRespectee(null, c)) { return false; } } return true; } public boolean resoudreMultidoku(boolean afficherEtape) { // Pour chaque sudoku (dans l'ordre des placements) for (SudokuPlacement sp : placements) { Sudoku s = sp.getSudoku(); // Propagation des valeurs partagées avant de résoudre ce sudoku for (List sharedGroup : casesPartagees) { // On parcourt le groupe pour trouver, le cas échéant, une valeur de référence Symbole valeurReference = null; boolean sudokuContientUneCasePartagee = false; for (Case c : sharedGroup) { if (belongsToSudoku(c, s)) { sudokuContientUneCasePartagee = true; } if (c.getSymbole() != null) { if (valeurReference == null) { valeurReference = c.getSymbole(); } else if (!c.getSymbole().equals(valeurReference)) { System.out.println("Conflit de valeurs dans un groupe partagé avant résolution."); return false; } } } // Si le sudoku contient une case partagée et qu'une valeur a été définie, on la // propage if (valeurReference != null && sudokuContientUneCasePartagee) { for (Case c : sharedGroup) { if (belongsToSudoku(c, s) && c.getSymbole() == null) { c.setSymbole(valeurReference); } } } } // Résolution du sudoku courant par backtracking ResolveurBacktraceSimple resolver = new ResolveurBacktraceSimple(s); if (!resolver.resoudre(s, afficherEtape)) { System.out.println("Échec de la résolution pour un sudoku."); return false; } } // Vérification finale des contraintes partagées if (!verifierContraintesPartagees()) { System.out.println("Les contraintes partagées ne sont pas respectées !"); return false; } return true; } /** * Méthode utilitaire qui vérifie si une case fait partie du sudoku donné. * On parcourt la grille du sudoku et on compare les instances. */ private boolean belongsToSudoku(Case c, Sudoku s) { Grille g = s.getGrille(); int taille = g.getTaille(); for (int i = 0; i < taille; i++) { for (int j = 0; j < taille; j++) { if (g.getCase(i, j) == c) { return true; } } } return false; } }