1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
| from tkinter import *
from math import *
#verifie si un nombre est dans une case
def numberfits(table,nombre,ligne,colonne):
length = len(table)
introot = int(floor(sqrt(length)))
#l et c sont les premieres case dans un "carré" par exemple dans un sudoku 9x9 , le carrée est3x3
l = int(floor(ligne/introot))*introot
c = int(floor(colonne/introot))*introot
#verifie les carrés, les lignes et les colonnes pour les cases indiqué
for i in range(length):
#case dans un carré = nombre? case dans une ligne = nombre? case dans une colonne = nombre?
if table[l+int(floor(i/introot))][c+(i%introot)]==nombre or table[i][colonne]==nombre or table[ligne][i]==nombre:
return False
#Si le nombre n'est pas trouvé est le carré, ligne ou colonne, c'est qu'il convient, la fonction retourne true
return True
class configuation:
#Initialise les données
backtrack = [[0,0,1]] #[[ligne , colonne , nombre] commence a 0!
solutionscreen = [] #la solution affiché
solution = [] #solution pas affiché
taille = 0 #taille de la grille (nombre de cellule = taille x taille)
ligne = 0 #1ere ligne à regarder
colonne = 0 #1ere colonne a regarder
nombre = 1 #1er nombre à essayer
repetition = 0 #nombre de repetition faite
def __init__(self, master):
############################# Interface # Debut ###########################################
#Fenetre
mainframe = Frame(master)
mainframe.pack(side=TOP,expand=1, fill=X)
#Ouvrir
Button(mainframe, text = "Ouvrir une grille", command = self.askopenfile).grid(row=0, column=0, sticky=N+E+S+W)
#Resoudre
Button(mainframe, text = "Resoudre la grille", command = self.solveall).grid(row=0, column=1, sticky=N+E+S+W)
#Placement du bouton etape
butframe = Frame(mainframe)
#bouton etape
for i in range(5):
Button(butframe,text = " Etape "+str(int(10**i))+" ", command = self.dostep(int(10**i))).grid(row=i, column=0, sticky=N+E+S+W)
butframe.grid(row = 1, column = 1, rowspan = 3, sticky=N+E+S+W)
#Placement pour la résolution
self.solutionframe = Frame(mainframe, bd=4, relief=RAISED)
self.solutionframe.grid(row=1, column = 0)
############################# Interface # Fin #############################################
def askopenfile(self):
#supprimer les anciennes "traces"
if len(self.solutionscreen)>0:
for i in range(len(self.solutionscreen)):
for j in range(len(self.solutionscreen[i])):
self.solutionscreen[i][j].destroy()
#Ouvre et lit le fichier
file = filedialog.askopenfile(mode='r',title="Open input file", filetypes=[('Textfiles only','*.txt')])
table = file.readlines()
for i in range(len(table)):
table[i] = table[i].replace("\n","").split(" ")
#Crée une nouvelle grille et solution widgets
self.solutionscreen = []
for i in range(len(table)):
self.solutionscreen.append([])
for j in range(len(table[i])):
if table[i][j] == "0":
txt = ""
background = "#F0F0F0"
else:
txt = table[i][j]
background = "#DDDDFF"
solutionlabel = Label(self.solutionframe, text=txt, bd=1, relief=GROOVE, width=2, height=1, font=("Courier", 16), bg = background)
self.solutionscreen[i].append(solutionlabel)
solutionlabel.grid(row=i,column=j)
#nouvelle grille caché et solution caché (pas resolu)
self.solution = [[int(table[i][j]) for j in range(len(table[i]))] for i in range(len(table))]
#Modifie les donnés
self.taille = len(self.solution)
self.ligne = 0
self.colonne = 0
self.nombre = 1
self.backtrack = [[0,0,1]]
self.repetition = 0
def solvestep(self):
#Si ce n'est plus necessaire de regarder les cases dans la grille
if self.ligne < self.taille:
#Si le nombre est plus grand que la taille du sudoku
if self.nombre > self.taille:
#Si le nombre est plus grand que la taille du sudoku alors c'est qu'il n'y a pas de solution donc on revient en arrière
if len(self.backtrack)==0:
print("Il n'y a pas de solution!")
return False
#ajoute1
back = self.backtrack.pop()
self.solution[self.ligne][self.colonne] = 0
self.ligne = back[0]
self.colonne = back[1]
self.solution[self.ligne][self.colonne] = 0
self.nombre = back[2]+1
#affiche le backtracking a l'écran
self.updatebacktrack()
#sinon , si le nombre est inférieur a la taille du sudoku faut le tester
else:
#si le nombre est placé dans la bonne case
if self.solution[self.ligne][self.colonne]==0: #a expliquer
#si le nombre respect les conditions imposée
if numberfits(self.solution,self.nombre,self.ligne,self.colonne):
#place le nombre dans la case et met a jour le backtracking
self.solution[self.ligne][self.colonne] = self.nombre
self.backtrack.append([self.ligne,self.colonne,self.nombre])
self.nombre = 1
self.colonne += 1
#affiche le backtracking a l'écran
self.updatebacktrack()
#si il respecte pas on ajoute 1
else:
self.nombre += 1
#sinon on change de colonne
else:
self.colonne+=1
#si la colonne est plus grande que la grille , c'est qu'il faut aller a la premiere colonne de la ligne suivante
if self.colonne > self.taille-1:
self.colonne = 0
self.ligne += 1
#Si il y a encore des cases a remplir , renvoyer true
#Ajouter une réptition au nb de répétition totales
self.repetition += 1
return True
#Si toutes les cases ont été rempli , le sudoku est fini
else:
print("Nombre etape : "+str(self.repetition))
return False
#Affiche les solutions a l'écran
def updatesolution(self):
for i in range(len(self.solutionscreen)):
for j in range(len(self.solutionscreen[i])):
if self.solution[i][j]>0:
self.solutionscreen[i][j]
else:
self.solutionscreen[i][j]["bg"] = "#F0F0F0"
if self.solution[i][j] != 0:
self.solutionscreen[i][j]["text"] = str(self.solution[i][j])
else:
self.solutionscreen[i][j]["text"] = ""
if self.ligne<self.taille and self.colonne<self.taille:
self.solutionscreen[self.ligne][self.colonne]["bg"] = "#FFFFDD"
if self.solution[self.ligne][self.colonne] == 0:
self.solutionscreen[self.ligne][self.colonne]["text"] = self.nombre
#affiche le backtracking a l'écran
def updatebacktrack(self):
if len(self.backtrack)<6:
s = str(self.repetition)+": "+str(self.backtrack)
else:
s = str(self.repetition)+": [("+str(len(self.backtrack)-5)+")..."
for i in range(len(self.backtrack)-5,len(self.backtrack)):
s += ", "+str(self.backtrack[i])
s += "]"
print(s+"\n")
#boutons etapes
def dostep(self,v):
return lambda: self.step(v)
def step(self,v):
count = 0
while (count<v and self.solvestep()):
count += 1
self.updatesolution()
#bouton de résolution immédiate
def solveall(self):
while (self.solvestep()):
self.repetition += 1
self.ligne = 0
self.colonne = 0
self.updatesolution()
root = Tk()
configuation = configuation(root)
root.mainloop() |
Partager