Voici ma version du démineur : https://workshop.numworks.com/python/ar ... n/demineur
Cette version est destinée à l’entretien et à la compréhension du code, donc elle est un peu lourde (6ko) et ne fonctionne que sur Omega. Le script peut donc aisément être réduit, ne serait que par la suppression des commentaires et de la documentation interne au code, mais j'attends l'ajout des dernières fonctionnalités avant de l'optimiser.
Deux fonctionnalités non présentes sur la version de @cent20 :
- impossible de perdre sur le premier clic,
- possibilité de relancer une partie facilement.
Des idées d'améliorations :
- modification du critère de victoire (actuellement pas parfait),
- affichages de toutes les bombes lors d'un échec,
- une fonction qui révèle toutes les cases mitoyennes non découvertes si l'on clique sur un chiffre (évite les clics répétitifs).
Vos commentaires/critiques sont bienvenus.
- Code: Select all
# Démineur - Mars 2020
# Arthur Jacquin - arthur062318@gmail.com
# Commentaires
""" Touches
Naviguation : flèches
Révéler une case : OK
Placer un drapeau : Flèche retour
Quitter : Accueil + spam Flèche retour
"""
""" Valeurs des cases de la matrice
| Contexte | Bombe | Non bombe |
| Non découvert | 9 | 0 à 8 (nombre de bombes adjacentes) |
| Découvert | 19 | 10 à 18 (nombre de bombes adjacentes + 10) |
| Couvert par un drapeau | 29 | 20 à 28 (nombre de bombes adjacentes + 20) |
"""
""" Améliorations possibles
Gestion des variables
Critère de réussite
Fonction qui découvre toutes les cases non découvertes à côté si chiffre cliqué
Affichage des bombes lors d'un échec
"""
""" Archives de dévellopement
def aff():
to_print = ""
for y in Yset:
for x in Xset: to_print += (" "+str(MAT[y][x]))[-3:]
to_print += "\n"
print(to_print)
"""
# Initialisation
# Modules
from kandinsky import set_pixel, draw_string, fill_rect
from random import choice
from ion import keydown
# Couleurs
imp = (0,0,0) # Titre, curseur, contenu
grey = (70,70,70) # Texte, bordures
light = (220,220,220) # Inconnus
none = (255,255,255) # Vide, background
curs = (0,0,255) # Curseur
# Paramètres et outils
Xdomain = range(16)
Ydomain = range(10)
mines = 20
prox = [[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]]
buttons = [0,1,2,3,4,5,6,52]
# Fonctions graphiques
def mine(x,y,b,a):
fill_rect(x+2,y+2,9,9,b)
fill_rect(x,y+6,13,1,b)
fill_rect(x+6,y,1,13,b)
fill_rect(x+4,y+4,2,2,(255,255,255))
for p in [[9,2],[10,3],[10,9],[9,10],[3,10],[2,9],[2,3],[3,2]]:
set_pixel(x+p[0],y+p[1],a)
def top():
fill_rect(0,0,320,20,none)
if etat == "MINESWEEPER":
draw_string("Arthur J.",5,2,grey)
draw_string(etat,105,2,imp)
draw_string((" "+str(compteur)+"/20")[-5:],265,2,grey)
mine(249,4,grey,none)
else:
if etat == "YOU WON !": draw_string(etat,28,2,(0,153,0))
else: draw_string(etat,23,2,(255,0,0))
draw_string("EXE to PLAY AGAIN",145,2,grey)
def case(X,Y):
value = MAT[Y][X]
x, y = 20*X, 20*Y + 22
fill_rect(x,y,20,20,grey) # Bordures
fill_rect(x+1,y+1,18,18,none) # Vide
if value < 10: fill_rect(x+1,y+1,18,18,light) # Inconnu
elif value in range(11,19): draw_string(str(value-10),x+5,y+1,imp) # Valeur
elif value == 19:
fill_rect(x+1,y+1,18,18,(255,0,0))
mine(x+4,y+4,imp,(255,0,0)) # Bombe
elif value > 19: # Drapeau
fill_rect(x+1,y+1,18,18,light)
fill_rect(x+10,y+8,2,6,imp)
fill_rect(x+6,y+14,8,2,imp)
fill_rect(x+6,y+4,6,4,(0,153,0))
if cursor == [X,Y]:
fill_rect(x,y,20,3,curs)
fill_rect(x,y,3,20,curs)
fill_rect(x,y+17,20,3,curs)
fill_rect(x+17,y,3,20,curs)
# Fonctionnement
def key():
key = -1
while key == -1:
for k in buttons:
if keydown(k): key = k
while keydown(key): True
return key
def search(old): # Recherche des cases adjacentes vides
new = []
for i in old:
for p in prox:
tX, tY = i[0]+p[0], i[1]+p[1]
if tX in Xdomain and tY in Ydomain:
if MAT[tY][tX] == 0: new.append([tX,tY])
MAT[tY][tX] = (MAT[tY][tX])%10 + 10
case(tX,tY)
if new != []: search(new)
# Boucle principale
def play():
# Initialisation
global compteur, cursor, etat, cases, MAT
compteur = mines # Décompte apparent du nombre de bombes restant
decouv = 0 # Nombres réel de bombes déduites
cursor = [8,5] # Coordonnées du curseur
etat = "MINESWEEPER" # Etat de la partie
MAT = [[0]*len(Xdomain)]*len(Ydomain) # Génération d'une matrice blanche
# Dessin de l'interface
top()
fill_rect(0,21,320,1,grey)
for Y in Ydomain:
for X in Xdomain: case(X,Y)
# Boucle principale
while True:
X, Y = cursor[0], cursor[1]
result = key()
if result < 4:
if result==0 and cursor[0]-1 in Xdomain: cursor[0] -= 1
elif result==1 and cursor[1]-1 in Ydomain: cursor[1] -= 1
elif result==2 and cursor[1]+1 in Ydomain: cursor[1] += 1
elif result==3 and cursor[0]+1 in Xdomain: cursor[0] += 1
case(X,Y)
case(cursor[0],cursor[1])
elif result == 4:
if MAT == [[0]*len(Xdomain)]*len(Ydomain):
bombes = []
for i in range(mines):
x, y = choice(Xdomain), choice(Ydomain)
while (x,y) in bombes or (x,y) == (X,Y):
x, y = choice(Xdomain), choice(Ydomain)
bombes.append((x,y))
MAT = []
for y in Ydomain:
column = []
for x in Xdomain:
if (x,y) in bombes: column.append(9)
else: column.append(sum([1*((x+t[0],y+t[1]) in bombes) for t in prox]))
MAT.append(column)
for Y in Ydomain:
for X in Xdomain: case(X,Y)
X, Y = cursor[0], cursor[1]
if MAT[Y][X] > 19:
compteur += 1
draw_string((" "+str(compteur))[-2:],265,2,grey)
MAT[Y][X] = (MAT[Y][X])%10 + 10
case(X,Y)
if MAT[Y][X] == 10: search([[X,Y]])
elif MAT[Y][X] == 19:
etat = "YOU LOSE !"
break
elif result == 5:
if MAT[Y][X] < 20:
compteur -= 1
draw_string((" "+str(compteur))[-2:],265,2,grey)
if MAT[Y][X] == 9: decouv += 1
MAT[Y][X] = (MAT[Y][X])%10 + 20
case(X,Y)
elif result == 6: break
if compteur == 0 and decouv == 20:
etat = "YOU WON !"
break
top()
result = -1
while True:
if result == 52: play()
elif result == 6: break
else: result = key()
play()