Xavier Lacot <xavier@lacot.org>, Mastère IDL, ENST
Ce document constitue la présentation du monde virtuel COMICS, programmé en Eiffel dans le cadre du projet SmallWorld. COMICS permet à l'utilisateur d'observer l'évolution du monde et de ses habitants, qui interagissent en fonction de leur rencontres et/ou de leur nature.
COMICS est un monde mettant en scène les personnages habituels des comics. Pour simplifier, le monde est constitué de méchants et de gentils, dont les actions correspondent globalement à celles que l'on trouve dans toute bande dessinée.
Le programme repose sur un système de jours. Au premier jour, c'est à dire au démarrage du programme, il est demandé à l'utilisateur de rentrer certaines données quantifiées caractérisant le monde à observer : superficie, population, objets disponibles. Chaque jour, les créatures peuvent effectuer un déplacement et une action, sauf si elles sont mortes ou qu'elles se trouvent en prison. Les notions d'age et de vieillissement sont prises en compte dans le jeu, les créatures perdant toutes un point de vie par jour (hormis les créatures détenues en prison, qui en perdent trois).
La monnaie officielle de COMICS est le batdollar, en référence évidemment à Batman
L'utilisation du hasard dans certaines décisions des créatures permet d'assurer une évolution non planifiée du monde.
Le code source du logiciel et cette présente documentation sont publiés sous licence Creative Commons Attribution-NonCommercial-ShareAlike 2.0, et peuvent être obtenus dans l'archive se trouvant à l'adresse suivante : http://lacot.org/public/enst/comics-an-eiffel-smallworld.tar.gz.
Par soucis de rapidité et de clarté, cette documentation utilise la feuille de style empoyée par le W3C pour la publication de Recommandations. Les règles de licence suivantes s'appliquent à cette feuille de style : http://www.w3.org/Consortium/Legal/copyright-software
Cette archive peut être employée en effectuant les commandes suivantes :
$ tar xzvf comics-an-eiffel-smallworld.tar.gz $ cd comics-an-eiffel-smallworld $ make
Le Makefile fournit dans l'archive prévoit l'emploi de la commande "compile" du compilateur Smarteiffel.
Afin de définir une notion de proximité entre les êtres présents dans COMICS, une géographie simpliste a été définie, sous forme de grille rectangulaire dont l'utilisateur définit les dimensions au démarrage du système. Dans cette grille, les objets et les êtres sont repérés par leurs coordonnées (x, y)
Une commande permet à l'utilisateur de visualiser une carte du monde, alors représenté sous forme de cases, dont le contenu indique le nombre de créatures présentes dans la case correspondante. Dans l'exemple ci-dessous, on observe ainsi que la case (3, 0) du monde contient 4 créatures.
(C)ontinuer, (D)etails, (M)Carte, (Q)uitter ? M 0 1 2 3 4 5 6 7 8 9 0 4 1 1 1 2 2 1 3 1 1 2 1 1 4 1 1 1 1 5 1 1 1 6 1 7 1 1 8 1 1 1 2 9 1 1 1
Lorsque le niveau de vie d'une créature atteint 0, elle meurt, mais son cadavre encore chaud reste visible dans le monde des créatures. Ce n'est que pendant la nuit, lorsque toutes les créatures sont calmes, que les êtres décédés sont transportés jusqu'au cimetière.
Une créature qui se trouve au cimetière étant morte, elle ne peut ni se déplacer ni effectuer d'action.
Il peut arriver que des créatures se fassent arrêter par les autorités de COMICS. Dans ce cas, elles sont transférées la nuit venue à la prison.
Une créature qui se trouve en prison ne peut pas se déplacer. Par contre, les gardiens sont assez laxistes et ne placent les détenus en cellule de sécurité qu'après trois tentatives d'évasion consécutives et infructueuses. Un prisonnier en cellule de sécurité ne peut sortir de prison qu'en payant la somme de 1000 batdollars à l'état.
La prison étant un lieu très stressant, un être qui y est détenu perd trois points de vie par jour.
Schéma d'organisation des classes du système.
COMICS est la classe générale du système, UTILS
ne contient qu'un générateur de nombres
aléatoires.
Une classe CREATURE
a été écrite, afin de
représenter les caractéristiques communes à tous les
êtres vivants :
Cette classe ne peut pas être instanciée, c'est une
classe abstraite. Cependant, elle définit certaines
methodes communes à tous les types de créatures du
monde : accesseurs, naissance, utilisation d'objet,
etc. Les méthodes get_premier_objet(objets:
FAST_ARRAY[OBJET]): OBJET
et
get_premiere_creature(creatures:
FAST_ARRAY[CREATURE]; type: STRING): CREATURE
ont pour rôle de renvoyer un objet ou une creature
présente dans la même case que la créature courante,
et répondant à certaines caractéristiques. Ces deux
méthodes sont très utilisées, notamment, dans la
définition des actions possibles pour chaque type de
créature en fonction de son environnement.
La classe CREATURE
propose quelques
méthodes retardées, dont get_type: STRING
.
Cette méthode, définie dans toute classe héritée de
CREATURE
, renvoie sous forme de
STRING le
type de la créature.
Les animaux sont somme toute des créatures relativement basiques. Leur présence dans le monde n'est destinée qu'à fournir une source d'alimentation pour les humains. Les animaux peuvent brouter de l'herbe et se déplacer de 3 cases chaque jour.
Les humains permettent d'introduire la notion de richesse. Chaque humain naît avec 1000 batdollars en poche, qu'il dépense, gagne ou perd au grès de ses rencontres ou de ses trouvailles.
En plus des actions que peuvent effectuer les créatures basiques, les humains peuvent se faire prendre en photo avec un Superheros présent dans la même case que lui, ou encore manger un animal proche de lui... s'il parvient à attraper celui-ci !
Comme dans la réalité, un policier est un humain dont le rôle est de protéger et ordonner le monde. Pour cela, chaque policier est équipé d'un revolver, qu'il doit approvisionner en munitions, et dispose d'actions bien spécifiques :
Lorsqu'un policier parvient à abattre un superméchant, il lui fait généralement les poches et arrondit ainsi ses fins de mois...
C'est des superméchants que vient tout le désordre qui règne dans COMICS. Sans eux, COMICS serait un monde en paix, où les créatures vivraient en harmonie, sans se faire de mal (oui, c'est effectivement un peu naïf...). C'est sans doute la raison pour laquelle le champ d'actions que peut réaliser un superméchant est assez vaste. On distingue deux cas :
Étant à la fois des humains et des animaux, les superhéros peuvent se déplacer à la vitesse des animaux (3 cases/jour), tout en réalisant les actions des humains basiques. Ceci dit, un superhéros n'en serait pas un sans certaines finesses :
Afin de rendre le monde plus vivant et attractif, il a été décidé d'introduire des objets dont l'utilisateur fixe le nombre au démarrage du programme, et avec lesquels les créatures peuvent interagir. On trouve ainsi :
Il peut y avoir dans le monde plusieurs occurences de chaque type d'objet. Un objet est utilisé instantanément après sa découverte par une créature.
La classe OBJET
n'a cependant pas été
suffisamment développée pour permettre une utilisation
intelligente et cohérente des objets par les créatures.
Ainsi, il pourra arriver dans le système qu'un animal
trouve de l'argent, et qu'il l'utilise. Afin d'éviter
toute incohérence, une méthode vide
gagner(somme: INTEGER)
a donc été créée
dans la classe ANIMAL
.
L'interactivité du système est volontairement limitée, de manière à éviter que l'utilisateur ait trop d'actions à réaliser au cours d'une évolution du monde.
Au démarrage du logiciel, l'utilisateur se voit poser plusieurs questions dont les réponses permettent de définir les caractéristiques principales du monde :
Activer le deroulement automatique
(O)ui, (N)on ?
Un mode automatique a été prévu dans le monde.
Avec ce mode automatique, les jours défilent
sans pause, ce qui fait que l'utilisateur ne
peut demander de détails au sujet de l'état du
monde. Cette exécution automatique rend la main
à l'utilisateur uniquement lorsque le monde
atteint un état stable, c'est à dire
dans lequel :
Taille x du monde ?
Permet de définir la "largeur" du monde.
Taille y du monde ?
Permet de définir la "hauteur" du monde. La
surface du monde (ie., le nombre de "cases"
composant sa géographie) est alors obtenue par
l'opération hauteur * largeur.
Nombre de creatures a creer ?
Permet de définir le nombre de créatures qui
vont naître et peupler le monde avant son
animation.
Nombre d'objets a placer dans
le monde ?
Permet de définir le nombre de créatures qui
vont naître et peupler le monde avant son
animation.
A chaque tour de jeu (chaque "jour"), en mode de
déroulement non automatique, l'utilisateur a le choix
entre quatre actions différentes : (C)ontinuer,
(D)etails, (M)Carte, (Q)uitter
. Voici le
détail de chacune de ces actions :
(C)ontinuer
permet de poursuivre
l'exécution et commence un nouveau jour.
(D)etails
permet d'afficher une
liste de détails exaustifs du monde. L'exemple
suivant indique l'état du monde en debut
d'exécution pour 10 créatures et 10 objets dans
un monde de 10 cases par 10 :
(C)ontinuer, (D)etails, (M)Carte, (Q)uitter ? d +------------------------------------------------------------------------------+ + CREATURES : encore 10 etres vivants dont 0 en prison +------------------------------------------------------------------------------+ 3, 0 superheros_1 vie : 85 1000 batdollars 1, 5 humain_1 vie : 22 1000 batdollars 6, 3 policier_1 vie : 56 1000 batdollars arme non degainee 6 munitions 1, 8 animal_1 vie : 17 6, 3 policier_2 vie : 55 1000 batdollars arme non degainee 6 munitions 1, 8 supermechant_1 vie : 72 1000 batdollars 6, 3 humain_2 vie : 27 1000 batdollars 4, 8 policier_3 vie : 36 1000 batdollars arme non degainee 6 munitions 9, 3 animal_2 vie : 11 4, 1 policier_4 vie : 35 1000 batdollars arme non degainee 6 munitions +------------------------------------------------------------------------------+ + PRISON : 0 prisonnier(s) +------------------------------------------------------------------------------+ +------------------------------------------------------------------------------+ + OBJETS : il reste 10 objets +------------------------------------------------------------------------------+ 2, 9 un porte-monnaie contenant 1000 batdollars 3, 7 un piege mortel 1, 8 une gourde de potion de vie 9, 6 un inverseur de coordonnees 0, 4 des munitions 8, 5 un porte-monnaie contenant 1000 batdollars 6, 3 un piege mortel 7, 4 une gourde de potion de vie 5, 2 des vapeurs de pollution chimique 6, 0 des munitions
(M)Carte
affiche une représentation
du monde sous forme de carte (cf.
Géographie).
(Q)uitter
permet d'interrompre
l'exécution courante.
Plusieurs améliorations possibles pourraient être
apportées à ce projet. On peut penser, notamment, à une
modélisation plus propre des objets, uniquement
matérialisés par un INTEGER
dans le projet
actuel.
Certaines curiosités du compilateur, observées au cours
du développement, ont mené à des modifications "à
tâtons" du code, sans qu'aucune explication rationnelle
ne puisse expliquer ces dysfonctionnements. Le plus
flagrant concerne la classe SUPERHEROS
,
lignes 56-70, dans lesquelle on souhaite employer deux
fois l'appel à Precursor
. Avec le code
suivant, la compilation se déroule sans problèmes :
56 if creature_presente(creatures, "ANIMAL") then 57 -- soigne un animal s'il est trop faible 58 animal := get_premiere_creature(creatures, "ANIMAL") 59 if animal.get_vie < 10 then 60 soigner_creature(animal) 61 end 62 else 63 -- effectue une des autres 64 -- actions disponibles 65 Precursor(creatures, objets) 66 end
Avec le code suivant, qui effectue deux appels à
Precursor
, la production d'un
exécutable échoue au niveau du linkage par ld
des fichiers compilés :
56 if creature_presente(creatures, "ANIMAL") then 57 -- soigne un animal s'il est trop faible 58 animal := get_premiere_creature(creatures, "ANIMAL") 59 if animal.get_vie < 10 then 60 soigner_creature(animal) 61 else 62 -- effectue une des autres 63 -- actions disponibles 64 Precursor(creatures, objets) 65 end 66 else 67 -- effectue une des autres 68 -- actions disponibles 69 Precursor(creatures, objets) 70 end
L'erreur produite a alors la douce allure suivante :
gavroche tp/eiffel/comics_new $ make compile -o comics comics.e comics1.c:5137: redefinition of `r79_P_46_action' comics1.c:5065: `r79_P_46_action' previously defined here /usr/ccs/bin/as: "<stdin>", line 26268: error: redefinition of symbol "r79_P_46_action" comics1.c:5179: redefinition of `r79_P_43_action' comics1.c:5107: `r79_P_43_action' previously defined here /usr/ccs/bin/as: "<stdin>", line 26609: error: redefinition of symbol "r79_P_43_action" /usr/ccs/bin/as: "<stdin>", line 26593: warning: size of "r79_P_46_action" redefined /usr/ccs/bin/as: "<stdin>", line 26711: warning: size of "r79_P_43_action" redefined Undefined first referenced symbol in file M44 comics4.o M45 comics4.o M46 comics4.o M47 comics4.o s80_7778127 comics2.o s43_425724326 comics2.o s22_1941685 comics3.o r45get_position_x comics4.o M63 comics4.o r45get_position_y comics4.o M64 comics4.o M65 comics4.o s22_603461234 comics3.o M79 comics4.o r79default comics4.o r79details comics4.o M80 comics4.o M82 comics4.o M83 comics4.o M86 comics4.o r79vivante comics4.o se_prinT1 comics4.o r45gagner comics4.o se_prinT2 comics4.o se_prinT3 comics4.o se_prinT5 comics4.o se_prinT6 comics4.o se_prinT7 comics4.o se_prinT8 comics4.o se_prinT9 comics4.o s46_1616409705 comics2.o r79get_random_integer comics4.o s80_1848995245 comics2.o s80_568530978 comics2.o s80_467284159 comics2.o s44_33574519 comics2.o r79get_position_x comics4.o r79get_position_y comics4.o se_signal_handler comics4.o r79objet_present comics4.o r45get_type comics4.o s47_1331371528 comics4.o r79is_default comics4.o s22_473935479 comics3.o s22_1313310242 comics3.o ci comics2.o gc_fsoc_get1 comics4.o gc_fsoc_get2 comics4.o s22_54515690 comics3.o r45io comics4.o s44_61410822 comics2.o r45inverser_coordonnees comics4.o s22_1432086796 comics3.o r79get_type comics4.o se_gc_check_id comics4.o r79io comics4.o r79utiliser_objet comics4.o s44_1430963478 comics2.o s43_9 comics2.o s46_1530702985 comics2.o s43_1712820107 comics2.o s22_2074145 comics3.o s22_992 comics3.o s80_1856798732 comics2.o r79gagner comics4.o s43_252 comics2.o s22_652423121 comics3.o r45soigner comics4.o s22_4622985 comics3.o gcmt comics4.o s46_845437880 comics2.o s80_518931484 comics2.o s44_346795607 comics2.o s22_1966263839 comics3.o r45est_libre comics4.o r79action comics4.o s22_420935003 comics3.o s22_2028280921 comics3.o ac_civ comics3.o vc comics2.o garbage_delayed comics4.o s22_1727813738 comics3.o r79get_premier_objet comics4.o stack_bottom comics4.o s80_1291676919 comics2.o s22_775354682 comics3.o s46_152855367 comics2.o s44_520333618 comics2.o s22_1032851696 comics3.o s22_1714894 comics3.o new_na comics4.o s46_289818 comics2.o gc_is_off comics4.o ac_inv comics2.o gcmt_used comics4.o mark_stack_and_registers comics4.o s46_1900393285 comics2.o s22_0 comics3.o s80_1530702986 comics2.o s43_1943769311 comics2.o r79get_richesse comics4.o s22_420486943 comics3.o s46_1488204435 comics2.o r45is_default comics4.o s22_557423960 comics3.o s22_164116148 comics3.o s22_851757497 comics3.o s43_1605022196 comics2.o r79soigner comics4.o r45details comics4.o s43_588747 comics2.o s43_1443750694 comics2.o r45mourir comics4.o s22_1441398327 comics3.o s80_281171235 comics2.o r79standard_streams comics4.o r45objet_present comics4.o s22_1678810106 comics3.o r79deplacer comics4.o se_require_last_result comics2.o s22_10 comics2.o s22_1630336474 comics3.o se_require_uppermost_flag comics2.o gcmt_max comics4.o s22_1176831237 comics3.o s43_599594626 comics2.o r79inverser_coordonnees comics4.o s80_434133564 comics2.o s22_32 comics3.o s43_284005346 comics2.o r45get_vie comics4.o s80_1349511683 comics2.o s22_60 comics3.o s22_67 comics3.o s22_216663156 comics3.o r45get_richesse comics4.o s22_81 comics3.o r45morte comics4.o se_dst comics2.o r45deplacer comics4.o r45emprisonner comics4.o s80_945262615 comics2.o s80_65735511 comics2.o ac_ens comics2.o s22_1405390195 comics3.o r79get_vie comics4.o se_malloc comics4.o r45get_premier_objet comics4.o r79depenser comics4.o se_prinT34 comics4.o s80_175 comics2.o se_prinT35 comics4.o s80_1049890763 comics2.o s22_113 comics3.o s22_521538372 comics3.o s31_117797837 comics4.o se_prinT41 comics4.o se_prinT44 comics4.o se_prinT45 comics4.o se_prinT46 comics4.o se_prinT47 comics4.o se_prinT10 comics4.o se_prinT11 comics4.o r45get_random_integer comics4.o s47_263326 comics2.o se_evobt comics2.o se_prinT22 comics4.o se_print_run_time_stack comics2.o gc_sweep comics4.o create45naissance comics3.o s45_778926303 comics2.o s22_693308564 comics3.o r79mourir comics4.o s22_1971954363 comics3.o r45blesser comics4.o oBC12standard_streams comics2.o fBC12standard_streams comics2.o s22_192 comics3.o r79est_libre comics4.o fsocfl comics4.o r79get_nom comics4.o s47_1571578954 comics4.o r45standard_streams comics4.o r45depenser comics4.o s80_1752698245 comics2.o M7 comics4.o r79blesser comics4.o s80_581773460 comics2.o se_prinT79 comics4.o s22_701318948 comics3.o se_prinT80 comics4.o se_prinT82 comics4.o se_prinT83 comics4.o se_prinT84 comics4.o se_prinT85 comics4.o se_prinT86 comics4.o se_prinT87 comics4.o s22_882043236 comics3.o ac_req comics2.o gc_find_chunk comics4.o r45default comics4.o s80_23046122 comics2.o r45vivante comics4.o se_prinT63 comics4.o se_prinT64 comics4.o se_prinT65 comics4.o r45utiliser_objet comics4.o r45action comics4.o s22_486409623 comics3.o se_rci comics2.o s44_304769429 comics2.o s46_1173507657 comics2.o r79morte comics4.o s22_1311456055 comics3.o M22 comics4.o r45get_nom comics4.o r79emprisonner comics4.o collector_counter comics4.o M34 comics4.o M35 comics4.o s22_1943246756 comics3.o gcmt_tail_addr comics4.o error2 comics4.o s79_202953028 comics2.o create79naissance comics3.o M41 comics4.o ld: fatal: Symbol referencing errors. No output written to comics collect2: ld returned 1 exit status
Le code fourni contient 13 lignes de code issues de Damien Accorsi <damien.accorsi@enst.fr>, qui a effectué les recherches concernant l'introduction d'évènements aléatoires dans le monde.