Resoudre Sudoku,,backTracking & random!!!
Résolu/Fermé
achoura
Messages postés
35
Date d'inscription
jeudi 24 janvier 2008
Statut
Membre
Dernière intervention
5 avril 2010
-
4 mars 2008 à 18:52
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 - 6 mars 2008 à 09:49
Mahmah Messages postés 496 Date d'inscription lundi 17 septembre 2007 Statut Membre Dernière intervention 22 juin 2010 - 6 mars 2008 à 09:49
A voir également:
- Resoudre Sudoku,,backTracking & random!!!
- Sudoku gratuit - Télécharger - Jeux vidéo
- Télécharger sudoku gratuit pour mobile - Télécharger - Puzzle & Réflexion
- Fonction random c++ ✓ - Forum C++
- Générateur sudoku - Forum Logiciels
- Comment créer un sudoku sur excel ✓ - Forum Réseaux sociaux
3 réponses
Mahmah
Messages postés
496
Date d'inscription
lundi 17 septembre 2007
Statut
Membre
Dernière intervention
22 juin 2010
125
4 mars 2008 à 21:54
4 mars 2008 à 21:54
Salutations,
J'aurais un sodoku à faire, je ferais trois parties.
- Un moteur de jeu
- Une brique de résolution
- Un programme d'interface.
Le moteur se concrétiserait par un .h avec des déclarations du type:
- récupérer la valeur en i, j
- écrire une valeur en i, j
- définition d'une constante pour 'valeur inconnue'
- des codes d'erreurs
- La case i, j est elle 'read only'
- La partie est est dans une configuration gagnante.
Enfin bref, le stricte nécessaire pour jouer, pas de structure de tableau ni rien d'accessible frauduleusement.
après au choix, soit des fonctions 'Administrateur' soit des fonctions permettant d'initialiser un jeu à partir de donnée pré-remplies. (et donc un autre .h avec les fonctions de créations ou chargement depuis un fichier)
La brique de résolution a à sa disposition les fonctions normale d'un joueur.
- En random, ben... random
- Back tracking. C'est une méthode de résolution qui explore "intelligemment" toutes les solutions. Un peu à la "Et ça fait quoi si là je met un 9, après là je pourrais mettre un 5, ah non, un 4 ? non plus, Ah bah je pouvais pas mettre un 9..." J'avais utilisé ça pour un TP sur les distributeurs automatiques de billets. Je dois rendre telles sommes sachant qu'il me reste X, Y, Z billets de 100, 50, 20, 10, 5, 2, 1 et que je dois rendre obligatoirement le plus gros possible qu'il me reste et 2 du plus petit. Pour cette méthode il faut constamment avoir sous la main le maximum d'information. Ce solver devra donc maintenir de liste de ce qui peut-être joué : Dans chaque ligne, dans chaque colonne et dans chaque pâté de neuf. Avec ensuite des fonctions de recoupement des trois infos pour connaître les valeurs jouables dans chaque case. Plus exactement, on fera une fonction qui mets les trois informations à jours et une qui récupère une liste de choix possibles pour une case. Le but sera ensuite de remplir les cases vides une par une en essayant un des nombres possibles. On obtient alors une nouvelle configuration. Si le jeu n'est pas gagnant, on le résout de la même façon (ie: en appelant la même fonction récursivement)
typiquement la fonction:
- cherche une case libre
- joue la première valeur possible (idéalement c'est une nouvelle fonction "jouer( i, j valeur )" qui met à jour les infos automatiquement et appelle le moteur pour jouer réellement).
- si le jeu est résolu on mémorise la grille complétée et on a fini (On a joué le dernier coup) -> return 1
- sinon, on relance la fonction de résolution.
- Si elle réussi on a fini (On a joué un coup intermédiaire) -> return 1
- sinon, on retire le coup qu'on a joué ("Back tracking") et on recommence à l'étape 2 avec la valeur possible suivante. Si il n'y en a plus, on ne peut pas résoudre cette partie -> return 0, l'appelant essaiera un autre valeur à lui.
Au final c'est presque un "brute force" de mot de passe: on essaie avec un 'a' en premier puis le reste du mot de passe, si ça marche pas on tentera avec un 'b' etc, sauf que le choix d'une lettre élimine des choix pour après, style un mot de passe qui ne contient que 2 fois une même lettre ou un truc du genre.
La partie interface, ben... permettre au joueur d'utiliser le moteur pour jouer. Ce qui importe c'est qu'elle ne rende pas le moteur de jeu dépendant d'elle.
Après, c'est juste ce que j'en pense... (Pour une fois que ça m'arrive !!)
M.
PS:
Pour stocker les valeurs possibles finalement je ferais une matrice d'entiers qui stockeraient les valeurs possibles par des flags. Ensuite, on peut directement savoir si une valeur est jouable en faisant un ET logique sur le secteur voulu, ligne, colonne ou pâté de 9. Chacun son truc...
J'aurais un sodoku à faire, je ferais trois parties.
- Un moteur de jeu
- Une brique de résolution
- Un programme d'interface.
Le moteur se concrétiserait par un .h avec des déclarations du type:
- récupérer la valeur en i, j
- écrire une valeur en i, j
- définition d'une constante pour 'valeur inconnue'
- des codes d'erreurs
- La case i, j est elle 'read only'
- La partie est est dans une configuration gagnante.
Enfin bref, le stricte nécessaire pour jouer, pas de structure de tableau ni rien d'accessible frauduleusement.
après au choix, soit des fonctions 'Administrateur' soit des fonctions permettant d'initialiser un jeu à partir de donnée pré-remplies. (et donc un autre .h avec les fonctions de créations ou chargement depuis un fichier)
La brique de résolution a à sa disposition les fonctions normale d'un joueur.
- En random, ben... random
- Back tracking. C'est une méthode de résolution qui explore "intelligemment" toutes les solutions. Un peu à la "Et ça fait quoi si là je met un 9, après là je pourrais mettre un 5, ah non, un 4 ? non plus, Ah bah je pouvais pas mettre un 9..." J'avais utilisé ça pour un TP sur les distributeurs automatiques de billets. Je dois rendre telles sommes sachant qu'il me reste X, Y, Z billets de 100, 50, 20, 10, 5, 2, 1 et que je dois rendre obligatoirement le plus gros possible qu'il me reste et 2 du plus petit. Pour cette méthode il faut constamment avoir sous la main le maximum d'information. Ce solver devra donc maintenir de liste de ce qui peut-être joué : Dans chaque ligne, dans chaque colonne et dans chaque pâté de neuf. Avec ensuite des fonctions de recoupement des trois infos pour connaître les valeurs jouables dans chaque case. Plus exactement, on fera une fonction qui mets les trois informations à jours et une qui récupère une liste de choix possibles pour une case. Le but sera ensuite de remplir les cases vides une par une en essayant un des nombres possibles. On obtient alors une nouvelle configuration. Si le jeu n'est pas gagnant, on le résout de la même façon (ie: en appelant la même fonction récursivement)
typiquement la fonction:
- cherche une case libre
- joue la première valeur possible (idéalement c'est une nouvelle fonction "jouer( i, j valeur )" qui met à jour les infos automatiquement et appelle le moteur pour jouer réellement).
- si le jeu est résolu on mémorise la grille complétée et on a fini (On a joué le dernier coup) -> return 1
- sinon, on relance la fonction de résolution.
- Si elle réussi on a fini (On a joué un coup intermédiaire) -> return 1
- sinon, on retire le coup qu'on a joué ("Back tracking") et on recommence à l'étape 2 avec la valeur possible suivante. Si il n'y en a plus, on ne peut pas résoudre cette partie -> return 0, l'appelant essaiera un autre valeur à lui.
Au final c'est presque un "brute force" de mot de passe: on essaie avec un 'a' en premier puis le reste du mot de passe, si ça marche pas on tentera avec un 'b' etc, sauf que le choix d'une lettre élimine des choix pour après, style un mot de passe qui ne contient que 2 fois une même lettre ou un truc du genre.
La partie interface, ben... permettre au joueur d'utiliser le moteur pour jouer. Ce qui importe c'est qu'elle ne rende pas le moteur de jeu dépendant d'elle.
Après, c'est juste ce que j'en pense... (Pour une fois que ça m'arrive !!)
M.
PS:
Pour stocker les valeurs possibles finalement je ferais une matrice d'entiers qui stockeraient les valeurs possibles par des flags. Ensuite, on peut directement savoir si une valeur est jouable en faisant un ET logique sur le secteur voulu, ligne, colonne ou pâté de 9. Chacun son truc...
achoura
Messages postés
35
Date d'inscription
jeudi 24 janvier 2008
Statut
Membre
Dernière intervention
5 avril 2010
1
5 mars 2008 à 12:48
5 mars 2008 à 12:48
Merci de ton aide :)
J'ai bien compris l'idée du backtracking, et bien saisi l'algorithme grâce à ton explication.
Et puis, me reste comment mettre toutes les fonctions que j'ai citées ci-dessus en boucle afin de créer la fonction
- joue la première valeur possible (idéalement c'est une nouvelle fonction "jouer( i, j valeur )" qui met à jour les infos automatiquement et appelle le moteur pour jouer réellement).
--> Si tu veux bien, tu m'expliques comment ça se fait réellement, comment mettons-nous à jour la grille, comment activons-nous le moteur du jeu ? Le principe m'est clair, mais je ne sais pas d'où commencer et comment mettre en "évidence" mes connaissances théoriques.
Pour stocker les valeurs possibles finalement je ferais une matrice d'entiers qui stockeraient les valeurs possibles par des flags. Ensuite, on peut directement savoir si une valeur est jouable en faisant un ET logique sur le secteur voulu, ligne, colonne ou pâté de 9. Chacun son truc...
--> J'arrive pas à saisir comment utiliser les mots en gras afin de stocker les valeurs de ma matrice.
Merci encore une fois :)
J'ai bien compris l'idée du backtracking, et bien saisi l'algorithme grâce à ton explication.
Et puis, me reste comment mettre toutes les fonctions que j'ai citées ci-dessus en boucle afin de créer la fonction
"jouer( i, j,valeur )"dont tu m'as parlé et surtout comment retourner est ce oui ou non la grille Sudoku a été bien résolu ou manque d'autres cellules à rechercher.
- joue la première valeur possible (idéalement c'est une nouvelle fonction "jouer( i, j valeur )" qui met à jour les infos automatiquement et appelle le moteur pour jouer réellement).
--> Si tu veux bien, tu m'expliques comment ça se fait réellement, comment mettons-nous à jour la grille, comment activons-nous le moteur du jeu ? Le principe m'est clair, mais je ne sais pas d'où commencer et comment mettre en "évidence" mes connaissances théoriques.
Pour stocker les valeurs possibles finalement je ferais une matrice d'entiers qui stockeraient les valeurs possibles par des flags. Ensuite, on peut directement savoir si une valeur est jouable en faisant un ET logique sur le secteur voulu, ligne, colonne ou pâté de 9. Chacun son truc...
--> J'arrive pas à saisir comment utiliser les mots en gras afin de stocker les valeurs de ma matrice.
Merci encore une fois :)
Mahmah
Messages postés
496
Date d'inscription
lundi 17 septembre 2007
Statut
Membre
Dernière intervention
22 juin 2010
125
5 mars 2008 à 16:06
5 mars 2008 à 16:06
>>comment retourner est ce oui ou non la grille Sudoku a été bien résolu ou manque d'autres cellules à rechercher
Selon moi, la fonction de résolution renvoie 0 ou 1 selon qu'elle ait pu résoudre ou non la grille complète. 1 la solution a été trouvée, 0 la résolution est impossible. La fonction répond 0 lorsqu'elle n'a pas ou plus de valeurs possibles à tester pour sa case. L'appel précédent de la fonction récupère donc cette réponse, tente une autre valeur et rappelle la fonction de résolution.
Pour la réalisation d'une fonction "jouer" plus ou moins intelligente, cela implique d'avoir définie une manière de conserver les informations nécessaires. Il n'y a pas de restriction sur la manière de le faire, il faut juste pouvoir connaître à tout moment et pour chaque case toutes les valeurs qu'il est possible de jouer.
Concretement:
Il n'y a pas forcement besoin d'une fonction BT_mettreAJour mais comme je ne savais pas comment tu veux la faire...
Pour simplifier, une première version de la résolution par backtracking pourrait ne pas garder d'information supplémentaire sur les valeurs possibles. Elles sont tout simplement choisies de 1 à 9 pour chaque cases. Cela va tester des réponses stupides comme une grille avec que des '1' etc mais cela a les mêmes chances de résolution.
Moi je verrais bien ton projet en projetS.
Une librairie Sudoku (.dll, .lib, .a ou .su selon ton choix et ton OS)
Une librairie de résolution qui exploite la librairie sudoku.
Un executable d'interface utilisateur pour exploiter les deux librairies.
Pour ce qui est de mon choix de stocker les coups possibles/impossibles par des flags je trouve ça plus intéressant du point de vue programmeur. (Surtout pour ne pas se prendre la tête avec des if ou des switch.)
Le principe est simple, plutôt que de faire un tableau de 1 à 9 pour chaque case pour dire si le chiffre peut être joué, on code ça sur les bits d'un entier. (chaque bit représentant une puissance de 2)
#define _VALUE_1 0x0001
#define _VALUE_2 0x0002
#define _VALUE_3 0x0004
#define _VALUE_4 0x0008
#define _VALUE_5 0x0010
#define _VALUE_6 0x0020
#define _VALUE_7 0x0040
#define _VALUE_8 0x0080
#define _VALUE_9 0x0100
D'ailleurs, à y réfléchir, on peut ne garder les valeurs possibles que pour les lignes, les colonnes et les pâtés et non pas pour chaque cases. Ce qui fait 2 tableaux de 9 entiers et un de 3x3. Comme ça "maintenir les infos à jour" revient modifer trois entiers :)
Ensuite, pour obtenir les valeurs possibles pour une case on a une fonction plus simple:
unsigned int ValeurPossiblesCase( unsigned int uLigne, unsigned int u )
{
return ValeurPossiblesLigne( uLigne) & ValeurPossiblesColonne( uColonne ) &
ValeurPossiblesPâté( uLigne, uColonne );
}
Les autres étant des accesseurs tout simples pour les trois tableaux.
Pour savoir si on peut jouer un 5:
if ( ValeurPossiblesCase( i, j ) & _VALUE_5 )
(Petit rappel sur les opérateurs "binaire" ou "logique":
0011
& 1010 et
= 0010
(Ainsi:
65 & 17 = 1 (leur seul bit en commun)
65 & 18 = 0 etc)
0011
| 1010 ou
= 1011
0011
^ 1010 ou exclusif, xor -> L'un ou l'autre mais pas les deux.
= 1001
~ 01
= 10
(/!\ ~1 = 11111110 sur 8 bits, 1111111111111110 sur 16 bits etc)
et aussi << et >> qui ne vont pas beaucoup servir ici.
Le & nous dira donc dans la fonction ci dessus quels bits sont à 1 dans les 3 cas.
Pour mettre un bit à 1:
"1 ou X = 1"
i = i | _VALUE_5; ou
i |= _VALUE_5;
Pour mettre un bit à 0:
"0 et X = 0"
i = i & (~_VALUE_5); ou
i &= ~_VALUE_5;
Vérifier qu'un nombre ne s'écrit qu'avec un seul bit (= est une puissance de 2)
( i & ( i - 1 ) ) == 0
Enfin bref, je dévie un peu de la question...
Est-ce plus clair ?
M.
Selon moi, la fonction de résolution renvoie 0 ou 1 selon qu'elle ait pu résoudre ou non la grille complète. 1 la solution a été trouvée, 0 la résolution est impossible. La fonction répond 0 lorsqu'elle n'a pas ou plus de valeurs possibles à tester pour sa case. L'appel précédent de la fonction récupère donc cette réponse, tente une autre valeur et rappelle la fonction de résolution.
Pour la réalisation d'une fonction "jouer" plus ou moins intelligente, cela implique d'avoir définie une manière de conserver les informations nécessaires. Il n'y a pas de restriction sur la manière de le faire, il faut juste pouvoir connaître à tout moment et pour chaque case toutes les valeurs qu'il est possible de jouer.
Concretement:
#include "sudoku.h" // interface du moteur de jeu void BT_mettreAJour( unsigned int uLigne, unsigned int uColonne, unsigned int uValeur ); // BTpour Back Tracking void BT_jouer( unsigned int uLigne, unsigned int uColonne, unsigned int uValeur ) { BT_mettreAJour( uLigne, uColonne, uValeur ); sudoku_jouer( uLigne, uColonne, uValeur ); }
Il n'y a pas forcement besoin d'une fonction BT_mettreAJour mais comme je ne savais pas comment tu veux la faire...
Pour simplifier, une première version de la résolution par backtracking pourrait ne pas garder d'information supplémentaire sur les valeurs possibles. Elles sont tout simplement choisies de 1 à 9 pour chaque cases. Cela va tester des réponses stupides comme une grille avec que des '1' etc mais cela a les mêmes chances de résolution.
Moi je verrais bien ton projet en projetS.
Une librairie Sudoku (.dll, .lib, .a ou .su selon ton choix et ton OS)
Une librairie de résolution qui exploite la librairie sudoku.
Un executable d'interface utilisateur pour exploiter les deux librairies.
Pour ce qui est de mon choix de stocker les coups possibles/impossibles par des flags je trouve ça plus intéressant du point de vue programmeur. (Surtout pour ne pas se prendre la tête avec des if ou des switch.)
Le principe est simple, plutôt que de faire un tableau de 1 à 9 pour chaque case pour dire si le chiffre peut être joué, on code ça sur les bits d'un entier. (chaque bit représentant une puissance de 2)
#define _VALUE_1 0x0001
#define _VALUE_2 0x0002
#define _VALUE_3 0x0004
#define _VALUE_4 0x0008
#define _VALUE_5 0x0010
#define _VALUE_6 0x0020
#define _VALUE_7 0x0040
#define _VALUE_8 0x0080
#define _VALUE_9 0x0100
D'ailleurs, à y réfléchir, on peut ne garder les valeurs possibles que pour les lignes, les colonnes et les pâtés et non pas pour chaque cases. Ce qui fait 2 tableaux de 9 entiers et un de 3x3. Comme ça "maintenir les infos à jour" revient modifer trois entiers :)
Ensuite, pour obtenir les valeurs possibles pour une case on a une fonction plus simple:
unsigned int ValeurPossiblesCase( unsigned int uLigne, unsigned int u )
{
return ValeurPossiblesLigne( uLigne) & ValeurPossiblesColonne( uColonne ) &
ValeurPossiblesPâté( uLigne, uColonne );
}
Les autres étant des accesseurs tout simples pour les trois tableaux.
Pour savoir si on peut jouer un 5:
if ( ValeurPossiblesCase( i, j ) & _VALUE_5 )
(Petit rappel sur les opérateurs "binaire" ou "logique":
0011
& 1010 et
= 0010
(Ainsi:
65 & 17 = 1 (leur seul bit en commun)
65 & 18 = 0 etc)
0011
| 1010 ou
= 1011
0011
^ 1010 ou exclusif, xor -> L'un ou l'autre mais pas les deux.
= 1001
~ 01
= 10
(/!\ ~1 = 11111110 sur 8 bits, 1111111111111110 sur 16 bits etc)
et aussi << et >> qui ne vont pas beaucoup servir ici.
Le & nous dira donc dans la fonction ci dessus quels bits sont à 1 dans les 3 cas.
Pour mettre un bit à 1:
"1 ou X = 1"
i = i | _VALUE_5; ou
i |= _VALUE_5;
Pour mettre un bit à 0:
"0 et X = 0"
i = i & (~_VALUE_5); ou
i &= ~_VALUE_5;
Vérifier qu'un nombre ne s'écrit qu'avec un seul bit (= est une puissance de 2)
( i & ( i - 1 ) ) == 0
Enfin bref, je dévie un peu de la question...
Est-ce plus clair ?
M.
achoura
Messages postés
35
Date d'inscription
jeudi 24 janvier 2008
Statut
Membre
Dernière intervention
5 avril 2010
1
6 mars 2008 à 09:21
6 mars 2008 à 09:21
Dear Mahmah
votre aide ,,, vraiment ça fait plaisir de donner autant de temps pour me faire expliquer que mon professeur !!! c'est paradoxale non!!
j'ai une meilleur vision pour le problème grâce à vous et je mettra tous ça en œuvre des maintenant,,,je vous répondra en fonction des mes avances,,,,,,merci autre fois, merci merci merci
votre aide ,,, vraiment ça fait plaisir de donner autant de temps pour me faire expliquer que mon professeur !!! c'est paradoxale non!!
j'ai une meilleur vision pour le problème grâce à vous et je mettra tous ça en œuvre des maintenant,,,je vous répondra en fonction des mes avances,,,,,,merci autre fois, merci merci merci
Mahmah
Messages postés
496
Date d'inscription
lundi 17 septembre 2007
Statut
Membre
Dernière intervention
22 juin 2010
125
>
achoura
Messages postés
35
Date d'inscription
jeudi 24 janvier 2008
Statut
Membre
Dernière intervention
5 avril 2010
6 mars 2008 à 09:49
6 mars 2008 à 09:49
Y a pas de soucis ^^
M.
M.