sudoku — Solveur de sudoku¶
Usage¶
Un solveur de Sudoku
usage: sudoku [-h] [--version] [-f {short,long}] [-j PROCESSUS] [-a ACTIONS]
[fichier]
Positional Arguments¶
- fichier
File containing the starting array. If absent, array is read from standard input.
Default:
<_io.TextIOWrapper name='<stdin>' mode='r' encoding='utf-8'>
Named Arguments¶
- --version
show program’s version number and exit
- -f, --format
Possible choices: short, long
Input file format: « short » (…232….4….1) or long (lines of « … 2 3 »).
Default:
'long'
- -j, --jobs
Number of jobs to run simultaneously. Default is the number of cpu cores.
Default:
2
- -a, --actions
Action to perform on solved arrays. This argument is a combination of letters “p” (print solution), “c” (count solutions), and “t” (time solutions).
Default:
'p'
Algorithme¶
Autres algorithmes¶
Je suis loin d’être le seul à m’être intéressé à ce problème, et je suis loin de proposer la meilleure solution. Par exemple, Peter Norvig a réalisé un solveur de sudoku bien plus rapide que le mien. En revanche, il ne peut résoudre que des grilles de 9x9.
Représentation des données¶
Grille¶
Une grille de sudoku est représentée par l’objet
Grille
, dont un de ses attributs est un
tableau (sous la forme d’une liste de listes) des valeurs de ses cellules
(None si elles sont inconnues). Elle contient également un ensemble des
contraintes connues sur la grille, dans l’attribut Grille.contraintes
.
Contraintes¶
Les contraintes sur la grille sont stockées de deux manières différentes.
Celles déjà prises en compte sont stockées dans les attributs
Grille.possible_{case,ligne,colonne,bloc}
:
- Grille.possible_case¶
L’élément possible_case[x][y] contient l’ensemble des entiers possibles pour la case (x, y). Lorsque cette liste ne contient qu’un élément, cet élément peut être attribué à la case.
- Grille.possible_ligne¶
L’élément possible_ligne[y][valeur] contient l’ensemble des absisses possibles pour la valeur valeur dans la ligne d’abscisse y. Lorsque cette liste ne contient qu’un élément, valeur peut être attribué à la case (x, y).
- Grille.possible_colonne¶
Même fonction que
Grille.possible_ligne
, pour les colonnes.
- Grille.possible_bloc¶
L’élément possible_bloc[bloc][valeur] contient l’ensemble des indices possibles pour valeur dans le bloc d’indice bloc (un bloc désigne ici une carré de la grille devant contenir tous les nombres de 1 à \(taille^2\)). Lorsque cette liste ne contient qu’un élément, valeur peut être attribué à la case d’indice correspondant dans le bloc.
Les contraintes en cours de traitement, elles, sont stockées dans l’attribut
Grille.contraintes
. Cette liste contient une liste de contraintes à
vérifier. La raison de cette liste (pourquoi les contraintes ne sont-elles pas
reportées immédiatement dans les attributs Grille.possible_*
?) est
expliquée dans la partie suivante.
Recherche de solutions¶
Une file contient l’ensemble des grilles incomplètes à étudier. La résolution,
mise en œuvre dans la fonction solveur()
consiste à prendre une grille dans cette liste, et la remplir autant que
possible sans faire d’hypothèses (avec la méthode Grille.remplit()
).
Si la grille est remblie, une solution a été trouvée. Sinon, à partir de la
case qui a le moins de possibilités, mettre dans la file les grilles
correspondant aux hypothèses sur cette case.
1def solveur(candidats, fonction):
2 """Essaye de résoudre l'ensemble des candidats.
3
4 Boucle infinie qui prend des candidats dans la queue, tente de les
5 resoudre, et applique la fonction quand ils sont resolu.
6 """
7 while True:
8 grille = candidats.get()
9 grille.remplit()
10 if grille.resolu:
11 fonction(grille)
12 elif grille.impossible:
13 pass
14 else:
15 (x, y) = grille.cherche_plus_proche()
16 for valeur in grille.possible_case[x][y]:
17 copie = grille.copy()
18 copie.affecte((x, y), valeur)
19 candidats.put(copie)
20 candidats.task_done()
La fonction Grille.remplit()
prend l’ensemble des contraintes en
attente de traitement (l’attribut Grille.contraintes
), et applique les
effets sur la grille. Ces opérations pouvant faire naitre de nouvelles
contraintes, la taille de la boucle n’est pas connue à l’avance. Lorsque la
liste des contraintes est vide, c’est que la grille n’est pas possible, qu’elle
est résolue, ou qu’il est impossible de la remplir davantage sans faire
d’hypothèses.
1 def remplit(self):
2 """Remplit autant que possible la grille, avec les contraintes connues.
3
4 S'arrete quand plus rien ne peut etre deduit.
5 """
6 while self.contraintes and not self.impossible:
7 # Extrait une contrainte
8 contrainte = self.contraintes.pop()
9 # Applique la contrainte
10 contrainte.fonction(*contrainte.argument)