Université Paris 6
Licence d'informatique
Module de Programmation
Année 2000-2001


Travaux pratiques n° 1
Le jeu « SameGame »




1   Description d'une version simple du jeu

Le tableau de jeu a la forme d'un rectangle de 10 lignes et 15 colonnes où figurent des pièces variées (disques de couleur ou bien comme dans cet énoncé pièces géométriques), avec 3 types possibles. Ces pièces sont tirées au hasard au début du jeu. On peut quitter le jeu à tout instant en tapant q (pour quitter) dans la fenêtre graphique. Voici un exemple de tableau de jeu au début d'une partie :
Il s'agit pour le joueur d'enlever le plus possible de pièces sur le rectangle de jeu, sachant que pour retirer une pièce il faut que cette pièce fasse partie d'une sélection. Une sélection comprend nécessairement au minimum deux pièces du même type disposées côte à côte verticalement ou horizontalement. La sélection s'effectue en cliquant sur une pièce et le programme détermine lui-même quel est l'ensemble des pièces qui forment une chaîne ininterrompue de sélections élémentaires à partir de la pièce désignée par le joueur. Par exemple sur le jeu ci-dessus, si l'on clique sur la pièce qui se trouve dans la huitième ligne à partir du bas et la quatrième colonne à partir de la gauche, alors la sélection effectuée correspond à la zone entourée sur la vue suivante :
Cette sélection va disparaître selon la première règle suivante : les pièces qui se trouvaient au dessus des pièces de la sélection descendent pour prendre leur place. Pour la sélection de notre exemple, cela donne :
Si la suppression de la sélection produit une colonne vide, comme pour la sélection ci-dessous :
alors cette colonne vide disparaît et les colonnes suivantes sont décalées d'un cran sur la gauche, comme le montre la figure ci-dessous pour la deuxième sélection de notre exemple.
Le jeu est terminé quand il n'est plus possible de retirer aucune pièce du tableau de jeu comme dans le cas ci-dessous.
Il apparaît alors un message pour proposer une nouvelle partie ou quitter. Par exemple :
Une version complète du jeu avec historique, scores, table des meilleurs scores, visualisation de la sélection avec le nombre de pièces qu'elle contient, etc. est disponible ici.

2   Description du projet

2.1   Représentation interne du jeu

Tableau débordant

On représente le tableau de jeu par un tableau débordant, c'est-à-dire que si le jeu comprend l lignes et c colonnes, on crée un tableau de taille (l+2)×(c+2), dont les indices de lignes peuvent varier de 0 à l+1 et les indices de colonnes de 0 à c+1.

Ceci a pour effet que, lors de la détection de la zone sélectionnée, pour tout indice de ligne i (resp. de colonne j) compris entre 1 et l (resp. c), les lignes i-1 et i+1 (resp. les colonnes j-1 et j+1) du tableau existent.

On représente graphiquement les lignes (resp. colonnes) d'indice compris entre 1 et l (resp. c).

Pièces

Chaque case du tableau a une couleur qui est celle de la pièce s'il y en a une ou bien celle du fond s'il n'y en a pas. Par exemple, les bords du tableau (ces lignes et colonnes d'indice 0, l+1 et c+1) sont de la couleur du fond. Ceci nous sert lors de la détection de la zone sélectionnée.

Par ailleurs, lors de cette détection, on a besoin de pouvoir marquer les cases selon que l'on sait déjà que la pièce appartient ou n'appartient pas à la zone sélectionnée, ou que cette pièce n'a pas encore été examinée.

On définit donc un type somme qui représente cette information de marquage, un type somme pour les couleurs si on le désire, puis un type produit qui contient une couleur et une marque. Les éléments du tableau sont de ce type.

2.2   L'algorithme central : la détection de la zone sélectionnée et sa suppression

Le marquage

Dans un premier temps, pour détecter la zone sélectionnée dans le tableau t à la iligne et la jcolonne, on va passer d'une situation où toutes les cases sont marquées Peut_être à une situation où toutes les cases sont marquées Oui ou Non. Pour cela on procède récursivement dans les quatre directions à partir de la case de départ en s'arrêtant aux bords et en essayant de ne marquer que les cases qui ne le sont pas déjà (celles marquées Peut_être).

La suppression

Lorsque le tableau est entièrement marqué de cette façon et que la sélection comporte au moins deux pièces, on va tout d'abord supprimer dans chaque colonne les pièces qui appartiennent à la sélection, puis supprimer les colonnes vides, démarquer les cases du tableau et mettre à jour la représentation graphique du tableau de jeu.

2.3   La bibliothèque graphique

Pour la représentation graphique, on utilisera la bibliothèque libgraph de Caml en l'appelant interactivement de la façon suivante :
camllight camlgraph
ou pour obtenir un exécutable dans une commande batch de la forme
camlc -custom unix.zo graphics.zo same_game.ml -o same -lgraph -lunix \
-ccopt -L/usr/X11R6/lib -lX11
Cette bibliothèque est documentée dans le manuel de référence qui vous a été distribué au début de l'année ou bien sur le Web (en allant dans le menu Tuareg sous emacs ou bien directement à l'adresse locale ou encore le site d'origine).

À titre indicatif, voici des fonctions qui seront probablement utilisées dans votre projet:

2.4   Le hasard

Le hasard dans le choix de la couleur des pièces lorsque l'on produit le tableau de jeu sera obtenu en utilisant le module random de la standard library. Pour initialiser le générateur aléatoire, on peut utiliser la valeur fournie par la fonction unix__time, de la bibliothèque unix qui est disponible avec la bibliothèque graphique. Pendant la mise au point du programme on laissera la ligne d'initialisation du générateur en commentaires pour avoir des parties reproductibles.

2.5   La gestion évènementielle

On dispose avec la bibliothèque graphique de quelques fonctions de gestion évènementielle. Il s'agit de détecter les actions au niveau de la souris et du clavier dans la fenêtre graphique.

La souris

La seule action de la souris qui nous intéresse est d'attendre que le joueur clique sur une pièce et de connaître la position de cette pièce.

La fonction wait_next_event nous permet entre autres choses de détecter que le bouton de la souris (cette petite bibliothèque graphique ne différencie pas les boutons de la souris) a été enfoncé ou au contraire qu'on l'a relâché.

À titre d'exemple, on peut détecter un clic de souris de la façon suivante :
let wait_click () = 
  wait_next_event [Button_down]; wait_next_event [Button_up];;
Le résultat de cette fonction est alors de type status, qui comporte les informations mouse_x et mouse_y, c'est-à-dire les coordonnées graphiques de la souris au moment du clic.

On exploite alors la position de la souris de la façon suivante :
let s = wait_click () in
let x = s.mouse_x and y = s.mouse_y in
...

Le clavier

Il faut aussi lire des caractères au clavier, puisque le joueur peut taper « q » à tout moment d'une part et d'autre part on lui demande ses intentions quand le jeu est terminé.

Cette lecture peut se faire :

2.6   Travail à réaliser

Il vous est demandé d'implémenter cette version simple le plus proprement possible. En particulier les paramètres du jeu devront pouvoir être aisément modifiés. Vous pourrez ensuite enrichir votre version comme vous le souhaitez en l'indiquant dans votre rapport.

Vous remettrez un petit rapport en même temps que votre programme, mentionnant les difficultés rencontrées, les choix que vous avez faits ainsi qu'une description précise de la façon dont vous avez exploité le marquage de la sélection lors de la suppression.

Annexe: un petit canevas

Voici une proposition de structure pour votre programme. Il n'est pas du tout obligatoire de la suivre, elle n'est indiquée que pour vous aider si vous rencontrez des difficultés à concevoir la structure de votre programme.


Ce document a été traduit de LATEX par HEVEA.