INFORMATIQUE
TP 10
Jeu de la Vie
L'un des premiers programmes qui essayait de simuler le processus d'expansion de la vie était "Life" de J. Conway en octobre 1970.
Le principe de base de ces programmes consiste à calculer les générations successives en partant d'une population initiale.
Notre population initiale sera représentée par les cellules d'une grille. Nous utiliserons des cellules très petites et chaque cellule pourra contenir une bactérie. La présence d'une bactérie sera matérialisée par la couleur rouge.
Les règles suivantes déterminent la naissance, la survie ou la mort d'une bactérie :
- Chaque bactérie ayant 2 ou 3 voisines survit pour la génération suivante.
- Chaque bactérie ayant 4 voisines ou plus meurt par surpopulation.
- Chaque bactérie n'ayant qu'une voisine ou aucune voisine meurt d'isolement.
- Chaque cellule vide entourée exactement de 3 bactéries donne naissance à une nouvelle bactérie.
2. Mise en place des composants
La liste des composants figure dans le tableau ci-dessous :
Fiche principale (Form) Name fmVie Caption Jeu de la vie StringGrid Name grMilieu ColCount 100 RowCount 70 FixedCols 0 FixedRows 0 DefaultColWidth 4 DefaultRowHeight 4 Height 353 Width 505 ScrollBars ssNone Button1 Name btInitialiser Caption &Initialiser Button2 Name btDepart Caption &Départ Enabled False Button3 Name btStop Caption &Stop Enabled False Button4 Name btQuitter Caption &Quitter Mettre en place, sans plus attendre le code habituel en réponse à un clic sur le bouton btQuitter.
Enregistrer tout :
- l'unité " Unit1 " sous le nom uVie dans un nouveau dossier
- le projet " Project1 " sous le nom Vie dans ce même dossier
Si on lance l'application, l'écran doit alors ressembler à ceci :
3. Initialisation du jeu
Il s'agit d'ensemencer, en quelque sorte, la grille avec 1000 bactéries disposées au hasard (la grille dispose de 7000 cases). La présence d'une bactérie dans la grille sera marquée par la présence du chiffre 1. Cependant, comme les cellules sont très petites (4 x 4 pixels) le contenu des cellules sera invisible. C'est la raison pour laquelle nous devrons dessiner la cellule en rouge lorsqu'elle sera " infectée " par une bactérie.
Pour cela , il nous faut agir sur l'événement OnDrawCell de la grille :procedure TfmVie.grMilieuDrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
begin
with grMilieu do
begin
if cells[ACol,ARow]='' then Canvas.brush.Color:=clWhite
else Canvas.Brush.Color:=clRed;
Canvas.Brush.Style:=bsSolid;
Canvas.Rectangle(Rect.Left-1,Rect.Top-1,Rect.Right+1,Rect.Bottom+1);
end;
end;Ajouter une variable globale nommée blStop de type boolean : la déclaration de cette variable doit se faire juste après la variable fmVie : TfmVie. Cette variable nous permettra d'interrompre l'évolution d'une population bactérienne.
L'initialisation du jeu se fera sur un clic sur le bouton btInitialiser. Voici ce qu'il faut mettre dans le code de la procédure btInitialiserClick :
- Effacer toutes les cellules de la grille : ceci peut se faire colonne par colonne (voir TP8 p4) ou ligne par ligne (utiliser alors Rows au lieu de Cols)
- Initialiser le générateur de nombres aléatoires (voir TP1 p3)
- Affecter la valeur False à la variable blStop.
- Calculer 1000 couples de coordonnées au hasard et affecter '1' dans chacune des cellules ainsi définies. On évitera d'infecter les cellules qui sont au bord de la grille (pour des raisons partiques de calcul que nous verrons plus loin). Comme la grille comporte 100 colonnes (de 0 à 99) et 70 lignes (de 0 à 68), on pourra faire comme ceci :
for k:=1 to 1000 do
begin
x:=1+random(98); // x est un entier compris entre 1 et 98 inclus
y:=1+random(68); // y est un entier compris entre 1 et 68 inclus
grMilieu.Cells[x,y]:='1';
end;- Remettre la propriété Enabled du bouton btDepart à True.
Voici ce qu'on doit obtenir lorsqu'on clique sur le bouton Initialiser :
4. Départ du jeu
Il s'agit maintenant de calculer et d'afficher les générations successives de bactéries. Ce processus se fait dans une boucle qui s'arrête uniquement lorsqu'on clique sur le bouton Stop ou sur le bouton Quitter. On peut tout de suite taper le code de la méthode btStopClick :
procedure TfmVie.btStopClick(Sender: TObject);
begin
blStop:=True;
btStop.Enabled:=false;
btDepart.Enabled:=true;
btInitialiser.Enabled:=true;
end;Il nous faut également modifier le code de la méthode btQuitterClick :
procedure TfmVie.btQuitterClick(Sender: TObject);
begin
btStopClick(sender);
Close;
end;Pour le calcul d'une nouvelle génération, il faut procéder en 2 temps :
- Commencer par faire une copie de la grille, dans un tableau de même taille que la grille. La cellule (x,y) du tableau contiendra le chiffre 1 si la cellule (x,y) du composant grMilieu contient le " texte " '1' et le chiffre 0 sinon.
- Déterminer, pour chacune des cellules du tableau, le nombre de cellules voisines qui sont infectées. Ce nombre détermine ensuite, en fonction de la présence ou non d'une bactérie, s'il y a une naissance ou une mort d'une bactérie. Ce nombre sera calculé à l'aide de la fonction suivante :
function NbCellulesInfectees(i,j:byte):byte;
begin
NbCellulesInfectees:=
TableauVie[i-1,j-1]+TableauVie[i,j-1]+TableauVie[i+1,j-1]+
TableauVie[i-1,j]+TableauVie[i+1,j]+
TableauVie[i-1,j+1]+TableauVie[i,j+1]+TableauVie[i+1,j+1];
end;Cette fonction utilise la variable TableauVie qui doit être déclarée globalement, juste après la variable blStop : TableauVie : array[0..99,0..69] of byte;
La boucle principale de la méthode btDepartClick pourra être la suivante :
repeat
for x:=1 to 99 do for y:=0 to 69 do
if grMilieu.cells[x,y]='1' then TableauVie[x,y]:=1
else TableauVie[x,y]:=0;
for x:=1 to 98 do for y:=1 to 68 do
begin
n:=NbCellulesInfectees(x,y);
if TableauVie[x,y]=1 then
begin
if (n<2)or(n>3) then grMilieu.cells[x,y]:=''
end
else if n=3 then grMilieu.cells[x,y]:='1';
end;
grMilieu.Refresh;
Application.ProcessMessages;
until blStop;L'instruction grMilieu.Refresh est indispensable pour permettre à l'application d'actualiser l'affichage de la grille. Sinon, la grille ne s'actualiserait que lorsque les calculs s'arrêteraient.
L'instruction Application.ProcessMessages est également indispensable. Elle permet d'interronmpre les calculs afin que Windows puisse gérer les éventuels événements en attente de traitement. Si on ne faisait pas cela, un clic sur le bouton Stop ne serait jamais réalisé.
Avant cette boucle principale, il faut :
- Mettre la propriété Enabled du bouton btStop à True,
- Mettre la propriété Enabled du bouton btDepart à False,
- Mettre la propriété Enabled du bouton btInitialiser à False,
- Affecter la valeur False à la variable blStop.
Au bout quelques générations, voici ce qu'on peut obtenir :