package sudoku; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Scanner; import java.util.Set; import sudoku.core.Console; /** * Classe représentant un sudoku. */ public class Sudoku { private final Grille grille; private final List contraintes; private String nom; /** * Constructeur permettant d'initialiser un sudoku à partir de la taille de sa * grille * * @param taille : taille de la grille */ public Sudoku(int taille) { this.grille = new Grille(taille, this); this.contraintes = new ArrayList<>(); } /** * Méthode permettant de modifier le nom du sudoku, utilisé pour l'affichage. * * @param nom : nom du sudoku */ public void setNom(String nom) { this.nom = nom; } /** * Méthode permettant de récupérer le nom du sudoku * * @return nom du sudoku */ public String getNom() { return this.nom; } /** * Méthode permettant d'ajouter une contrainte à un sudoku * * @param contrainte : contrainte à ajouter */ public void ajouterContrainte(Contrainte contrainte) { contraintes.add(contrainte); } /** * Méthode permettant de vérifier si une case est valide en fonction des * contraintes la concernant * * @param c : case à vérifier * @return boolean : true si la case est valide, false sinon */ public boolean estValide(Case c) { for (Contrainte contrainte : contraintes) { if (!contrainte.estRespectee(grille, c)) { return false; } } return true; } /** * Méthode permettant de vérifier si un sudoku est valide, c'est-à-dire si * toutes les cases sont valides. * * @return boolean : true si le sudoku est valide, false sinon */ public boolean estValide() { for (int i = 0; i < this.getGrille().getTaille(); i++) { for (int j = 0; j < this.getGrille().getTaille(); j++) { Case c = this.getGrille().getCase(i, j); if (c.getSymbole() != null && !this.estValide(c)) { return false; } } } return true; } /** * Méthode utilisée pour verifier toute les contraintes lors d'un ajout de Case * dans la grille. * * @return boolean : true si toutes les contraintes sont respectées, false sinon */ public boolean verifierToutesContraintes() { return grille.verifierToutesContraintes(contraintes); } /** * Méthode permettant de retourner la grille complète * * @return Grille */ public Grille getGrille() { return grille; } /** * Méthode du menu principal du jeu. */ public static void menu() { Scanner scanner = new Scanner(System.in); System.out.println("BIENVENUE DANS LE JEU DE SUDOKU"); while (true) { System.out.println("\nChoisissez le type de jeu :"); System.out.println("1 : Sudoku classique"); System.out.println("2 : Multidoku (prérempli)"); System.out.println("0 : Quitter"); try { int choix = Integer.parseInt(scanner.nextLine()); switch (choix) { case 0: System.out.println("Au revoir !"); return; case 1: menuSudoku(); break; case 2: menuMultidoku(); break; default: System.out.println("Choix invalide. Veuillez réessayer."); } } catch (NumberFormatException e) { System.out.println("Entrée invalide. Veuillez entrer un nombre."); } } } private static void menuMultidoku() { final int PAUSE_TIME = 3000; // 3 seconds System.out.println("MULTIDOKU"); System.out.println("\nChoisissez un type de Multidoku :"); System.out.println("1 : Multidoku avec cases partagées"); System.out.println("2 : Multidoku avec bloc partagé"); System.out.println("3 : Multidoku avec ligne partagée"); System.out.println("4 : Multidoku avec colonne partagée"); Scanner scanner = new Scanner(System.in); try { int choix = Integer.parseInt(scanner.nextLine()); Multidoku multidoku = creerMultidoku(choix); if (multidoku != null) { System.out.println("\nMultidoku initial :"); System.out.println(multidoku); System.out.println("\nDébut de la résolution dans 3 secondes..."); try { Thread.sleep(PAUSE_TIME); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("\nRésolution du Multidoku..."); if (multidoku.resoudreMultidoku(true)) { System.out.println("\nMultidoku résolu :"); System.out.println(multidoku); } else { System.out.println("Ce Multidoku n'a pas de solution."); } } } catch (NumberFormatException e) { System.out.println("Entrée invalide. Veuillez entrer un nombre."); } } private static Multidoku creerMultidoku(int type) { Multidoku multidoku = new Multidoku(); Sudoku s1 = new Sudoku(9); Sudoku s2 = new Sudoku(9); s1.setNom("Sudoku 1"); s2.setNom("Sudoku 2"); // Configuration commune ArrayList symboles = new ArrayList<>(); for (int i = 1; i <= 9; i++) { symboles.add(Symbole.of(i)); } for (Sudoku s : Arrays.asList(s1, s2)) { s.getGrille().setSymbolesPossibles(symboles); s.ajouterContrainte(new ContrainteLigne()); s.ajouterContrainte(new ContrainteColonne()); s.ajouterContrainte(new ContrainteBloc(s.getGrille().getTaille(), s.getGrille().getTaille())); s.getGrille().creerBlocCarre(); } switch (type) { case 1: // Cases partagées multidoku.ajouterSudoku(s1, 0, 0); multidoku.ajouterSudoku(s2, 8, 8); multidoku.ajouterCasesPartagees(Arrays.asList( s1.getGrille().getCase(8, 8), s2.getGrille().getCase(0, 0))); break; case 2: // Bloc partagé multidoku.ajouterSudoku(s1, 0, 0); multidoku.ajouterSudoku(s2, 6, 6); List casesBloc = new ArrayList<>(); for (int i = 6; i < 9; i++) { for (int j = 6; j < 9; j++) { casesBloc.add(s1.getGrille().getCase(i, j)); } } for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { casesBloc.add(s2.getGrille().getCase(i, j)); } } multidoku.ajouterCasesPartagees(casesBloc); break; case 3: // Ligne partagée multidoku.ajouterSudoku(s1, 0, 0); multidoku.ajouterSudoku(s2, 8, 1); multidoku.ajouterCasesPartagees(Arrays.asList(s1.getGrille().getCase(8, 1), s1.getGrille().getCase(8, 2), s1.getGrille().getCase(8, 3), s1.getGrille().getCase(8, 4), s1.getGrille().getCase(8, 5), s1.getGrille().getCase(8, 6), s1.getGrille().getCase(8, 7), s1.getGrille().getCase(8, 8), s2.getGrille().getCase(0, 0), s2.getGrille().getCase(0, 1), s2.getGrille().getCase(0, 2), s2.getGrille().getCase(0, 3), s2.getGrille().getCase(0, 4), s2.getGrille().getCase(0, 5), s2.getGrille().getCase(0, 6), s2.getGrille().getCase(0, 7))); break; case 4: // Colonne partagée multidoku.ajouterSudoku(s1, 0, 0); multidoku.ajouterSudoku(s2, 1, 8); multidoku.ajouterCasesPartagees(Arrays.asList( s1.getGrille().getCase(1, 8), s1.getGrille().getCase(2, 8), s1.getGrille().getCase(3, 8), s1.getGrille().getCase(4, 8), s1.getGrille().getCase(5, 8), s1.getGrille().getCase(6, 8), s1.getGrille().getCase(7, 8), s1.getGrille().getCase(8, 8), s2.getGrille().getCase(0, 0), s2.getGrille().getCase(1, 0), s2.getGrille().getCase(2, 0), s2.getGrille().getCase(3, 0), s2.getGrille().getCase(4, 0), s2.getGrille().getCase(5, 0), s2.getGrille().getCase(6, 0), s2.getGrille().getCase(7, 0))); break; default: System.out.println("Type de Multidoku non valide"); return null; } return multidoku; } /** * Méthode du menu principal du jeu de sudoku. */ public static void menuSudoku() { Scanner scanner = new Scanner(System.in); // ETAPE 1 : TAILLE GRILLE System.out.println("ETAPE 1 : Choisir la taille de la grille"); int tailleGrille = setTailleGrille(scanner); // récupère la taille de la grille Sudoku sudoku = new Sudoku(tailleGrille); // ETAPE 2 : SYMBOLE POSSIBLE System.out.println("ETAPE 2 : Choisir les symboles possibles"); int typeSymbole = Grille.choisirTypeSymbole(scanner); sudoku.getGrille().askSetSymbolesPossibles(typeSymbole, scanner); // demande à l'utilisateur de saisir ses // symboles // ETAPE 3 : REMPLIR LA GRILLE System.out.println("ETAPE 3 : Remplir la grille"); setValeursGrille(sudoku, scanner, tailleGrille, typeSymbole); System.out.println("Voici votre sudoku rempli :"); System.out.println(sudoku.getGrille().toString()); // ETAPE 4 : CHOIX DES CONTRAINTES choixContraintes(sudoku, scanner); // ETAPE 5 : RESOLUTION System.out.println("ETAPE 5 : Résolution du sudoku"); choixResolution(sudoku, scanner); } private static void choixContraintes(Sudoku sudoku, Scanner scanner) { System.out.println("Choisissez une combinaison entre 0 et 3 contraintes parmi les suivantes :"); System.out.println("1: Contrainte Bloc, 2: Contrainte Ligne, 3: Contrainte Colonne"); System.out.println( "Entrez les numéros des contraintes séparés par des virgules (par exemple, 1,2,3 pour choisir toute les contraintes) ou appuyez sur Entrée pour aucune contrainte :"); String input = scanner.nextLine(); if (input.trim().isEmpty()) { System.out.println("Aucune contrainte sélectionnée."); return; } String[] choix = input.split(","); Set contraintesChoisies = new HashSet<>(); for (String choixStr : choix) { try { int choixInt = Integer.parseInt(choixStr.trim()); if (choixInt >= 1 && choixInt <= 3) { contraintesChoisies.add(choixInt); } else { System.out.println("Choix invalide : " + choixInt); } } catch (NumberFormatException e) { System.out.println("Entrée invalide : " + choixStr); } } for (int choixContrainte : contraintesChoisies) { switch (choixContrainte) { case 1: sudoku.ajouterContrainte( new ContrainteBloc(sudoku.getGrille().getTaille(), sudoku.getGrille().getTaille())); System.out.println("Contrainte Bloc ajoutée."); break; case 2: sudoku.ajouterContrainte(new ContrainteLigne()); System.out.println("Contrainte Ligne ajoutée."); break; case 3: sudoku.ajouterContrainte(new ContrainteColonne()); System.out.println("Contrainte Colonne ajoutée."); break; default: System.out.println("Choix invalide : " + choixContrainte); } } } private void resolutionSudoku(Sudoku sudoku, boolean afficherEtape) { ResolveurBacktraceSimple resolveur = new ResolveurBacktraceSimple(); resolveur.resoudre(sudoku, afficherEtape); System.out.println("Sudoku résolu :"); System.out.println(sudoku.getGrille().toString()); } private static void choixResolution(Sudoku sudoku, Scanner scanner) { System.out.println("Voulez-vous résoudre le sudoku ? (O/N)"); String choix = scanner.nextLine(); if (choix.equalsIgnoreCase("O")) { System.out.println("Voulez-vous afficher les étapes de la résolution ? (O/N)"); String choixAffichageEtapes = scanner.nextLine(); if (choixAffichageEtapes.equalsIgnoreCase("O")) { sudoku.resolutionSudoku(sudoku, true); } else if (choixAffichageEtapes.equalsIgnoreCase("N")) { sudoku.resolutionSudoku(sudoku, false); } else { System.err.println("Choix invalide."); } } else if (choix.equalsIgnoreCase("N")) { System.err.println("Fin du jeu."); } else { System.out.println("Choix invalide."); choixResolution(sudoku, scanner); } } private static int setTailleGrille(Scanner scanner) { int tailleGrille = -1; while (tailleGrille <= 0) { try { tailleGrille = Integer.parseInt(scanner.nextLine()); if (tailleGrille <= 0) { Console.errorln("Erreur : Veuillez entrer un entier positif."); } } catch (NumberFormatException e) { Console.errorln("Erreur : Entrée invalide. Veuillez entrer un nombre."); } } return tailleGrille; } private static void setValeursGrille(Sudoku sudoku, Scanner scanner, int tailleGrille, int typeSymbole) { while (true) { try { System.out.println("Veuillez faire votre choix"); System.out.println("1 : Entrer les blocs manuellement"); System.out.println("2 : Entrer les blocs à l'aide de la longueur et de la largeur"); System.out.println("3 : Générer des blocs carrés"); int choixGenerationBloc = Integer.parseInt(scanner.nextLine()); switch (choixGenerationBloc) { case 1: System.out.println("Entrez les blocs manuellement."); creationBlocManuel(sudoku, scanner, tailleGrille); break; case 2: try { System.out.println("Entrez les blocs à l'aide de la longueur et de la largeur."); System.out.println("Entrez la longueur du bloc : "); int longueurBloc = Integer.parseInt(scanner.nextLine()); System.out.println("Entrez la largeur du bloc :"); int largeurBloc = Integer.parseInt(scanner.nextLine()); sudoku.getGrille().creerBlocRectangulaire(longueurBloc, largeurBloc); break; } catch (IllegalArgumentException e) { Console.errorln("Erreur : " + e.getMessage()); continue; } case 3: try { sudoku.getGrille().creerBlocCarre(); break; } catch (IllegalArgumentException e) { Console.errorln("Erreur : " + e.getMessage()); continue; } default: Console.errorln("Choix invalide."); continue; } break; // Sort de la boucle si la création des blocs a réussi } catch (NumberFormatException e) { Console.errorln("Erreur : Veuillez entrer un nombre valide."); } } // DEBUT DU REMPLISSAGE DU SUDOKU String input; while (true) { // Demander et vérifier la ligne int ligne = -1; while (true) { System.out.println("Pour arrêter la saisie, tapez \"esc\"."); System.out.println( "Entrez le numéro de ligne :"); input = scanner.nextLine(); // Lire la ligne if (input.equalsIgnoreCase("ESC")) { break; // Sortie de la boucle si l'utilisateur tape ESC } if (input.isEmpty()) { Console.errorln("Veuillez entrer un numéro de ligne valide."); continue; // Recommencer la saisie de la ligne si l'entrée est vide } try { ligne = Integer.parseInt(input); // Convertir la ligne en entier break; // Sortir de la boucle si la ligne est valide } catch (NumberFormatException e) { Console.errorln("Veuillez entrer un numéro de ligne valide (un nombre entier)."); } } // ARRET DE LA BOUCLE SI SAISIE DE "ESC" if (input.equalsIgnoreCase("ESC")) { break; // Sortie de la boucle principale si l'utilisateur tape ESC } // Demander et vérifier la colonne int colonne; while (true) { System.out.println("Entrez le numéro de colonne :"); input = scanner.nextLine(); // Lire la colonne if (input.isEmpty()) { Console.errorln("Veuillez entrer un numéro de colonne valide."); continue; // Recommencer la saisie de la colonne si l'entrée est vide } try { colonne = Integer.parseInt(input); // Convertir la colonne en entier break; // Sortir de la boucle si la colonne est valide } catch (NumberFormatException e) { Console.errorln("Veuillez entrer un numéro de colonne valide (un nombre entier)."); } } // Demander et vérifier le symbole String symbole; while (true) { System.out.println("Entrez le symbole :"); symbole = scanner.nextLine(); // Lire le symbole if (symbole.isEmpty()) { Console.errorln("Veuillez entrer un symbole valide."); continue; // Recommencer la saisie du symbole si l'entrée est vide } try { Symbole s; switch (typeSymbole) { case 1: // Entiers if (!Symbole.of(symbole).isInt()) { throw new IllegalArgumentException("Veuillez entrer un nombre valide."); } s = Symbole.of(Integer.parseInt(symbole)); break; case 2: // Lettres Symbole tempSymbole = Symbole.of(symbole); if (!tempSymbole.isLetter()) { throw new IllegalArgumentException("Veuillez entrer une seule lettre."); } s = tempSymbole; break; case 3: // Chaînes de caractères s = Symbole.of(symbole); break; default: throw new IllegalArgumentException("Type de symbole invalide."); } sudoku.getGrille().setCase(ligne, colonne, s); // Ajouter le symbole à la grille System.out.println(sudoku.getGrille().toString()); break; // Sortir de la boucle si le symbole est valide } catch (IllegalArgumentException e) { System.out.println("Symbole non valide. " + e.getMessage()); } } } } private static void creationBlocManuel(Sudoku sudoku, Scanner scanner, int tailleGrille) { int nombreBloc = tailleGrille; // Nombre de blocs dans la grille Set toutesLesCoordonnees = new HashSet<>(); // Stocke toutes les coordonnées utilisées while (nombreBloc != 0) { List listeCases = new ArrayList<>(); // Liste des cases du bloc System.out.println("Remplissage du bloc " + nombreBloc); for (int i = 1; i <= tailleGrille; i++) { int choixLigne, choixColonne; boolean coordonneesValides; do { coordonneesValides = true; // Par défaut, on suppose que la coordonnée est valide // Demande de saisie System.out.println("Entrez la ligne de la case " + i + " dans le bloc " + nombreBloc + " (0 à " + (tailleGrille - 1) + "):"); choixLigne = scanner.nextInt(); System.out.println("Entrez la colonne de la case " + i + " dans le bloc " + nombreBloc + " (0 à " + (tailleGrille - 1) + "):"); choixColonne = scanner.nextInt(); // Vérification des limites if (choixLigne < 0 || choixLigne >= tailleGrille || choixColonne < 0 || choixColonne >= tailleGrille) { Console.errorln("Erreur : Coordonnées hors limites ! Veuillez réessayer."); coordonneesValides = false; continue; } // Vérification si la coordonnée existe déjà dans TOUTE la grille String coordonneeStr = choixLigne + "-" + choixColonne; if (toutesLesCoordonnees.contains(coordonneeStr)) { Console.errorln( "Erreur : Ces coordonnées sont déjà utilisées dans un autre bloc ! Veuillez en entrer une nouvelle."); coordonneesValides = false; } } while (!coordonneesValides); // Tant que la saisie est invalide, on redemande // Ajout des coordonnées validées listeCases.add(new int[] { choixLigne, choixColonne }); toutesLesCoordonnees.add(choixLigne + "-" + choixColonne); // Ajout dans l'ensemble global } // Création du bloc après la saisie complète try { sudoku.getGrille().creerBlocPersonnalise(listeCases); System.out.println("Bloc " + nombreBloc + " enregistré avec succès !"); } catch (IllegalArgumentException e) { Console.errorln("Erreur lors de la création du bloc : " + e.getMessage()); System.out.println("Veuillez recommencer la saisie de ce bloc."); continue; // Recommence le bloc en cours } nombreBloc--; // Passer au bloc suivant } } }