Obligement - L'Amiga au maximum

Mardi 22 août 2017 - 11:01  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

 · Accueil
 · A Propos
 · Articles
 · Galeries
 · Glossaire
 · Hit Parade
 · Liens
 · Liste jeux Amiga
 · Quizz
 · Téléchargements
 · Trucs et astuces


Articles

 · Actualité (récente)
 · Actualité (archive)
 · Comparatifs
 · Dossiers
 · Entrevues
 · Matériel (tests)
 · Matériel (bidouilles)
 · Points de vue
 · En pratique
 · Programmation
 · Reportages
 · Tests de jeux
 · Tests de logiciels
 · Tests de compilations
 · Articles divers

 · Articles in english
 · Articles in other languages


Twitter

Suivez-nous sur Twitter




Liens

 · Sites de téléchargements
 · Associations
 · Pages Personnelles
 · Moteurs de recherche
 · Pages de liens
 · Constructeurs matériels
 · Matériel
 · Autres sites de matériel
 · Réparateurs
 · Revendeurs
 · Presse et médias
 · Programmation
 · Développeurs logiciels
 · Logiciels
 · Développeurs de jeux
 · Jeux
 · Autres sites de jeux
 · Scène démo
 · Divers
 · Informatique générale


Jeux Amiga

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Trucs et astuces

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Glossaire

0, A, B, C, D, E, F,
G, H, I, J, K, L, M,
N, O, P, Q, R, S, T,
U, V, W, X, Y, Z


Partenaires

Annuaire Amiga

Amedia Computer

Relec

Hit Parade


Contact

David Brunet

Courriel

 


Programmation : C - description du code source d'A-News Life
(Article écrit par Yves Le Chevalier et extrait d'Amiga News - octobre 1990)


Comme tout débutant en C (ou en tout autre langage) le sait, une des difficultés, quand on commence à programmer, réside dans la mise en application des exemples multiples glanés dans les ouvrages. En effet, ces exemples sont souvent très bien faits mais ils doivent illustrer un point précis du langage et, de ce fait, sont souvent trop concis et hors de tout contexte.

Mon but, en écrivant ce programme (A-News Life), était de réaliser quelque chose de cohérent sans aucune prétention mais utilisant un peu des formidables possibilités graphiques de l'Amiga.

Ce programme peut servir de base à tous ceux qui veulent écrire leur premier programme sans recopier bêtement un listing auquel ils n'auront pas compris grand-chose (si, si, cela m'est déjà arrivé en me disant qu'en voyant le résultat je comprendrais de quoi il retourne...). Alors je vous propose d'explorer calmement Life...

Jeu de la vie

Tout d'abord, pour ceux qui n'ont pas encore deviné de quoi il s'agit (tout le monde n'est pas anglophile...), il s'agit du "Jeu De La Vie". Ce jeu a été inventé par John Conway de l'université de Cambridge et se jouait à l'origine avec un damier et des pions. Chaque pion représente un élément de vie ou "cellule" et obéit à des règles simples mais précises qui régissent les conditions de naissance et de mort des cellules.

A-News Life

Cette version n'a pas la prétention de rivaliser avec certaines versions ultrarapides (voir disquettes Fish : 31, 70, 111 et 131) ; le but de ce programme est de comprendre un peu mieux le C par la pratique : une façon de faire ses gammes quoi !

Ce programme a été compilé avec un Lattice 5.0.2 et utilise Intuition. Vous y verrez comment utiliser un écran, une fenêtre en haute résolution non entrelacée avec une palette de 16 couleurs, des gadgets booléens, un pointeur personnel et une routine d'affichage d'un histogramme qui s'adapte automatiquement au cadre dans lequel il s'affiche, et certainement plein de trucs qui vous intéresseront, du moins je l'espère !

Dans la première version, j'avais dessiné un fond d'écran avec Deluxe Paint et ce fond était chargé à l'aide de l'iff.library. Les gadgets étaient transparents puisque dessinés dans l'image du fond d'écran. Mais, à moins de vous obliger à taper quelques kilos de données, j'ai trouvé préférable de supprimer le fond d'écran et l'iff.library et avoir ainsi un programme plus facile à recopier. Du coup, je vous ai arrangé des gadgets avec texte et bordure qui ne sont activés que quand les fonctions qu'ils déclenchent sont permises.

Bien que le listing vous paraisse un peu long à taper, vous serez récompensé si vous allez jusqu'au bout car vous apprendrez plein de choses ce faisant. Pour ceux qui sont moins courageux avec leur clavier, DEEP mettra le source du programme en téléchargement.

Includes et constantes

En début de programme, nous trouvons les traditionnels "#include" nécessaires aux fonctions appelées (Intuition, acquisition mémoire du pointeur, etc.) ; puis viennent une série de "#define" fixant les valeurs des constantes (NB : c'est une bonne habitude mainte fois répétée d'utiliser des "defines" de constantes : une seule ligne à changer suffit à affecter toutes les instructions concernées par une modification. Et quand vous avez deux constantes qui comporte la même valeur, je vous laisse imaginer ce qui se passe si l'on veut en modifier une et que l'on n'a pas défini de nom pour cette constante... Vous pouvez aussi définir de façon très utile des abréviations de noms souvent utilisés comme par exemple RP pour RastPort).

Dans ces constantes on trouve, entre autres, les définitions de deux "fenêtres" ou cadres à l'intérieur de la véritable fenêtre (Window) d'Intuition : ce sont la fenêtre d'affichage des cellules (cadre principal) et une fenêtre plus petite où s'affiche notamment le graphique statistique des populations rencontrées.

Viennent ensuite des définitions de macros avec des opérateurs ternaires ("?", ":") qui seront utilisés dans la fonction Action() pour définir le champ d'exploration des cellules voisines d'une cellule sans déborder du cadre. Les cellules voisines sont celles qui jouxtent une cellule par un coin ou un côté : il y a donc huit cellules voisines d'une cellule donnée dans le milieu du cadre, mais sur un bord il n'y en a plus que cinq et dans un coin, il n'en reste que trois.

On définit ensuite des pointeurs sur les structures (en les initialisant pour certains à NULL) et sur des fonctions ainsi que les structures NewScreen et NewWindow.

C

Définition du tableau des couleurs

Après on trouve la définition du tableau des 16 couleurs de la palette qui va nous servir. Chacune des couleurs est définie par les quantités de rouge, vert et bleu qui la compose. A ce sujet, une bonne technique pour trouver facilement un choix de couleurs qui s'harmonisent entre elles sans faire de multiples essais entrecoupés de compilations fastidieuses consiste à se servir d'un programme de dessin qui affiche la valeur de la couleur que vous sélectionnez. Vous vous constituez ainsi une palette et vous n'avez plus qu'à noter les codes couleurs pour les réutiliser dans votre programme.

La structure TextAttr spécifie quelle police de caractères nous voulons utiliser pour les affichages par l'instruction Text(). Ici nous choisissons la police Topaz de taille 8 sans style particulier (gras, italique, etc.) et nous précisons qu'il faut la chercher dans les polices système par le drapeau "FPF_ROMFONT".

Le pointeur de votre programme est un pointeur utilisateur différent de celui du Workbench. Il est défini comme pour un sprite par une suite de double mots représentant chacun une ligne du pointeur. Remarquez cependant que les deux premiers et les deux derniers mots sont à zéro. La définition de l'image du pointeur sera utilisée dans la fonction InitPoint().

Gadgets

On passe ensuite à la définition des gadgets. Il y a six gadgets de taille identique dans le coin inférieur droit de l'écran. Ils possèdent tous un cadre rectangulaire entourant un texte spécifique à chacun. C'est pourquoi je n'ai défini qu'une seule structure Border pour définir un cadre commun à tous les gadgets ; ce cadre étant composé de cinq sommets (le premier et le dernier se confondant) définis par leur coordonnées relatives décrites dans le tableau BordXY[]. Chaque gadget a un cadre de 101 pixels horizontaux pour 21 pixels verticaux.

C

Chaque gadget est donc défini par une structure Gadget chaînée au gadget précédent et fournissant ses coordonnées absolues, sa taille (101x21), son mode de sélection (complémentation des couleurs), son type (booléen immédiat), la structure Border qu'il utilise, la structure Text qui décrit son contenu, et un numéro propre qui sera utilisé pour identifier le gadget sélectionné en cours de programme.

A chaque gadget une structure intuiText décrit le texte inscrit dans le gadget ainsi que sa couleur, sa position relative dans le gadget et son mode d'affichage (JAM1 indique une écriture avec la couleur de caractère spécifié mais en laissant le fond inchangé).

NB : pour définir des gadgets dans une fenêtre Intuition dont on peut modifier la taille ou la position, il aurait fallu indiquer non pas des coordonnées absolues, mais des coordonnées relatives à la fenêtre afin que ceux-ci suivent toujours les mouvements de la fenêtre.

Déclaration de variables

Pour finir avec la partie "préambule" du programme, nous trouvons la déclaration de quelques variables et tableaux de variables puis la déclaration par anticipation des fonctions qui composent le programme.

Remarquez que les fonctions admettant des paramètres sont prototypées (ça aussi c'est une bonne habitude à prendre !).

La boucle main()

Enfin, nous voilà arrivés dans la partie active du programme... Vous voyez ce n'est pas si long que ça, et croyez-moi, c'est beaucoup plus rapide à lire qu'à écrire.

La boucle principale (main) : elle ouvre les bibliothèques graphiques et Intuition, appelle la fonction d'ouverture d'écran et de la fenêtre Intuition, affiche le titre et le sous-titre du programme puis entre dans deux boucles "tant que" imbriquées. La première boucle attend que le booléen de fin ait été positionné pour sortir du programme tandis que la seconde boucle attend la sélection d'un gadget pour appeler la fonction correspondante.
  • Le gadget 1 affiche les instructions du programme dans le cadre secondaire, en bas de l'écran.
  • Le gadget 2 permet de dessiner la population initiale de cellules à l'aide de la souris dans le cadre principal.
  • Le gadget 3 lance la boucle de vie des cellules à condition qu'il y ait une population de plus de 0 individu.
  • Le gadget 4 positionne simplement le booléen de fin qui permet de sortir de la boucle principale et donc du programme.
  • Le gadget 5 affiche les statistiques de variation des populations depuis le peuplement initial à condition qu'il y ait au moins une génération et une population de plus de 0 individu.
  • Le gadget 6 arrête la fonction en cours (gadget Stop). Son test dans la fonction main n'est pas vraiment indispensable et il se contente d'afficher l'état d'arrêt des fonctions. Il est répété à ce niveau par souci d'exhaustivité dans les tests de gadgets.
C

La fonction InitScrWind()

Elle définit les structures NewScreen et NewWindow et ouvre la fenêtre et l'écran d'Intuition. Il s'agit dans le cas présent d'une fenêtre plein écran sans cadre ni titre, ni gadget de dimensionnement ou de profondeur. L'instruction LoadRGB4 charge notre palette de couleurs dans le viewport puis l'instruction RemakeDisplay() fait calculer la nouvelle liste Copper de l'écran Intuition. Cette fonction appelle à son tour la fonction d'initialisation de notre pointeur puis celle d'activation des gadgets pouvant fonctionner à ce moment.

La fonction InitPoint()

Elle acquiert une zone mémoire en mémoire Chip dans laquelle on recopie la définition du pointeur. Les registres 17, 18 et 19 servent à définir les couleurs du pointeur par les composantes rouge, vert et bleu, donc de façon indépendante de notre palette de couleurs. Enfin, l'instruction SetPointer() permet d'activer le pointeur en indiquant sa taille et la position relative de l'image par rapport au "hot spot" (le point actif) du pointeur.

C

La fonction Instruc1()

Elle affiche dans le cadre secondaire quelques instructions générales de fonctionnement du programme. Comme à chaque génération, il y a des cellules qui naissent, d'autres qui meurent et d'autres encore qui survivent, celles qui naissent sont de couleur bleue au moment de leur naissance et celle qui meurent sont de couleur brune au moment de leur mort. Les cellules qui restent en vie sont de couleur jaune. La petite boucle de cette fonction permet d'afficher simplement le texte voulu contenu dans le tableau doc[].

C

La fonction Instruc2()

Elle est activée au moment du peuplement initial et affiche dans le cadre secondaire des instructions se rapportant à la fonction de dessin de la population initiale dans le cadre principal. Il y a ici la même boucle que précédemment pour l'affichage du tableau contenant le texte des instructions.

La fonction PeupleInit()

Cette fonction permet le dessin à la souris des cellules composant la population de départ. Elle commence par effacer le tableau des cellules en mémoire, le tableau du nombre de cellules de chaque génération et met à zéro le nombre de générations et la population maximale atteinte. La création des cellules se fait en appuyant sur le bouton gauche de la souris (SELECTDOWN) tandis que pour l'effacement il suffit d'appuyer sur le bouton droit de la souris (MENUDOWN).

Ainsi, selon le bouton enfoncé, on trace des cellules ou on en efface d'autres ou encore on déplace le pointeur sans toucher aux cellules (si au lieu de dessiner un carré pour chaque cellule par Rectfill() on traçait simplement un point par WritePixel(), on pourrait dessiner...).

Une cellule est mémorisée dans le tableau de travail celt[][] et on appelle la fonction TracerCellule() pour que le cadre principal de l'écran soit le reflet graphique du tableau contenu en mémoire. On arrête la fonction de dessin initial des cellules en cliquant sur le gadget Stop (gadget n°6). Ceci appelle aussitôt la fonction RestoreCell() qui recharge le tableau des cellules à partir du tableau de travail que l'on vient d'initialiser.

La fonction Diodes()

Elle permet de simuler l'allumage et l'extinction de deux diodes, une rouge et une verte, dans le coin supérieur gauche de l'écran : ces diodes traduisant l'état de fonctionnement ou non du programme.

C

La fonction Action()

C'est cette fonction qui gère la vie des cellules et détermine l'évolution des populations à chaque génération. Une génération correspond à un cycle de cette fonction, mais tout d'abord il faut préciser quelles règles régissent nos cellules :
  • Chaque cellule entourée de deux ou trois cellules voisines reste en vie.
  • Chaque cellule n'ayant aucune cellule voisine ou n'ayant qu'une seule voisine meurt de solitude.
  • Chaque cellule ayant plus de trois voisines meurt par surpopulation.
  • Chaque emplacement vide ayant trois cellules voisines donne naissance à une nouvelle cellule.
C

Ces règles extrêmement simples comme on le voit vont donner naissance à une activité curieuse dans le "monde" constitué par notre cadre principal : les cellules vont naître et disparaître en dessinant des groupes de population aux formes de plus en plus géométriques. Petit à petit vont se former deux types de groupes de cellules : des groupes autostables survivants de façon continue sans varier de forme, et des groupes oscillants qui vont changer de forme indéfiniment de façon alternative ou cyclique.

Cette fonction boucle tant que l'on ne clique pas sur le gadget Stop ou tant qu'on n'a pas atteint le nombre maximum de générations "NG" (ce nombre correspond au nombre maxi de colonnes pouvant être représentées dans le graphique des statistiques compte tenu de la résolution de l'affichage). A chaque génération on inscrit dans le cadre secondaire les comptages du nombre d'individus, du nombre de naissances et celui des cellules mourantes.

C'est ici qu'on se sert des macros rencontrées en début de programme et qui permettent de définir le champs des cellules voisines de chacune des cellules qui sont testées à tour de rôle. Le comptage des voisines s'arrête à quatre puisque même au-delà de ce nombre la cellule centrale disparaîtra par étouffement. Bien entendu, on exclue du comptage la cellule centrale elle-même.

Cette opération se renouvelle donc à chaque génération pour chacune des 2208 cellules potentielles de notre tableau. Pour ne pas influencer la vie des autres cellules de la même génération, on reporte les résultats dans le tableau de travail celt[][] puis en fin de chaque génération on appelle la fonction RestoreCell() qui réinitialise le tableau principal avec la nouvelle situation et redessine dans le cadre principal les cellules encore en vie à la fin de la génération.

La fonction AffCompt()

Elle affiche les comptages de chaque génération en utilisant un tableau local lib[] contenant les messages à afficher et adressé par un indice fourni en argument à la fonction. Le compteur associé à ce message est transformé d'entier en chaîne de caractères par appel de la fonction Itoa().

C

La fonction RestoreCell()

Celle-ci se contente de restaurer le tableau de cellules cell[] à partir du tableau de travail celt[][] et de dessiner chaque cellule vivante dans le cadre principal de l'écran. En fin de fonction, un test permet de stocker éventuellement les populations minimales et maximales atteintes ainsi que la population de la génération en cours.

La fonction TracerCellule()

Elle dessine un petit carré de la couleur spécifiée par l'argument "coul" aux coordonnées fournies par les arguments "x" et "y". Mais les coordonnées fournies en argument sont celles du tableau en mémoire cell[][] et doivent donc être converties en coordonnées cartésiennes sur l'écran dans le cadre principal réservé à cet affichage. Cette conversion se fait automatiquement dans l'instruction RectFill() par rapport aux constantes définissant les coordonnées d'origine du cadre d'affichage et celles de la taille d'un carré représentant une cellule.

La fonction Stat()

Celle-ci n'est pas la moins intéressante dans la mesure où elle permet de dessiner un histogramme des populations de chaque génération en représentant en vert la population maximum atteinte et en rouge la ou les générations ayant atteint la population minimale. Cet histogramme s'adapte automatiquement au cadre secondaire dans lequel on l'affiche, quel que soit le nombre de générations et les nombres maxima et minima des populations rencontrées.

En surimpression au tracé graphique, on affiche en clair les messages indiquant les chiffres du nombre de générations, de la population maximale et de la population minimale atteintes.

C

Je vous laisse examiner cette fonction pour comprendre son fonctionnement. En fait, c'est assez simple : il suffit de diviser la largeur du cadre d'affichage par le nombre de générations pour connaître la largeur de chaque barre de l'histogramme; et de la même manière, on détermine le pas vertical du tracé en sachant que la hauteur totale du cadre correspond à la population maximale.

La fonction Itoa()

J'ai inclu cette fonction dans le programme pour les besoins de cet article car habituellement, je la stocke dans une bibliothèque d'objets précompilés et je l'appelle au moment de l'édition de liens (Blink) avec mon programme objet. Cette fonction permet la conversion de n'importe quel entier en une chaîne de caractères destinée à être la plupart du temps affichée par l'instruction Text().

La fonction GadgOnOff()

Elle permet de n'activer que les gadgets ayant une action possible à un moment donné. On appelle cette fonction en donnant sous forme booléenne les gadgets actifs (1) et les gadgets non actifs (0). Les gadgets non actifs sont alors dessinés en "fantômes" tandis que les gadgets actifs sont dessinés en traits pleins. Je vous rappelle à ce propos que la fonctionnalité dite "Mutual Exclude" n'est pas opérationnelle dans Intuition bien qu'elle figure dans certàins ouvrages. Voici donc une façon aisée de rendre vos gadgets plus attractifs selon vos désidératas.

C

La fonction FinProg()

Allez, devinez ce qu'elle fait ? Si vous hésitez à répondre ne serait-ce qu'un instant soyez sur que vous dormez au moins d'un oeil depuis un moment... Pour ceux qui ne roupillent pas encore (alors là bravo, je leur tire mon chapeau parce qu'ils ont vraiment du mérite) je termine mon exposé soporifique : et bien, comme son nom l'indique, cette fonction met fin au programme en fermant les bibliothèques qui ont été ouvertes et libère la mémoire acquise pour notre pointeur personnel (attention à procéder à la fermeture des bibliothèques ouvertes dans l'ordre inverse de leur ouverture. On commence par la dernière ouverte et on termine par la première).

On vérifie aussi avant de fermer une bibliothèque qu'elle a bien été ouverte, cela permet l'utilisation de cette fonction en cas d'anomalie en cours d'ouverture de celles-ci. Cette fonction affiche sur l'écran Workbench à partir duquel le programme a été lancé le message fourni en argument. Cela permet ainsi de savoir ce qui s'est passé en cas de fin impromptue du programme suite à une anomalie quelconque.

Conclusion

Nous voilà à la fin de ce programme et j'espère que vous n'aurez pas trouvé cela trop long mais au moins vous avez un exemple assez complet de diverses fonctions. Pour les goguenards au fond de la classe qui ont appris le C dès qu'ils ont abandonné leurs changes complets, je rappelle que cet exemple s'adresse à des débutants et n'a pas la prétention d'être un modèle parfait de programmation. Il a l'avantage s'il est bien compris (je l'espère) de pouvoir aussi servir de base à d'autres utilisations en modifiant un peu telle ou telle fonction.

Le jeu A-News Life est accessible à tous ceux d'entre vous qui disposent d'une durite de téléchargement pour raccorder leur petit Amiga à un bête Minitel et la dernière version du programme de treuillage de DEEP. "L'exécutable", comme on dit quand on connaît les mots magiques, est rangé dans la rubrique téléchargement munie de l'étiquette "A-News". Comme ça, vous pourrez voir le résultat et méditer par vous-même. Mais ce n'est pas tout, le source du jeu A-News Life est disponible selon le même principe et sensiblement au même endroit mais c'est de la triche.


[Retour en haut] / [Retour aux articles]