Files
Sudoku/app/src/main/java/sudoku/Sudoku.java

592 lines
24 KiB
Java

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<Contrainte> 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<Symbole> 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<Case> 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<Integer> 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<String> toutesLesCoordonnees = new HashSet<>(); // Stocke toutes les coordonnées utilisées
while (nombreBloc != 0) {
List<int[]> 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
}
}
}