Faire un jeu avec l'automate ? ... Le judootomate


INTRODUCTION

Quels jeux pourrait-on faire en partant de la base d'automate cellulaire présentée précédemment ?

Le Judootomate est une proposition mais le sujet n'est pas clôt ! L'objectif est d'illustrer le fait qu'il est possible de réinvestir de sens une proposition initiale et de dériver à partir d'un sujet vers quelque chose d'autre.

TELECHARGER

aaaTexte des explications, aaaExecutable (Windows), aaaSource (utilise la librairie Allegro)

 

EXPLICATIONS

1. Principe

En utilisant le moteur d'automate cellulaire précédent l'idée est de provoquer une lutte entre l'utilisateur et l'automate : l'utilisateur doit vaincre l'automate soit en détruisant toutes ses cellules, soit en contribuant à l'apparition d'une figure qui ne peut plus être sujette à évolution.

Du point de vue de l’automate cellulaire le moteur est celui l’automate cellulaire présenté précédemment mais au lieu de faire évoluer la totalité du plan il s’agit d’en faire évoluer uniquement des portions. Ces portions sont localisées à partir de la position du curseur de la souris et le moteur est actionné sur ces portions à chaque clic gauche. Eventuellement un clic droit permet de forcer une cellule à la valeur 1 afin de modifier la configuration de la zone pointée.

2. Mise en forme et initialisation

Comme dans le programme initial, le plan est défini par une matrice d’entiers et une seconde matrice-miroir est là pour stocker les résultats du calcul. Ces matrices de la même taille sont déclarées en globales à l’aide de deux macros TX et TY qui déterminent le nombre des cases de la zone de jeu. Les macros PASX et PASY donnent la taille en pixels de chaque case. La zone de jeu est représentée par un quadrillage centré à l’écran, les macros DECX et DECY donnent le décalage par rapport à l’origine. Le s macros Xe(n) et Ye(n) permettent d’obtenir une position à l’écran à partir d’une position dans la matrice.

aaaa#define TX 10
aaaa#define TY 10
aaaa#define PASX 40
aaaa#define PASY 40
aaaa#define DECX ((SCREEN_W-TX*PASX)/2)
aaaa#define DECY ((SCREEN_H-TY*PASY)/2)
aaaa#define Xe(n) ((n)*PASX)+DECX
aaaa#define Ye(n) ((n)*PASY)+DECY

aaaaint MAT[TY][TX];
aaaaint SAV[TY][TX];

La fonction d’initialisation met à 0 toute les positions de « MAT » puis distille au hasard des positions à 1. Ensuite la matrice souche « MAT » est recopiée dans la matrice miroir « SAV » :

aaaavoid init (void)
aaaa{
aaaaint i;
(1)aaaaa memset(MAT, 0, sizeof(int)*TX*TY);
(2)aaaaa for (i=0; i<TY*TX; i++)
aaaaaaaaaaaaif (rand()>RAND_MAX/2)
aaaaaaaaaaaaaaaaMAT[i/TX][i%TX]=1;
(3)aaaaa memcpy(SAV,MAT,sizeof(int)*TX*TY);
aaaa}

(1) Mise à 0 de la matrice MAT

(2) Des positions sont mises à 1 uniquement lorsque la fonction rand() renvoie une valeur supérieure à la moitié de valeur maximum qu’elle peut renvoyer (RAND_MAX/2)

(3) la matrice MAT est recopiée dans la matrice SAV.


3. Processus générateur, boucle d’événements

A la différence du programme précédent le développement de l’automate est interactif, piloté avec la souris ce qui donne essentiellement les lignes suivantes :

aaaaaawhile( ! key[KEY_ESC]) {
(1)aaaaaaa clic=mouse_b;
aaaaaaaaaaif (clic){
(2)aaaaaaaaaa x=(mouse_x-DECX)/PASX;
aaaaaaaaaaaaay=(mouse_y-DECY)/PASY;
(3)aaaaaaaaaa if (clic&1){
aaaaaaaaaaaaaaaacalcul(x,y);
aaaaaaaaaaaaaaaacopie();
aaaaaaaaaaaaa}
(4)aaaaaaaaaa if (clic&2)
aaaaaaaaaaaaaaaaif (x>=0 && x<TX && y>=0 && y<TY)
aaaaaaaaaaaaaaaaaaaMAT[y][x]=SAV[y][x]=1;

(5)aaaaaaaaaa scare_mouse();
aaaaaaaaaaaaaaffiche(bmp);
aaaaaaaaaaaaaunscare_mouse();
aaaaaaaaaa}
aaaaaa}

(1) le clic est récupéré dans la variable « clic »

(2) Si clic c’est la position de la souris qui est récupérée mais convertie en position matrice.

(3) S’il s’agit d’un clic gauche le calcul est effectué à partir de la position de la souris convertie en position (x,y) dans la matrice MAT et les résultats sont reportés dans la matrice SAV. La fonction calcul est modifiée par rapport au programme précédent : elle prend en argument une position dans la matrice et va s’exercer uniquement autour de cette position et non sur toute la matrice.
En revanche la fonction de recopie de SAV dans MAT est identique

(4) En cas de clic droit, la position courante de la matrice désignée par la position de la souris est mise à 1 pour MAT et SAV simultanément afin d’éviter une recopie un peu lourde.

(5) L’affichage se fait via la bitmap intermédiaire passée en argument à la fonction affiche qui gère ensuite le passage à l’écran. Les fonctions scare_mouse() et unscare_mouse() servent à cacher la bitmap du curseur souris au moment de l’affichage afin qu’il ne soit pas au dessous d’un affichage mais toujours au-dessus (sinon il se produit un trou dans l’image à l’écran lorsque sa position est modifiée ensuite).

Détail de la fonction de calcul

Le principe de la fonction calcul est identique hormis le fait qu’elle s’exerce à partir d’une position donnée (xo,yo) de la matrice.

aaaaaavoid calcul(int xo,int yo)
aaaaaa{
aaaaaa int x,y;
aaaaaa int voisin;

(1)aaaa aaafor(y=yo-1; y<=yo+1; y++){
aaaaaaaaaaaaafor (x=xo-1; x<=xo+1; x++){
(2)aaaaaaaaaaaaa if (x>0 && x<TX-1 && y>0 && y<TY-1){
(3)aaaaaaaaaaaaaaaa voisin = compte(x,y);
(4)aaaaaaaaaaaaaaaa if(voisin < 2 || voisin > 3)
aaaaaaaaaaaaaaaaaaaaaaSAV[y][x] = 0;
aaaaaaaaaaaaaaaaaaaelse
aaaaaaaaaaaaaaaaaaaaaaSAV[y][x] = 1;
aaaaaaaaaaaaaaaa}
aaaaaaaaaaaaa}
aaaaaaaaaa}
aaaaaa}

(1) Les seules cellules examinées sont celles comprises entre xo-1 et xo+1 pour l’horizontal et yo-1 et yo+1 pour la verticale.

(2) Le compte de voisins n’est possible que s’il n’y a pas de débordement du fait des cellules en bordure. Le test permet de s’assurer que la position aura bien huit positions voisines.

(3) Appel de la fonction de compte des cellules voisines sur 1. Cette fonction est identique à celle de l’automate original et suppose que x et y sont compris dans la matrice. Pour mémoire :

aaaaaaint compte (int x, int y)
aaaaaa{
aaaaaa int comptevoisin = 0;

aaaaaaaaaaif (MAT[y][x+1] ==1)
aaaaaaaaaaaaacomptevoisin++;
aaaaaaaaaaif (MAT[y][x-1] ==1)
aaaaaaaaaaaaacomptevoisin++;
aaaaaaaaaaif (MAT[y+1][x] ==1)
aaaaaaaaaaaaacomptevoisin++;
aaaaaaaaaaif (MAT[y+1][x+1] ==1)
aaaaaaaaaaaaacomptevoisin++;
aaaaaaaaaaif (MAT[y+1][x-1] ==1)
aaaaaaaaaaaaacomptevoisin++;
aaaaaaaaaaif (MAT[y-1][x] ==1)
aaaaaaaaaaaaacomptevoisin++;
aaaaaaaaaaif (MAT[y-1][x+1] ==1)
aaaaaaaaaaaaacomptevoisin++;
aaaaaaaaaaif (MAT[y-1][x-1] ==1)
aaaaaaaaaaaaacomptevoisin++;

aaaaaaaaaareturn comptevoisin;
aaaaaa}

(4) La loi de transition est la plus simple : si le nombre de voisins est inférieur à 2 ou supérieur à 3 la position courante prend la valeur 0 dans la matrice SAV du résultat et sinon elle prend la valeur 1.

Détail de la fonction d’affichage

La fonction d’affichage est quasiment identique à celle de l’automate original.

aaaaaavoid affiche(BITMAP *bmp)
aaaaaa{
aaaaaa int x, y;

(1)aaaaaaa for (y=0; y<TY; y++)
aaaaaaaaaaaaafor (x=0; x<TX; x++){
(2)aaaaaaaaaaaaa if( MAT[y][x] == 1)
aaaaaaaaaaaaaaaaaaarectfill(bmp, Xe(x), Ye(y), Xe(x+1), Ye(y+1), 150);
aaaaaaaaaaaaaaaaelse
aaaaaaaaaaaaaaaaaaarectfill(bmp, Xe(x), Ye(y), Xe(x+1), Ye(y+1), 0);
(3)aaaaaaaaaaaaa rect(bmp, Xe(x), Ye(y), Xe(x+1), Ye(y+1), 255);
aaaaaaaaaaaaa}
(4)aaaaaa acquire_screen();
aaaaaaaaablit(bmp, screen, 0,0,0,0,ECRAN_X,ECRAN_Y);
aaaaaaaaarelease_screen();
aaaaaa}

(1)(2) Toutes les positions de la matrice MAT sont passées en revue et coloriées lorsqu’elles sont à un.

(3) Est ajouté le dessin du quadrillage.

(4) Lorsque l’image est prête elle est plaquée à l’écran via un appel à la fonction « blit ».

La fonction de recopie

Cette fonction est identique à celle de l’automate original : un appel à la fonction « memcpy » de la librairie <string.h>.

aaaaaavoid copie(void)
aaaaaa{
aaaaaaaaaamemcpy(MAT,SAV,sizeof(int)*TX*TY);
aaaaaa}