//Retourne un Jeu avec une grille de sudoku générée //Difficulté (d) comprise entre 1 et 8 (1 très simple, 8 très compliqué) static public Jeu Generer(int d) { Random rand = new Random(); Jeu res = new Jeu(); Jeu[] tab = res.RempAlea(rand); res = tab[0]; //Arrivé ici, res contient une grille complète //On détermine les chiffres que l'ont peut enlever //Si la difficulté n'est pas une valeur correcte, on choisi 1 par défaut res.Masquer(((d > 0) && (d <= 9)) ? d : 1, rand); return(res); }
//Remplie aléatoirement une grille protected Jeu[] RempAlea(Random rand) { //On charche pour quelle case on a le moins de possibilités //Pour optimiser l'algorithme on remplie toutes les cases avec une seule possibilité //Si on a rempli au moins une case, les possibilités sont diminuées donc on recommence la recherche //Enfin s'il n'y a plus que des cases avec plusieurs possibilités, on selectionne celle qui en a le moins //On créé une nouvelle grille pour chaque possibilité de la case choisie et on réappelle cette fonction pour essayer de resoudre la grille int i, j; bool valeursSimples; do { //On fait les vérifications à chaque itération car une valeur a pu être entrée alors qu'elle est erronnée //Si la grille est pleine et sans incohérance, on retourne 1 if (Possible() && Remplie()) { //Console.WriteLine("Fin normale"); return(new Jeu[] { this }); } //Si elle n'est pas cohérente, on retourne 0 //Une grille est incohérente si on a une case vide avec aucune possibilité ou plusieurs fois le même chiffre dans des carrés, colonnes ou lignes if (!Possible()) { //Console.WriteLine("Fin impossible"); return(new Jeu[0]); } //On regarde les possibilités i = 0; j = 0; //On réinitialise notre valeur de test de while() pour ne pas avoir de boucle infinie valeursSimples = false; for (int x = 0; (x < 9); x++) { for (int y = 0; (y < 9); y++) { //Si on a une case vide sans valeurs possible la grille est incohérente if (m_tab_jeu[x, y].NbDispo() == 0) { return(new Jeu[0]); } if (m_tab_jeu[x, y].NbDispo() == 1) { //Si on trouve une case avec une seule valeur possible, on rentre cette valeur bool[] temp = m_tab_jeu[x, y].Dispo(); //L'ajout d'une valeur a modifié les possibilités des cases sur ses ligne, colonne et carré //Il faut donc revérifier les cases au dessus de celle-ci, c'est pourquoi on recommence la boucle valeursSimples = true; for (int z = 0; z < 9; z++) { if (temp[z]) { Set(x, y, (z + 1)); } } } else if ((!valeursSimples) && (m_tab_jeu[x, y].NbDispo() < m_tab_jeu[i, j].NbDispo())) { //On ne continue de chercher la case avec le moins de possibilité que si aucune case ayant une seule possibilité n'a été trouvée //En effet, dans ce cas les probabilités ont changé donc la recherche est faussée pour cette boucle i = x; j = y; } } } } while (valeursSimples); //On fait en fonction des possibilités disponnibles bool[] dispo = m_tab_jeu[i, j].Dispo(); int taille = m_tab_jeu[i, j].NbDispo(); bool continuer; Jeu[] res; do { //On choisie une valeur aléatoire parmi celles disponnibles continuer = false; int valRand = rand.Next(taille); int valeur = 0; for (int k = 0; valeur == 0; k++) { if (dispo[k]) { if (valRand == 0) { valeur = k + 1; } else { valRand--; } } } //On regarde si on peut faire une grille avec cette valeur Jeu temp = new Jeu(this); temp.Set(i, j, valeur); res = temp.RempAlea(rand); if ((taille > 1) && (res.Length == 0)) { //Si aucune grille n'a pu être remplie avec cette valeur, on la supprime des possibilités et on recommence taille--; dispo[valeur - 1] = false; continuer = true; } } while (continuer); //Lorsqu'on arrive là, on a deux possibilités // > res est un tableau d'une case contenant la grille aléatoire terminée // > res est un tableau de 0 case et le problème sera traité par la fonction précédente dans la récursion return(res); }