Atelier : Jeu démineur.


Prérequis : développement d'interfaces en Visual Basic, récursivité.

Un exemple de démineur ici (nécessites les DLL Visual Basic 5).

1
Développement de l'interface.

Le premier travail est de développer une interface semblable à celle représentée ci-contre :


2
Déclaration des principales variables du jeu.

Vous devez déclarer, en global, les variables suivantes :

Rappels sur... Programmer proprement !

Pour que votre programme soit lisible par vous, par le prof et par ceux qui sont susceptible de maintenir vos applications, n'oubliez pas les points suivants, tous fondamentaux :

  • utilisez des noms de variables explicites, quitte à les rallonger. Par exemple :
    • n'utilisez pas : i, j, k ; n , max ;
    • utilisez : iCases, iDirections, iVoisin, NombreMines, etc. ;
  • Indentez le code
  • Commenter votre code. En général, un commentaire par fonction suffit.
  • En Visual Basic, l'option de compilation Option Explicit permet d'éliminer 50% des erreurs.

3
Les fonctions non événementielles
pour nous aider par la suite.

 Trouver l'indice des cases voisines.

Nous allons avoir besoin d'une fonction qui nous permet de retrouver rapidement l'indice des cases qui entourent une case donnée en paramètre :

Function IndiceVoisin(UneCase as Integer, UneDirection as Integer) as Integer

1
2
3
8
UneCase
4
7
6
5
Tableau des directions
26
27
28
46
47
48
66
67
68
Exemple d'indices voisins de 47
(dans le cas ou NbColonnes = 20).

En suivant cet exemple, on doit avoir les résultats suivants (vous devez bien comprendre ça avant de passer à la suite) :

Petite complication : vous devez gérez les débordements spécifiques suivants :

Votre travail pour cette étape est d'écrire cette fonction.

 Compter le nombre de mines autour d'une case.

Nous allons avoir besoin d'une fonction qui nous donne le nombre de mines autour d'une case :

Function NombreMines(iCase As Integer) As Integer

L'algorithme est extrêmement simple :

Initialiser le nombre de mines voisines à 0
Pour chaque direction de 1 à 8 (utilisez la boucle For car la valeur de départ et d'arrivée de la boucle sont connues)
  Si l'indice du voisin existe (indice voisin différent de -1)
    S'il y a une mine sur la case voisine (utilisez le tableau BombePrésente)
      Incrémenter le nombre de mines voisines
    Fin Si
  Fin Si
Fin Pour

Vous devez écrire cette fonction.

4
Ecriture des fonctions événementielles
les plus simples.

 Form_load : démarrage de l'application.

Au démarrage du programme, vous devez :

Vous écrirez également cette fonction InitialiseNouvellePartie dont le rôle est :

 Clic droit sur une case du jeu.

Lorsque le joueur fait un clic droit sur une case, c'est pour marquer la présence d'une mine (ou pour enlever le marquage). Dans la pratique, on va colorer la Shape en rouge pour représenter ce marquage (il est possible d'utiliser un contrôle Image avec un drapeau).

Techniquement, vous devez :

 Clic sur le bouton de commande Nouvelle Partie.

Sur cet événement, vous devez simplement faire un appel à la fonction InitialiseNouvellePartie.

 Clic sur le bouton de commande Quitter.

Sur cet événement, vous devez simplement faire un appel à la fonction End. Remarquez que dans toutes les applications, le bouton Quitter a sa propriété Cancel=True. Cela permet de quitter le jeu en appuyant sur la touche Esc.

 La fonction Timer.

L'algorithme (très simple) est le suivant :

Si la partie a déjà commencé (Not AttentePremierClick) alors
  Afficher le temps écoulé en secondes (Temps - Timer) dans le contrôle label correspondant
Fin Si

5
La fonction événementielle la plus complexe :
Clic gauche sur une case du jeu.

 La fonction événementielle Case_Click.

Dans le démineur, on fait un clic gauche sur une case lorsqu'on pense n'elle ne cache pas de mine. Ce qu'il se passe :

On doit également dire que la partie est commencée (pour démarrer le compteur de temps) :

If AttentePremierClick Then
  AttentePremierClick = False
  Temps = Timer
End If

 La fonction récursive Ouvre.

Sub Ouvrir(iCase As Integer)

Cette fonction "ouvre" une cases lorsqu'on clique dessus, c'est à dire que :

Vous risquez d'avoir des erreurs si vous ne tenez pas compte des indications suivantes :

6
Ecriture le la fonction
pour savoir si la partie est gagnée.

La fonction pour savoir si la partie est gagnée n'est pas triviale :

Function Gagné() As Boolean

Ecrivez-là en vous basant sur l'algorithme suivant :

On suppose que la partie est gagnée
Pour chaque case encore visible :
   Si la case n'est pas encore ouvert (test sur la couleur ?)
   ou si elle est marqué par erreur (test sur la couleur et
BombePrésente)
     Alors la partie n'est pas gagnée
  Fin Si
Fin Pour

Vous devez intégrer cette fonction dans la fonction événementielle "Clic gauche sur une case du jeu" (décrite dans l'étape 5) : après avoir ouvert la case, on teste si la partie est gagnée. Si c'est le cas on affiche un message de victoire puis on recommence une nouvelle partie.

7
Test du jeu.

A ce stade, l'ensemble du jeu doit être fonctionnel. Testez et déboguez.

8
Ajout de la gestion des HighScores.

La gestion des High Scores représente, à mon avis, la partie la plus difficile de ce atelier. Cela demande en effet de faire une gestion de fichier (pour écrire les scores sur le disque dur) et un tri de tableau pour les afficher.

Débrouillez-vous avec les indications suivantes :

  • Lorsqu'on a gagné une partie, vous ouvrez le fichier des High Scores en mode Ajout, et vous ajoutez le nom du joueur et son temps (ceci est assez simple).

  • Lorsqu'on clique sur le bouton de commande Afficher les High Scores, vous devez :
    • lire les scores un par un dans le fichier des High Scores ;
    • les insérer au fur et à mesure dans un tableau (réalisez une insertion triée) ;
    • les afficher dans une nouvelle feuille.
C'est fini, testez...

9
Autres améliorations possibles.

Sébastien PASTORE.