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 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
| Option Explicit
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Nécessite de cocher les deux références suivantes (Menu Outils/Références)
'Microsoft Forms 2.0 Object Library
'Microsoft Visual Basic For Applications Extensibility 5.3
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
'Variables publiques
Public maForm As Object 'Userform
Public Fram As MSForms.Frame 'Frame = conteneur des boutons
Public Dico As Object 'Objet dictionary
Public DicoParent As Object 'Objet dictionary
Public TypeObjet As String 'Type d'objet (Ici, soit "Userform", soit "Frame")
Public Mine As Boolean 'Propriété Mine si True = bouton piégé
Public Decouverte As Boolean 'Propriété Découverte si True = "terrain(bouton) déminé"
'variables privées
Private Nom As String 'Nom => permet la construction et la destruction de l'userform
Private cVoisins() As cDemineur 'propriété sous forme de tableau listant les boutons voisins
'variables publiques "événementielles"
Public WithEvents Bouton As MSForms.CommandButton 'Bouton
'constantes
Private Const LARG_BTN As Byte = 18 'taille des boutons
Private Const MIN_LIGN As Byte = 7 'minimum de lignes
Private Const MAX_LIGN As Byte = 30 - MIN_LIGN 'maximum de lignes
Private Const MIN_COL As Byte = 7 'minimum de colonnes
Private Const MAX_COL As Byte = 40 - MIN_COL 'maximum de colonnes
Private Const POURCENT_SIMPLE As Byte = 10 '%age de mines en mode facile
Private Const POURCENT_MEDIUM As Byte = 2 * POURCENT_SIMPLE '%age de mines en mode médium
Private Const POURCENT_HARD As Byte = 3 * POURCENT_SIMPLE '%age de mines en mode difficile
Private Const COUL_MINE As Long = &H188B0 'couleur des boutons minés (pour les dévoiler)
Private Const COUL_BOUTON As Long = &H8000000F 'couleur des boutons
Private Const COUL_MINE_POSSIBLE As Long = &H80FF& 'couleur si bouton possiblement miné (bouton affiche ?) => doute
Private Const COUL_MINE_PROB As Long = &H8080FF 'couleur si bouton probablement miné (bouton affiche !) => attention danger
Property Get Voisins() As cDemineur()
'propriété Voisins en Lecture
Voisins = cVoisins
End Property
Property Let Voisins(ByRef nouvVoisins() As cDemineur)
'propriété Voisins en Ecriture
cVoisins = nouvVoisins
End Property
Private Sub Class_Initialize()
'constructeur de la classe cDémineur
Set Dico = CreateObject("Scripting.dictionary")
End Sub
Public Sub Show(ByRef Difficult As Long, Optional ModeTriche As Boolean = False)
'Méthode Show : permet l'affichage de l'Userform
On Error GoTo ErreurParametresMacros 'vérification si "accès approuvé au modèle objet du projet VBA" est cochée dans les options Excel
With ThisWorkbook.VBProject: End With
Dim Lign As Long, Col As Long, NbLignes As Long, NbColonnes As Long
Dim NbMines As Long, MineAdress() As String, CptMine As Long
Randomize Timer 'initialisation générateur de nombres aléatoires
NbLignes = Int(MAX_LIGN * Rnd) + MIN_LIGN 'Nombre de lignes de boutons
NbColonnes = Int(MAX_COL * Rnd) + MIN_COL 'Nombre de colonnes de boutons
Select Case Difficult 'Nombre de Mines selon la difficulté choisie
Case 0: Difficult = POURCENT_SIMPLE
Case 1: Difficult = POURCENT_MEDIUM
Case 2: Difficult = POURCENT_HARD
End Select
NbMines = (NbLignes * NbColonnes) * Difficult \ 100
ReDim MineAdress(NbMines)
For CptMine = 1 To NbMines 'coordonnées des Mines (Colonne & "-" & Ligne)
MineAdress(CptMine) = Int(NbColonnes * Rnd) + 1 & "-" & Int(NbLignes * Rnd) + 1
Next
Call Creation_Usf("Démineur", (NbColonnes * LARG_BTN) + 5, (NbLignes * LARG_BTN) + 22) 'création Userfom
Call Nouveau_Frame("Fram1", "", NbColonnes * LARG_BTN, NbLignes * LARG_BTN) 'création Frame
For Lign = 1 To NbLignes 'création Boutons
For Col = 1 To NbColonnes
Call Dico("Fram1").Nouveau_Bouton(Col & "-" & Lign, "", LARG_BTN * (Col - 1), LARG_BTN * (Lign - 1), EstDans(Col & "-" & Lign, MineAdress), ModeTriche)
Set Dico("Fram1").Dico(Col & "-" & Lign).DicoParent = Dico("Fram1").Dico
Next Col
Next Lign
maForm.Show
Exit Sub
ErreurParametresMacros:
MsgBox "Veuillez vérifier que vous avez approuvé l'accès au modèle objet du projet VBA."
End Sub
Private Sub Creation_Usf(Titre As String, Largeur As Double, Hauteur As Double)
'création Userfom
TypeObjet = "UserForm"
Set maForm = ThisWorkbook.VBProject.VBComponents.Add(3)
Nom = maForm.Name
VBA.UserForms.Add (Nom)
Set maForm = UserForms(UserForms.Count - 1)
With maForm
.Caption = Titre
.Width = Largeur
.Height = Hauteur
End With
End Sub
Public Sub Nouveau_Frame(monNom As String, Titre As String, Largeur As Double, Hauteur As Double)
'création Frame
If Dico.Exists(monNom) = True Then Exit Sub
Dim maClass As New cDemineur
Select Case TypeObjet
Case "UserForm": Set maClass.Fram = maForm.Controls.Add("forms.frame.1")
Case "Frame": Set maClass.Fram = Fram.Controls.Add("forms.frm.1")
End Select
maClass.TypeObjet = "Frame"
Set maClass.maForm = maForm
With maClass.Fram
.Name = monNom
.Caption = Titre
.Move 0, 0, Largeur, Hauteur
End With
Dico.Add monNom, maClass
Set maClass = Nothing
End Sub
Public Sub Nouveau_Bouton(monNom As String, Titre As String, Gauche As Double, Haut As Double, boolMine As Boolean, Optional ModeTriche As Boolean)
'création Boutons
If Dico.Exists(monNom) = True Then Exit Sub
Dim maClass As New cDemineur
Select Case TypeObjet
Case "UserForm": Set maClass.Bouton = maForm.Controls.Add("forms.CommandButton.1")
Case "Frame": Set maClass.Bouton = Fram.Controls.Add("forms.CommandButton.1")
End Select
Set maClass.maForm = maForm
maClass.Mine = boolMine
With maClass.Bouton
.Name = monNom
.Caption = Titre
.Move Gauche, Haut, LARG_BTN, LARG_BTN
If ModeTriche Then
If boolMine Then .BackColor = COUL_MINE Else .BackColor = COUL_BOUTON
Else
.BackColor = COUL_BOUTON
End If
End With
Dico.Add monNom, maClass
Set maClass = Nothing
End Sub
Private Function EstDans(adresse As String, Tb) As Boolean
'fonction de recherche d'une valeur dans une var tableau
Dim i As Long
For i = 0 To UBound(Tb)
If Tb(i) = adresse Then EstDans = True: Exit Function
Next i
End Function
Private Sub Bouton_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
'Procédure événementielle lors de l'appui, à l'aide d'un des 2 boutons de la souris, sur un Bouton de l'Userform
If Button = XlMouseButton.xlSecondaryButton Then 'clic droit
Select Case Bouton.Caption 'selon le Caption du bouton 4 possibilités
Case "": Bouton.Caption = "!": Bouton.BackColor = COUL_MINE_PROB 'si caption est vide : on affiche ! (= attention danger)
Case "!": Bouton.Caption = "?": Bouton.BackColor = COUL_MINE_POSSIBLE 'si caption est ! : on affiche ? (= doute)
Case "?": Bouton.Caption = "": Bouton.BackColor = COUL_BOUTON 'si caption est ? : on affiche rien (= levée du doute)
Case Else: 'sinon (caption = chiffre (Nbre de mines voisines)) On ne fait rien
End Select
ElseIf Button = XlMouseButton.xlPrimaryButton Then 'clic gauche
If DicoParent.Item(Bouton.Name).Mine Then 'si bouton miné
Call Affiche_Toutes_Mines 'affichage de toutes les mines
MsgBox "Partie perdue" 'message
maForm.Hide 'on quitte
Else 'si bouton non miné
Bouton.BackColor = COUL_BOUTON 'remet la couleur par défaut en cas de clic droit précédent
Dim maClass As cDemineur 'on appelle la procédure de déminage
Set maClass = DicoParent.Item(Bouton.Name) 'procédure récursive de propagation
Call Demine(maClass) 'des boutons dont les voisins de sont pas des mines
End If
End If
If Partie_Gagnee Then
Call Affiche_Toutes_Mines
MsgBox "Partie Gagnée."
maForm.Hide
End If
End Sub
Private Sub Affiche_Toutes_Mines()
'En cas de partie perdue, colore tous les boutons minés
Dim cle As Variant
For Each cle In DicoParent.Keys
If DicoParent.Item(cle).Mine Then DicoParent.Item(cle).Bouton.BackColor = COUL_MINE
Next
End Sub
Private Sub Demine(Cl As cDemineur)
'procédure récursive de propagation de la découverte des boutons non minés
Dim NbMines As Integer
NbMines = CompteMines(Cl.Bouton.Name)
If NbMines > 0 Then
Cl.Bouton.Caption = NbMines
Cl.Decouverte = True
Cl.Bouton.BackColor = COUL_BOUTON
Else
If Cl.Decouverte = False Then
Cl.Decouverte = True
Cl.Bouton.Visible = False
Quels_Voisins Cl
Dim Tb() As cDemineur, i As Integer
Tb = Cl.Voisins
For i = 0 To UBound(Tb)
Demine Tb(i)
Next
End If
End If
End Sub
Private Function CompteMines(Bout As String) As Integer
'fonction comptant les mines contenues dans les boutons voisins
Dim i As Integer, j As Integer, Col As Integer, Lig As Integer
Dim maClass As cDemineur
For i = -1 To 1
For j = -1 To 1
Col = CInt(Split(Bout, "-")(0)) + i
Lig = CInt(Split(Bout, "-")(1)) + j
If DicoParent.Exists(Col & "-" & Lig) Then
Set maClass = DicoParent.Item(Col & "-" & Lig)
If maClass.Mine Then CompteMines = CompteMines + 1
End If
Next j
Next i
End Function
Private Sub Quels_Voisins(Cl As cDemineur)
'procédure affectant, à la propriété Voisins() d'un bouton, la liste des boutons qui l'entourent
Dim i As Integer, j As Integer, Col As Integer, Lig As Integer
Dim maClass As cDemineur, ListeVoisins() As cDemineur, cpt As Byte
For i = -1 To 1
For j = -1 To 1
Col = CInt(Split(Cl.Bouton.Name, "-")(0)) + i
Lig = CInt(Split(Cl.Bouton.Name, "-")(1)) + j
If DicoParent.Exists(Col & "-" & Lig) And Cl.Bouton.Name <> Col & "-" & Lig Then
Set maClass = DicoParent.Item(Col & "-" & Lig)
ReDim Preserve ListeVoisins(cpt)
Set ListeVoisins(cpt) = maClass
cpt = cpt + 1
End If
Next j
Next i
Cl.Voisins = ListeVoisins
End Sub
Private Function Partie_Gagnee() As Boolean
Dim cle As Variant
For Each cle In DicoParent.Keys
If DicoParent.Item(cle).Decouverte = False And DicoParent.Item(cle).Mine = False Then Partie_Gagnee = False: Exit Function
Next
Partie_Gagnee = True
End Function
Private Sub Class_Terminate()
'destructeur de la classe cDémineur
Dim VBComp As VBComponent
Set Dico = Nothing
If Nom <> "" Then
Set VBComp = ThisWorkbook.VBProject.VBComponents(Nom)
ThisWorkbook.VBProject.VBComponents.Remove VBComp
End If
End Sub |
Partager