Bonjour,

Voici le jeu Terrain miné - fait maison - à ne pas confondre avec le jeu "Mines".

Nom : exemple-jeu-terrain-mine.png
Affichages : 413
Taille : 8,7 Ko

But du jeu :

Vous devez déplacer une grosse boule vers un carré placé en bas de l'écran.
Malheureusement pour vous, le terrain est miné !
Saurez-vous atteindre votre but sans vous faire exploser ?
Bien entendu, vous êtes libres de reprendre ce code pour faire vos propres forks.

Version Python2 : python2-jeu-tkinter-terrain-mine.py (fichier à télécharger)

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
# logiciel pour Python2
 
# (c) 2014 Raphaël SEBAN (tarball69)
# publié sous licence GNU General Public License GPLv3
# cf http://www.gnu.org/licenses/gpl.html
 
# on demande en premier lieu
# la librairie graphique Tkinter
from Tkinter import *
 
# on veut aussi utiliser
# les nombres aléatoires
import random
 
# ------------------- DEBUT DU PROGRAMME -------------------------------
 
def debut_jeu ():
    "le programme commence ici"
    # init variables globales
    global fenetre, canvas, bouton_jouer
    global c_width, c_height, center_x, center_y
    # on crée la fenêtre principale
    fenetre = Tk()
    fenetre.title("Mon super jeu !")
    fenetre.resizable(width=False, height=False)
    # on ajoute des composants graphiques
    canvas = Canvas(fenetre, bg="black", width=400, height=300)
    canvas.pack(padx=5, pady=5)
    # on met des données courantes en globales
    c_width = canvas.winfo_reqwidth()
    c_height = canvas.winfo_reqheight()
    center_x = c_width // 2
    center_y = c_height // 2
    # bouton 'jouer'
    bouton_jouer = Button(fenetre)
    bouton_jouer.pack(side=LEFT, padx=50, pady=5)
    # bouton 'quitter'
    Button(
        fenetre, text="Quitter", command=fenetre.destroy
    ).pack(side=RIGHT, padx=5, pady=5)
    # on affiche une présentation du jeu
    presenter_jeu()
    # et on lance la
    # boucle événementielle principale
    fenetre.mainloop()
# end def
 
def presenter_jeu ():
    "on affiche ici une présentation du jeu"
    # on commence toujours par effacer le canevas graphique
    # qui nous fait office d'écran de jeu
    canvas.delete(ALL)
    # on affiche un titre
    # par rapport au centre de l'écran de jeu
    canvas.create_text(
        center_x, center_y - 100,
        text="Terrain miné",
        font="sans 16 bold",
        fill="navajowhite2",        # couleur du texte
    )
    # on affiche des règles du jeu succinctes
    canvas.create_text(
        center_x, center_y + 30,
        text="""\
Règles du jeu:
 
Vous devez déplacer une grosse boule vers un carré placé en bas de l'écran.
Malheureusement pour vous, le terrain est miné !
Saurez-vous atteindre votre but sans vous faire exploser ?
Utilisez les flèches du clavier pour vous déplacer.
        """,
        justify=LEFT,
        font="sans 10 bold italic",
        fill="Pale Goldenrod",
        width=0.8 * c_width,        # 80% de la largeur totale
    )
    # on s'assure que le joueur pourra (re)jouer
    bouton_jouer.configure(
        text="Jouer !", command=nouvelle_partie, state=NORMAL
    )
# end def
 
def nouvelle_partie ():
    "on réinitialise une nouvelle partie ici"
    # init variables globales
    global tid_joueur, tid_mines
    # on autorise le joueur à arrêter la partie quand il le souhaite
    bouton_jouer.configure(
        text="Stop !", command=arreter_partie, state=NORMAL
    )
    # on efface le canevas graphique
    canvas.delete(ALL)
    # on crée l'avatar du joueur (boule)
    creer_joueur()
    # on crée le carré d'arrivée
    creer_carre_final()
    # on mine le terrain
    miner_terrain()
    # on associe les touches fléchées du clavier
    # à nos propres fonctions de gestion du mouvement
    fenetre.bind_all("<Up>", deplacer_haut)
    fenetre.bind_all("<Down>", deplacer_bas)
    fenetre.bind_all("<Left>", deplacer_gauche)
    fenetre.bind_all("<Right>", deplacer_droite)
    # c'est ici qu'on lance les boucles inscrites dans le temps
    tid_joueur = fenetre.after(100, boucle_joueur)
    # les mines aussi vont avoir leur propre cycle de vie
    tid_mines = fenetre.after(1000, boucle_mines)
# end def
 
def creer_joueur ():
    """
        on crée l'avatar du joueur;
        ici le joueur est représenté par une grosse boule;
        on conserve l'identifiant de l'objet sur le canevas;
        ça nous servira plus tard;
        on prend des coordonnées au hasard;
    """
    # init variables globales
    global diametre_boule, rayon
    global boule_x, boule_y
    global sens_x, sens_y, pas
    global boule_joueur
    # inits
    diametre_boule = 20         # diamètre de la boule
    rayon = diametre_boule // 2
    boule_x = diametre_boule + random.randint(0, c_width - diametre_boule)
    boule_y = diametre_boule + random.randint(0, 10)
    sens_x, sens_y = (0, 0)     # sens de déplacement initial
    pas = random.randint(2, 5)  # quantité de déplacement (pixels)
    # on dessine l'avatar joueur (boule)
    boule_joueur = canvas.create_oval(
        boule_x - rayon, boule_y - rayon,
        boule_x + rayon, boule_y + rayon,
        outline="ivory2",       # couleur du contour
        width=1,                # épaisseur du trait de contour (pixels)
        fill="light sky blue",  # couleur du remplissage
    )
# end def
 
def creer_carre_final ():
    """
        on crée le carré final (carré d'arrivée);
        on conserve l'identifiant de l'objet sur le canevas;
        ça nous servira plus tard;
        on prend des coordonnées au hasard;
    """
    # init variables globales
    global taille_carre, demi_carre
    global carre_x, carre_y
    global carre_final
    # on crée le carré d'arrivée
    taille_carre = diametre_boule + 6
    demi_carre = taille_carre // 2
    carre_x = 10 + random.randint(0, c_width - taille_carre - 10)
    carre_y = c_height - taille_carre - random.randint(5, 20)
    # on dessine le carré final (arrivée)
    carre_final = canvas.create_rectangle(
        carre_x - demi_carre, carre_y - demi_carre,
        carre_x + demi_carre, carre_y + demi_carre,
        outline="ivory",        # couleur du contour
        width=1,                # épaisseur du trait de contour (pixels)
        fill="palegreen1",      # couleur du remplissage
    )
# end def
 
def miner_terrain ():
    "on mine le terrain au début d'une partie"
    # on choisit un nombre au hasard
    nombre_mines = random.randint(10, 20)
    # on boucle pour poser autant de mines
    for i in range(nombre_mines):
        # on pose une mine au hasard
        poser_mine()
    # end for
# end def
 
def poser_mine ():
    """
        on pose une mine au hasard, mais jamais dans le voisinage
        immédiat du joueur;
    """
    # init variables locales
    tailles = [8, 10, 12]
    couleurs = ["sandy brown", "peru", "sienna1", "coral1"]
    couleur_contour = "burlywood1"
    # on boucle tant que ce n'est pas bon
    # ici, on bouclera cent fois (par précaution)
    # et on abandonnera si on n'y arrive pas
    for i in range(100):
        # on choisit de nouvelles coordonnées au hasard
        mine_x = random.randint(0, c_width)
        mine_y = random.randint(0, c_height)
        # les mines ont une taille variable;
        taille_mine = random.choice(tailles)
        # on recherche les collisions avec d'autres objets
        collisions = canvas.find_overlapping(
            *b_rect(mine_x, mine_y, taille_mine)
        )
        # aucun objet dans le voisinage ?
        if not collisions:
            # on choisit une couleur parmi une palette
            color = random.choice(couleurs)
            # on peut poser notre mine !
            canvas.create_oval(
                b_rect(mine_x, mine_y, taille_mine//2),
                outline=couleur_contour,
                width=1,        # épaisseur de trait de contour (pixels)
                fill=color,     # couleur du remplissage
            )
            # on remonte la boule joueur
            # au premier plan d'affichage
            canvas.tag_raise(boule_joueur, ALL)
            # c'est bon, on a posé notre mine
            # on peut quitter la boucle
            break
        # end if
    # end for
# end def
 
def b_rect (x, y, size):
    """
        retourne les coordonnées (top, left, bottom, right) d'un
        rectangle de délimitation autour d'un point P(x, y);
    """
    # rectangle d'influence (bounding rectangle)
    return (x - size, y - size, x + size, y + size)
# end def
 
def boucle_joueur ():
    "boucle temporelle qui gère les mouvements du joueur"
    # init variables globales
    global boule_x, boule_y
    global tid_joueur
    # le joueur a atteint le carré final ?
    if carre_x - demi_carre < boule_x < carre_x + demi_carre and \
            carre_y - demi_carre < boule_y < carre_y + demi_carre:
        # bravo ! c'est gagné !
        return bravo()
    # end if
    # on recherche des collisions
    collisions = canvas.find_overlapping(*canvas.bbox(boule_joueur))
    # le joueur a heurté autre chose que le carré final ?
    if len(collisions) > 1 and carre_final not in collisions:
        # aïe ! dommage !
        return game_over()
    # end if
    # tout est OK on déplace le joueur
    boule_x = (boule_x + sens_x * pas) % c_width
    boule_y = (boule_y + sens_y * pas) % c_height
    canvas.coords(
        boule_joueur,
        boule_x - rayon, boule_y - rayon,
        boule_x + rayon, boule_y + rayon,
    )
    # on boucle à nouveau dans le temps
    tid_joueur = fenetre.after(50, boucle_joueur)
# end def
 
def boucle_mines ():
    "boucle temporelle qui gère la vie des mines"
    # init variables globales
    global tid_mines
    # on pose une mine de temps en temps
    poser_mine()
    # prochaine mine dans... (millisecondes)
    delai = random.randint(1000, 2000)
    # on relance la boucle après ce délai
    tid_mines = fenetre.after(delai, boucle_mines)
# end def
 
def bravo ():
    "super ! le joueur a réussi !"
    # on affiche l'écran final
    ecran_final(
        titre="BRAVO",
        sous_titre="C'est gagné !",
        couleur_titre="lemon chiffon",
        couleur_sous_titre="pale green",
    )
# end def
 
def game_over ():
    "c'est ballot ! le joueur a perdu !"
    # on affiche l'écran final
    ecran_final(
        titre="GAME OVER",
        sous_titre="C'est perdu !",
        couleur_titre="firebrick1",
        couleur_sous_titre="floral white",
    )
# end def
 
def arreter_partie ():
    "le joueur souhaite arrêter la partie"
    # on affiche l'écran final
    ecran_final(
        titre="ABANDON",
        sous_titre="Partie annulée !",
        couleur_titre="gold",
        couleur_sous_titre="light grey",
    )
# end def
 
def ecran_final (**kw):
    "on effectue ici des opérations communes à tous les écrans finaux"
    # on bloque les clics intempestifs sur le bouton 'jouer'
    bouton_jouer.configure(state=DISABLED)
    # on stoppe les boucles temporelles qui pourraient
    # éventuellement être encore en cours d'exécution
    arreter_boucles()
    # on efface le canevas graphique
    canvas.delete(ALL)
    # on récupère le titre et sa couleur
    titre = kw.get("titre") or ""
    couleur = kw.get("couleur_titre") or "white"
    # on affiche le titre
    canvas.create_text(
        center_x, center_y - 40,
        text=titre,
        font="sans 36 bold",
        fill=couleur,
    )
    # on récupère le sous-titre et sa couleur
    sous_titre = kw.get("sous_titre") or ""
    couleur = kw.get("couleur_sous_titre") or "white"
    # on affiche le sous-titre
    canvas.create_text(
        center_x, center_y,
        text=sous_titre,
        font="sans 16 bold italic",
        fill=couleur,
    )
    # on relance presenter_jeu() après @delai
    # avec toutefois un minimum de 1 seconde
    delai = kw.get("delai") or 2000     # valeur par défaut
    fenetre.after(max(1000, delai), presenter_jeu)
# end def
 
def arreter_boucles ():
    "on arrête les boucles temporelles susceptibles de tourner encore"
    # init variables globales
    global tid_joueur, tid_mines
    # stooop !
    fenetre.after_cancel(tid_joueur)
    fenetre.after_cancel(tid_mines)
    # RAZ thread ids
    tid_joueur = tid_mines = 0
# end def
 
def deplacer_haut (event=None):
    "on déplace l'avatar du joueur vers le haut"
    # init variables globales
    global sens_x, sens_y
    # grâce à la boucle du jeu,
    # nous n'avons pas besoin de plus que ça
    sens_x, sens_y = (0, -1)
# end def
 
def deplacer_bas (event=None):
    "on déplace l'avatar du joueur vers le bas"
    # init variables globales
    global sens_x, sens_y
    # grâce à la boucle du jeu,
    # nous n'avons pas besoin de plus que ça
    sens_x, sens_y = (0, 1)
# end def
 
def deplacer_gauche (event=None):
    "on déplace l'avatar du joueur vers la gauche"
    # init variables globales
    global sens_x, sens_y
    # grâce à la boucle du jeu,
    # nous n'avons pas besoin de plus que ça
    sens_x, sens_y = (-1, 0)
# end def
 
def deplacer_droite (event=None):
    "on déplace l'avatar du joueur vers la droite"
    # init variables globales
    global sens_x, sens_y
    # grâce à la boucle du jeu,
    # nous n'avons pas besoin de plus que ça
    sens_x, sens_y = (1, 0)
# end def
 
# ------------------- FIN DU PROGRAMME ---------------------------------
 
# ce script est automatiquement lancé grâce à ceci :
 
# /!\ à connaître par coeur /!\
if __name__ == "__main__":
    # lancer le programme
    debut_jeu()
# end if
Version Python3 : python3-jeu-tkinter-terrain-mine.py (fichier à télécharger)

Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
 
# logiciel pour Python3
 
# (c) 2014 Raphaël SEBAN (tarball69)
# publié sous licence GNU General Public License GPLv3
# cf http://www.gnu.org/licenses/gpl.html
 
# on demande en premier lieu
# la librairie graphique Tkinter
from tkinter import *
 
# on veut aussi utiliser
# les nombres aléatoires
import random
 
# ------------------- DEBUT DU PROGRAMME -------------------------------
 
def debut_jeu ():
    "le programme commence ici"
    # init variables globales
    global fenetre, canvas, bouton_jouer
    global c_width, c_height, center_x, center_y
    # on crée la fenêtre principale
    fenetre = Tk()
    fenetre.title("Mon super jeu !")
    fenetre.resizable(width=False, height=False)
    # on ajoute des composants graphiques
    canvas = Canvas(fenetre, bg="black", width=400, height=300)
    canvas.pack(padx=5, pady=5)
    # on met des données courantes en globales
    c_width = canvas.winfo_reqwidth()
    c_height = canvas.winfo_reqheight()
    center_x = c_width // 2
    center_y = c_height // 2
    # bouton 'jouer'
    bouton_jouer = Button(fenetre)
    bouton_jouer.pack(side=LEFT, padx=50, pady=5)
    # bouton 'quitter'
    Button(
        fenetre, text="Quitter", command=fenetre.destroy
    ).pack(side=RIGHT, padx=5, pady=5)
    # on affiche une présentation du jeu
    presenter_jeu()
    # et on lance la
    # boucle événementielle principale
    fenetre.mainloop()
# end def
 
def presenter_jeu ():
    "on affiche ici une présentation du jeu"
    # on commence toujours par effacer le canevas graphique
    # qui nous fait office d'écran de jeu
    canvas.delete(ALL)
    # on affiche un titre
    # par rapport au centre de l'écran de jeu
    canvas.create_text(
        center_x, center_y - 100,
        text="Terrain miné",
        font="sans 16 bold",
        fill="navajowhite2",        # couleur du texte
    )
    # on affiche des règles du jeu succinctes
    canvas.create_text(
        center_x, center_y + 30,
        text="""\
Règles du jeu:
 
Vous devez déplacer une grosse boule vers un carré placé en bas de l'écran.
Malheureusement pour vous, le terrain est miné !
Saurez-vous atteindre votre but sans vous faire exploser ?
Utilisez les flèches du clavier pour vous déplacer.
        """,
        justify=LEFT,
        font="sans 10 bold italic",
        fill="Pale Goldenrod",
        width=0.8 * c_width,        # 80% de la largeur totale
    )
    # on s'assure que le joueur pourra (re)jouer
    bouton_jouer.configure(
        text="Jouer !", command=nouvelle_partie, state=NORMAL
    )
# end def
 
def nouvelle_partie ():
    "on réinitialise une nouvelle partie ici"
    # init variables globales
    global tid_joueur, tid_mines
    # on autorise le joueur à arrêter la partie quand il le souhaite
    bouton_jouer.configure(
        text="Stop !", command=arreter_partie, state=NORMAL
    )
    # on efface le canevas graphique
    canvas.delete(ALL)
    # on crée l'avatar du joueur (boule)
    creer_joueur()
    # on crée le carré d'arrivée
    creer_carre_final()
    # on mine le terrain
    miner_terrain()
    # on associe les touches fléchées du clavier
    # à nos propres fonctions de gestion du mouvement
    fenetre.bind_all("<Up>", deplacer_haut)
    fenetre.bind_all("<Down>", deplacer_bas)
    fenetre.bind_all("<Left>", deplacer_gauche)
    fenetre.bind_all("<Right>", deplacer_droite)
    # c'est ici qu'on lance les boucles inscrites dans le temps
    tid_joueur = fenetre.after(100, boucle_joueur)
    # les mines aussi vont avoir leur propre cycle de vie
    tid_mines = fenetre.after(1000, boucle_mines)
# end def
 
def creer_joueur ():
    """
        on crée l'avatar du joueur;
        ici le joueur est représenté par une grosse boule;
        on conserve l'identifiant de l'objet sur le canevas;
        ça nous servira plus tard;
        on prend des coordonnées au hasard;
    """
    # init variables globales
    global diametre_boule, rayon
    global boule_x, boule_y
    global sens_x, sens_y, pas
    global boule_joueur
    # inits
    diametre_boule = 20         # diamètre de la boule
    rayon = diametre_boule // 2
    boule_x = diametre_boule + random.randint(0, c_width - diametre_boule)
    boule_y = diametre_boule + random.randint(0, 10)
    sens_x, sens_y = (0, 0)     # sens de déplacement initial
    pas = random.randint(2, 5)  # quantité de déplacement (pixels)
    # on dessine l'avatar joueur (boule)
    boule_joueur = canvas.create_oval(
        boule_x - rayon, boule_y - rayon,
        boule_x + rayon, boule_y + rayon,
        outline="ivory2",       # couleur du contour
        width=1,                # épaisseur du trait de contour (pixels)
        fill="light sky blue",  # couleur du remplissage
    )
# end def
 
def creer_carre_final ():
    """
        on crée le carré final (carré d'arrivée);
        on conserve l'identifiant de l'objet sur le canevas;
        ça nous servira plus tard;
        on prend des coordonnées au hasard;
    """
    # init variables globales
    global taille_carre, demi_carre
    global carre_x, carre_y
    global carre_final
    # on crée le carré d'arrivée
    taille_carre = diametre_boule + 6
    demi_carre = taille_carre // 2
    carre_x = 10 + random.randint(0, c_width - taille_carre - 10)
    carre_y = c_height - taille_carre - random.randint(5, 20)
    # on dessine le carré final (arrivée)
    carre_final = canvas.create_rectangle(
        carre_x - demi_carre, carre_y - demi_carre,
        carre_x + demi_carre, carre_y + demi_carre,
        outline="ivory",        # couleur du contour
        width=1,                # épaisseur du trait de contour (pixels)
        fill="palegreen1",      # couleur du remplissage
    )
# end def
 
def miner_terrain ():
    "on mine le terrain au début d'une partie"
    # on choisit un nombre au hasard
    nombre_mines = random.randint(10, 20)
    # on boucle pour poser autant de mines
    for i in range(nombre_mines):
        # on pose une mine au hasard
        poser_mine()
    # end for
# end def
 
def poser_mine ():
    """
        on pose une mine au hasard, mais jamais dans le voisinage
        immédiat du joueur;
    """
    # init variables locales
    tailles = [8, 10, 12]
    couleurs = ["sandy brown", "peru", "sienna1", "coral1"]
    couleur_contour = "burlywood1"
    # on boucle tant que ce n'est pas bon
    # ici, on bouclera cent fois (par précaution)
    # et on abandonnera si on n'y arrive pas
    for i in range(100):
        # on choisit de nouvelles coordonnées au hasard
        mine_x = random.randint(0, c_width)
        mine_y = random.randint(0, c_height)
        # les mines ont une taille variable;
        taille_mine = random.choice(tailles)
        # on recherche les collisions avec d'autres objets
        collisions = canvas.find_overlapping(
            *b_rect(mine_x, mine_y, taille_mine)
        )
        # aucun objet dans le voisinage ?
        if not collisions:
            # on choisit une couleur parmi une palette
            color = random.choice(couleurs)
            # on peut poser notre mine !
            canvas.create_oval(
                b_rect(mine_x, mine_y, taille_mine//2),
                outline=couleur_contour,
                width=1,        # épaisseur de trait de contour (pixels)
                fill=color,     # couleur du remplissage
            )
            # on remonte la boule joueur
            # au premier plan d'affichage
            canvas.tag_raise(boule_joueur, ALL)
            # c'est bon, on a posé notre mine
            # on peut quitter la boucle
            break
        # end if
    # end for
# end def
 
def b_rect (x, y, size):
    """
        retourne les coordonnées (top, left, bottom, right) d'un
        rectangle de délimitation autour d'un point P(x, y);
    """
    # rectangle d'influence (bounding rectangle)
    return (x - size, y - size, x + size, y + size)
# end def
 
def boucle_joueur ():
    "boucle temporelle qui gère les mouvements du joueur"
    # init variables globales
    global boule_x, boule_y
    global tid_joueur
    # le joueur a atteint le carré final ?
    if carre_x - demi_carre < boule_x < carre_x + demi_carre and \
            carre_y - demi_carre < boule_y < carre_y + demi_carre:
        # bravo ! c'est gagné !
        return bravo()
    # end if
    # on recherche des collisions
    collisions = canvas.find_overlapping(*canvas.bbox(boule_joueur))
    # le joueur a heurté autre chose que le carré final ?
    if len(collisions) > 1 and carre_final not in collisions:
        # aïe ! dommage !
        return game_over()
    # end if
    # tout est OK on déplace le joueur
    boule_x = (boule_x + sens_x * pas) % c_width
    boule_y = (boule_y + sens_y * pas) % c_height
    canvas.coords(
        boule_joueur,
        boule_x - rayon, boule_y - rayon,
        boule_x + rayon, boule_y + rayon,
    )
    # on boucle à nouveau dans le temps
    tid_joueur = fenetre.after(50, boucle_joueur)
# end def
 
def boucle_mines ():
    "boucle temporelle qui gère la vie des mines"
    # init variables globales
    global tid_mines
    # on pose une mine de temps en temps
    poser_mine()
    # prochaine mine dans... (millisecondes)
    delai = random.randint(1000, 2000)
    # on relance la boucle après ce délai
    tid_mines = fenetre.after(delai, boucle_mines)
# end def
 
def bravo ():
    "super ! le joueur a réussi !"
    # on affiche l'écran final
    ecran_final(
        titre="BRAVO",
        sous_titre="C'est gagné !",
        couleur_titre="lemon chiffon",
        couleur_sous_titre="pale green",
    )
# end def
 
def game_over ():
    "c'est ballot ! le joueur a perdu !"
    # on affiche l'écran final
    ecran_final(
        titre="GAME OVER",
        sous_titre="C'est perdu !",
        couleur_titre="firebrick1",
        couleur_sous_titre="floral white",
    )
# end def
 
def arreter_partie ():
    "le joueur souhaite arrêter la partie"
    # on affiche l'écran final
    ecran_final(
        titre="ABANDON",
        sous_titre="Partie annulée !",
        couleur_titre="gold",
        couleur_sous_titre="light grey",
    )
# end def
 
def ecran_final (**kw):
    "on effectue ici des opérations communes à tous les écrans finaux"
    # on bloque les clics intempestifs sur le bouton 'jouer'
    bouton_jouer.configure(state=DISABLED)
    # on stoppe les boucles temporelles qui pourraient
    # éventuellement être encore en cours d'exécution
    arreter_boucles()
    # on efface le canevas graphique
    canvas.delete(ALL)
    # on récupère le titre et sa couleur
    titre = kw.get("titre") or ""
    couleur = kw.get("couleur_titre") or "white"
    # on affiche le titre
    canvas.create_text(
        center_x, center_y - 40,
        text=titre,
        font="sans 36 bold",
        fill=couleur,
    )
    # on récupère le sous-titre et sa couleur
    sous_titre = kw.get("sous_titre") or ""
    couleur = kw.get("couleur_sous_titre") or "white"
    # on affiche le sous-titre
    canvas.create_text(
        center_x, center_y,
        text=sous_titre,
        font="sans 16 bold italic",
        fill=couleur,
    )
    # on relance presenter_jeu() après @delai
    # avec toutefois un minimum de 1 seconde
    delai = kw.get("delai") or 2000     # valeur par défaut
    fenetre.after(max(1000, delai), presenter_jeu)
# end def
 
def arreter_boucles ():
    "on arrête les boucles temporelles susceptibles de tourner encore"
    # init variables globales
    global tid_joueur, tid_mines
    # stooop !
    fenetre.after_cancel(tid_joueur)
    fenetre.after_cancel(tid_mines)
    # RAZ thread ids
    tid_joueur = tid_mines = 0
# end def
 
def deplacer_haut (event=None):
    "on déplace l'avatar du joueur vers le haut"
    # init variables globales
    global sens_x, sens_y
    # grâce à la boucle du jeu,
    # nous n'avons pas besoin de plus que ça
    sens_x, sens_y = (0, -1)
# end def
 
def deplacer_bas (event=None):
    "on déplace l'avatar du joueur vers le bas"
    # init variables globales
    global sens_x, sens_y
    # grâce à la boucle du jeu,
    # nous n'avons pas besoin de plus que ça
    sens_x, sens_y = (0, 1)
# end def
 
def deplacer_gauche (event=None):
    "on déplace l'avatar du joueur vers la gauche"
    # init variables globales
    global sens_x, sens_y
    # grâce à la boucle du jeu,
    # nous n'avons pas besoin de plus que ça
    sens_x, sens_y = (-1, 0)
# end def
 
def deplacer_droite (event=None):
    "on déplace l'avatar du joueur vers la droite"
    # init variables globales
    global sens_x, sens_y
    # grâce à la boucle du jeu,
    # nous n'avons pas besoin de plus que ça
    sens_x, sens_y = (1, 0)
# end def
 
# ------------------- FIN DU PROGRAMME ---------------------------------
 
# ce script est automatiquement lancé grâce à ceci :
 
# /!\ à connaître par coeur /!\
if __name__ == "__main__":
    # lancer le programme
    debut_jeu()
# end if
Amusez-vous bien !

@+.