Sudoku
Publié : mar. 16/sept./2008 23:06
J'ai commencé un petit programme pour résoudre des grilles de Sudoku en automatique (sale tricheur)
Bon, pour le moment, ca ne résoud que des grilles faciles.
Mais sinon, l'interface marche très bien et peut vous permettre de rentrer une grille et de jouer avec, sans avoir a se faire ... avec un crayon et une gomme.
En 3 heures de programmation, c'est déjà bien d'en être la
Je vous laisse découvrir.
Il ne me reste plus qu'à amélioré le système de résolution automatique.
Dès que ca avance, je vous tiendrais au courant
Bon, pour le moment, ca ne résoud que des grilles faciles.
Mais sinon, l'interface marche très bien et peut vous permettre de rentrer une grille et de jouer avec, sans avoir a se faire ... avec un crayon et une gomme.
En 3 heures de programmation, c'est déjà bien d'en être la

Je vous laisse découvrir.
Il ne me reste plus qu'à amélioré le système de résolution automatique.
Dès que ca avance, je vous tiendrais au courant
Code : Tout sélectionner
; Auteur : Le Soldat Inconnu
; Version de PB : 4
;
; Explication du programme :
; Pour résoudre une grille de Sudoku
Structure Structure_Case
Valeur.s
Gadget.l
Image.l
Original.l
EndStructure
Enumeration
#Rechercher
#Sauver
#Charger
#Vider
EndEnumeration
#Case_Nb = 9
#Case_T = 11 * 3 + 2
#Case_Separation = 3
#Case_Espace = 4
#Case_ValeurDefaut = "123456789"
#Bouton_H = 25
Global Police_Gras, Police_Normal
Global Dim Sudoku.Structure_Case(#Case_Nb - 1, #Case_Nb - 1)
Procedure.l LoadWindowFont(Bold = -1, Italic = -1, UnderLine = -1)
Protected ncm.NONCLIENTMETRICS
ncm\cbSize = SizeOf(NONCLIENTMETRICS)
SystemParametersInfo_(#SPI_GETNONCLIENTMETRICS, SizeOf(NONCLIENTMETRICS), @ncm, 0)
If Bold = 0
ncm\lfMessageFont\lfWeight = 0
ElseIf Bold = 1
ncm\lfMessageFont\lfWeight = 700
EndIf
If Italic = 0
ncm\lfMessageFont\lfItalic = 0
ElseIf Italic = 1
ncm\lfMessageFont\lfItalic = 1
EndIf
If UnderLine = 0
ncm\lfMessageFont\lfUnderline = 0
ElseIf UnderLine = 1
ncm\lfMessageFont\lfUnderline = 1
EndIf
ProcedureReturn CreateFontIndirect_(@ncm\lfMessageFont)
EndProcedure
Procedure DessinCase(x, y)
; Analyse de nombre de case à créer
; On va dessiner une matrice SousCase_Nb*SousCase_Nb
SousCase_Nb = Round(Sqr(Len(#Case_ValeurDefaut)), #PB_Round_Up)
SousCase_T = #Case_T / SousCase_Nb
StartDrawing(ImageOutput(Sudoku(x, y)\Image))
Box(0, 0, #Case_T, #Case_T, $FFFFFF)
DrawingMode(#PB_2DDrawing_Transparent | #PB_2DDrawing_Outlined)
Index = 0
For nn = 0 To SousCase_Nb - 1
For n = 0 To SousCase_Nb - 1
Index + 1
Texte.s = Mid(#Case_ValeurDefaut, Index, 1)
If Texte
Box(n * SousCase_T, nn * SousCase_T, SousCase_T + 1, SousCase_T + 1, $E0E0E0)
If FindString(Sudoku(x, y)\Valeur, Texte, 1)
If Len(Sudoku(x, y)\Valeur) = 1
If Sudoku(x, y)\Original
Couleur = $0080E0
Else
Couleur = $000000
EndIf
DrawingFont(Police_Gras)
Else
Couleur = $606060
DrawingFont(Police_Normal)
EndIf
Else
Couleur = $E0E0E0
DrawingFont(Police_Normal)
EndIf
DrawText(n * SousCase_T + (SousCase_T - TextWidth(Texte)) / 2, nn * SousCase_T + (SousCase_T - TextHeight(Texte)) / 2, Texte, Couleur)
EndIf
Next
Next
Box(0, 0, SousCase_Nb * SousCase_T + 1, SousCase_Nb * SousCase_T + 1, $000000)
StopDrawing()
; On affiche l'image
SetGadgetState(Sudoku(x, y)\Gadget, ImageID(Sudoku(x, y)\Image))
EndProcedure
Procedure CaseSelection(x, y)
; Analyse de nombre de case
; On va dessiner une matrice SousCase_Nb*SousCase_Nb
SousCase_Nb = Round(Sqr(Len(#Case_ValeurDefaut)), #PB_Round_Up)
SousCase_T = #Case_T / SousCase_Nb
; Position du clic sur l'image
Clic_X = WindowMouseX(0) - GadgetX(Sudoku(x, y)\Gadget)
Clic_Y = WindowMouseY(0) - GadgetY(Sudoku(x, y)\Gadget)
; Case cliquée
SousCase_X = Clic_X / SousCase_T
SousCase_Y = Clic_Y / SousCase_T
; Valeur cliquée
Index = SousCase_Y * SousCase_Nb + SousCase_X + 1
Valeur.s = Mid(#Case_ValeurDefaut, Index, 1)
If FindString(Sudoku(x, y)\Valeur, Valeur, 1) ; Si le chiffre cliqué exite déjà
If Len(Sudoku(x, y)\Valeur) = Len(#Case_ValeurDefaut) ; Si tous les chiffres existaient déjà, on ne garde que le chiffre cliqué
Sudoku(x, y)\Valeur = Valeur
Sudoku(x, y)\Original = 1
Else ; Sinon, on retire le chiffre cliqué
Sudoku(x, y)\Valeur = ReplaceString(Sudoku(x, y)\Valeur, Valeur, "")
If Sudoku(x, y)\Valeur = "" ; Si on a retiré tous les chiffres, on les remet tous
Sudoku(x, y)\Valeur = #Case_ValeurDefaut
Sudoku(x, y)\Original = 0
EndIf
EndIf
Else
; tout ce bazar, c'est pour garder les numéros dans le bon ordre
For n = 1 To Len(Sudoku(x, y)\Valeur)
Texte.s = Mid(Sudoku(x, y)\Valeur, n, 1)
Emplacement = FindString(#Case_ValeurDefaut, Texte, 1)
If Emplacement > Index
Sudoku(x, y)\Valeur = Left(Sudoku(x, y)\Valeur, n - 1) + Valeur + Right(Sudoku(x, y)\Valeur, Len(Sudoku(x, y)\Valeur) - n + 1)
Break
EndIf
If n = Len(Sudoku(x, y)\Valeur)
Sudoku(x, y)\Valeur + Valeur
Break
EndIf
Next
Sudoku(x, y)\Original = 0
EndIf
DessinCase(x, y)
EndProcedure
Procedure Sauver()
Grille.s = SaveFileRequester("Sudoku - Sauver", GetCurrentDirectory() + "Grille " + FormatDate("%yyyy-%mm-%dd %hh-%ii-%ss", Date()) + ".sudoku", "Grille sudoku|*.sudoku", 0)
If Grille
If LCase(GetExtensionPart(Grille)) <> "sudoku"
Grille + ".sudoku"
EndIf
If CheckFilename(GetFilePart(Grille))
If CreatePreferences(Grille)
For x = 0 To #Case_Nb - 1
For y = 0 To #Case_Nb - 1
WritePreferenceString(Str(x) + "x" + Str(y), Sudoku(x, y)\Valeur)
WritePreferenceLong(Str(x) + "x" + Str(y) + " Original", Sudoku(x, y)\Original)
Next
Next
ClosePreferences()
Else
MessageRequester("Sudoku - Erreur", "Impossible de créer la sauvegarde")
EndIf
Else
MessageRequester("Sudoku - Erreur", "Nom de fichier incorrect")
EndIf
EndIf
EndProcedure
Procedure Charger()
Grille.s = OpenFileRequester("Sudoku - Sauver", GetCurrentDirectory(), "Grille sudoku|*.sudoku", 0)
If Grille
OpenPreferences(Grille)
For x = 0 To #Case_Nb - 1
For y = 0 To #Case_Nb - 1
Sudoku(x, y)\Valeur = ReadPreferenceString(Str(x) + "x" + Str(y), "")
If Len(Sudoku(x, y)\Valeur) = 1
Sudoku(x, y)\Original = 1
Else
Sudoku(x, y)\Original = 0
EndIf
Sudoku(x, y)\Original = ReadPreferenceLong(Str(x) + "x" + Str(y) + " Original", Sudoku(x, y)\Original)
DessinCase(x, y)
Next
Next
ClosePreferences()
EndIf
EndProcedure
Procedure Vider()
If MessageRequester("Sudoku - Effacer", "Effacer la grille ?", #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
For x = 0 To #Case_Nb - 1
For y = 0 To #Case_Nb - 1
Sudoku(x, y)\Valeur = #Case_ValeurDefaut
DessinCase(x, y)
; SetGadgetText(Sudoku(x, y)\Gadget, "")
; SetGadgetFont(Sudoku(x, y)\Gadget, Police_Normal)
Next
Next
EndIf
EndProcedure
Procedure Solution()
Protected Nouveau.s, Evolution.l
; On va traiter par passe sucessive les cases jusqu'à ce qu'il ne reste plus qu'une seule possibilité dans chaque case
Repeat
Evolution = 0
;- Les lignes verticales
; on retire tous les cas impossible. si on a une case valide, on retire le chiffre de cette case à toutes les autres case de la ligne
For x = 0 To #Case_Nb - 1
For y = 0 To #Case_Nb - 1
If Len(Sudoku(x, y)\Valeur) = 1 ; Si case valide
For nn = 0 To #Case_Nb - 1
If nn <> y
Nouveau = ReplaceString(Sudoku(x, nn)\Valeur, Sudoku(x, y)\Valeur, "")
If Sudoku(x, nn)\Valeur <> Nouveau
Sudoku(x, nn)\Valeur = Nouveau
Evolution = 1
EndIf
EndIf
Next
EndIf
Next
Next
;- Les lignes horizontales
; on retire tous les cas impossible. si on a une case valide, on retire le chiffre de cette case à toutes les autres case de la ligne
For y = 0 To #Case_Nb - 1
For x = 0 To #Case_Nb - 1
If Len(Sudoku(x, y)\Valeur) = 1 ; Si case valide
For n = 0 To #Case_Nb - 1
If n <> x
Nouveau = ReplaceString(Sudoku(n, y)\Valeur, Sudoku(x, y)\Valeur, "")
If Sudoku(n, y)\Valeur <> Nouveau
Sudoku(n, y)\Valeur = Nouveau
Evolution = 1
EndIf
EndIf
Next
EndIf
Next
Next
;- Les carrés
; on retire tous les cas impossible. si on a une case valide, on retire le chiffre de cette case à toutes les autres case du carré
For x = 0 To #Case_Nb - 1 Step #Case_Separation
For y = 0 To #Case_Nb - 1 Step #Case_Separation
For xx = x To x + #Case_Separation - 1
For yy = y To y + #Case_Separation - 1
If Len(Sudoku(xx, yy)\Valeur) = 1 ; Si case valide
For n = x To x + #Case_Separation - 1
For nn = y To y + #Case_Separation - 1
If n <> xx And nn <> yy
Nouveau = ReplaceString(Sudoku(n, nn)\Valeur, Sudoku(xx, yy)\Valeur, "")
If Sudoku(n, nn)\Valeur <> Nouveau
Sudoku(n, nn)\Valeur = Nouveau
Evolution = 1
EndIf
EndIf
Next
Next
EndIf
Next
Next
Next
Next
;- On affiche
For x = 0 To #Case_Nb - 1
For y = 0 To #Case_Nb - 1
DessinCase(x, y)
Next
Next
Until Evolution = 0
MessageBeep_(#MB_ICONEXCLAMATION)
EndProcedure
; Création de la fenêtre et de la GadgetList
If OpenWindow(0, 0, 0, #Case_T * #Case_Nb + #Case_Nb / #Case_Separation * #Case_Espace + #Case_Espace, #Case_T * #Case_Nb + #Case_Nb / #Case_Separation * #Case_Espace + #Case_Espace * 2 + #Bouton_H + MenuHeight(), "Sudoku", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget) = 0 Or CreateGadgetList(WindowID(0)) = 0
End
EndIf
StickyWindow(0, 1)
SetWindowColor(0, $FFFFFF)
If CreateMenu(0, WindowID(0))
MenuTitle("Grille")
MenuItem(#Vider, "Effacer la grille")
MenuBar()
MenuItem(#Sauver, "Sauver la grille")
MenuItem(#Charger, "Charger une grille")
EndIf
Police_Normal = LoadWindowFont()
Police_Gras = LoadWindowFont(1)
x_Compteur = 0
x_Decalage = #Case_Espace
For x = 0 To #Case_Nb - 1
x_Compteur + 1
If x_Compteur > #Case_Separation
x_Compteur = 1
x_Decalage + #Case_Espace
EndIf
y_Compteur = 0
y_Decalage = #Case_Espace
For y = 0 To #Case_Nb - 1
y_Compteur + 1
If y_Compteur > #Case_Separation
y_Compteur = 1
y_Decalage + #Case_Espace
EndIf
Sudoku(x, y)\Valeur = #Case_ValeurDefaut
Sudoku(x, y)\Image = CreateImage(#PB_Any, #Case_T, #Case_T)
Sudoku(x, y)\Gadget = ImageGadget(#PB_Any, x * #Case_T + x_Decalage, y * #Case_T + y_Decalage, #Case_T, #Case_T, ImageID(Sudoku(x, y)\Image))
DessinCase(x, y)
Next
Next
ButtonGadget(#Rechercher, #Case_Espace, #Case_T * #Case_Nb + #Case_Nb / #Case_Separation * #Case_Espace + #Case_Espace, #Case_T * #Case_Nb + #Case_Nb / #Case_Separation * #Case_Espace - #Case_Espace, #Bouton_H, "Rechercher la solution")
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Menu
Select EventMenu()
Case #Sauver
Sauver()
Case #Charger
Charger()
Case #Vider
Vider()
EndSelect
Case #PB_Event_Gadget
Select EventGadget() ; Gadgets
Case #Rechercher
; On recherche la solution
Solution()
Default ; Quand on édite une case
For x = 0 To #Case_Nb - 1
For y = 0 To #Case_Nb - 1
If EventGadget() = Sudoku(x, y)\Gadget
CaseSelection(x, y)
EndIf
Next
Next
EndSelect
EndSelect
Until Event = #PB_Event_CloseWindow
; On supprime les polices chargées
If Police_Normal
DeleteObject_(Police_Normal)
EndIf
If Police_Gras
DeleteObject_(Police_Gras)
EndIf
End