Acteurs
Le modèle basé sur les acteurs
(actor
model en anglais) est un modèle de programmation concurrent
construit sur l'idée d'un monde rempli d'acteurs
échangeant des messages. Dans le cadre de ce projet,
un message est une structure de données
constitué d'une étiquette ainsi que d'un ensemble
quelconque de paramètres (par ex.
L'objectif de ce projet consiste à mettre en place une
bibliothèque d'acteurs afin de permettre de construire un jeu
ressemblant à
un shoot'em
up12. Pour
simplifier par rapport au modèle de programmation cité ci-dessus,
nous allons considérer que les acteurs se mettent à jour de
manière synchronisée. Une horloge globale découpe le temps en
intervalles (tick en anglais). Au début d'un
intervalle de temps, les acteurs n'ont aucun message en
attente. Ils reçoivent un ensemble de messages pendant cet
intervalle (
'(move 1 0)
).
Un acteur est simplement une structure de
données possédant un état propre, et capable de recevoir et
d'émettre des messages. En fonction des messages
qu'elle reçoit, elle peut choisir de mettre à jour son
état, d'émettre de nouveaux messages, voire même de créer
de nouveaux acteurs ou de mourir de sa belle mort. Exemple
d'application très concret de ce style de programmation, le
jeu Terminal
Phase, codé en Racket par C. L. Webber, est basé sur un
modèle d'acteurs :
(image tirée
du jeu Terminal Phase
de C. L. Webber)
move
, collide
...) sans
autre modification. A la fin de l'intervalle de temps, ils
vident leur boîte de message et se mettent à jour en fonction de
ce qu'ils ont reçu. La mécanique de cette horloge globale
peut être réalisée à l'aide d'une structure de données globale
servant de runtime organisant la mise à jour
simultanée de tous les acteurs. Les messages sont alors considérés
comme reçus simultanément pendant cet intervalle3.
Description du projet
-
Écrire le code nécessaire pour définir des acteurs et des
messages. Les messages sont libres. Les acteurs sont censés
posséder au minimum une position dans l'espace. Chaque
acteur doit pouvoir répondre au message
'(move x y)
, et se déplacer de(x,y)
. Ils sont censés implémenter les fonctions suivantes :(actor-location actor) ;; Returns the actor location - actor -> location (actor-send actor msg) ;; Send a message to an actor - (actor*msg) -> actor ;; and returns an actor with a new message (actor-update actor) ;; Handles the actor list of messages - actor -> actor ;; and returns a new updated actor ;; with an empty mailbox
-
Écrire le code nécessaire (appelé
ci-après runtime) pour manipuler un
ensemble d'acteurs évoluant sur un tick
d'horloge. Pour commencer, il est conseillé de
construire une structure4
world
rassemblant l'ensemble des acteurs en jeu. À chaque tick d'horloge, cette structure pourra envoyer un message de déplacement à chaque acteur en jeu. - Écrire le code nécessaire pour visualiser la position des acteurs en jeu à chaque instant. La visualisation pourra se faire de différentes manières, mais devra être accessible depuis un terminal. Pour ce faire, l'utilisation de la bibliothèque raart est autorisée (cf. la section dédiée pour plus d'informations)
-
Adapter le code de manière à permettre aux acteurs de créer
de nouveaux acteurs.
Pour cela, il est envisageable de faire évoluer la fonction
actor-update
et de lui faire renvoyer une liste d'acteurs. Le runtime s'occupera alors de reconstituer la liste d'acteurs vivants au prochain tick. -
Adapter le code de manière à permettre de gérer les collisions
entre acteurs.
La gestion des déplacements est cruciale pour pouvoir gérer les collisions. Si l'on programme la chose de manière naïve, il est assez facile de voir que deux agents se dirigeant l'un vers l'autre risquent de passer au travers l'un de l'autre. Pour cela, il est possible de demander au runtime de gérer les déplacements indépendamment des autres messages.
-
Adapter le code de manière à permettre aux acteurs
d'envoyer des messages aux autres acteurs.
Pour cela, il est envisageable de faire évoluer la fonctionUn cas d'application possible consisterait à créer des bonus ayant un effet sur tous les acteurs ennemis (le plus simple étant leur destruction pure et simple5).
actor-update
et de lui faire renvoyer une liste de messages en plus d'une liste d'acteurs. Les messages peuvent être considérés comme retransmis à tous les autres acteurs d'un certain type. - Adapter le code de manière à permettre de remonter le temps, comme présenté dans le message du développeur de Terminal Phase.
- Animer le tout à l'aide de la bibliothèque Lux (cf. la section dédiée pour plus d'informations)
Détails techniques
Pour les besoins du projet, les fichiers devront être organisés de la
manière suivante dans le dépôt :
Le
Le paramètre passé à l'option / -- Base directory Makefile -- Global Makefile /doc -- Generated documentation files (should be empty in the repository) /src -- Source files /src/main.rkt -- Main file
Makefile
doit contenir une
règle test
permettant d'exécuter les tests
propres au projet, ainsi que d'une règle doc
pour générer la documentation (cf. la
section dédiée pour plus d'informations)). Le fichier
principal doit pouvoir être démarré en utilisant la commande
suivante :
racket src/main.rkt -f 60
-f
est le nombre
de FPS censé être atteint par le runtime (compris
entre 1
et 60
). Le nom du
fichier main.rkt
ne doit pas être modifié. A part
le répertoire doc
, d'autres fichiers peuvent
se trouver dans le dépôt s'ils sont indispensables au
projet.
Racket ASCII Art
La
bibliothèque raart,
pour "Racket ASCII Art" ou "Racket ANSI Art" selon les versions,
est une bibliothèque fournissant un ensemble de fonctions pour
dessiner du texte dans un terminal. Son fonctionnement consiste
à produire des chaînes de caractères comportant
des codes
ANSI. Ces chaînes, lorsqu'elles sont affichées dans un
terminal, peuvent prendre des formes variées en terme de style
et de couleur. Pour installer ce paquetage, il suffit de :
Un exemple
extrêmement simple de code Racket, qui, exécuté dans un
terminal, produit un résultat d'une esthétique foudroyante,
est fourni ci-après :
La
documentation fournit la liste des fonctions
disponibles. Le programmeur avisé reconnaîtra un style de
programmation fonctionnelle, avec de nombreuses fonctions
transformant des valeurs répondant à
raco pkg install raart
drracket
n'étant pas un terminal, il
n'interprétera pas lesdites chaînes de caractères et
affichera bêtement "[0mHello".
|
raart?
,
permettant d'obtenir une image par composition de
fonctions, comme dans l'exemple ci-dessus.
Lux
La
bibliothèque Lux
est une bibliothèque permettant de créer des "applications
graphiques temps-réel". Pour installer ce paquetage, il suffit
de :
La documentation permet de se faire une idée du fonctionnement de la bibliothèque, mais n'est pas indispensable à la réalisation du projet.
raco pkg install charterm lux
Dans le cadre de ce sujet, nous nous limiterons aux capacités
de la bibliothèque de faire du temps-réel à l'intérieur
d'un terminal. Il n'est pas
nécessaire d'avoir une compréhension complète de son
fonctionnement pour réaliser le sujet, la lecture de cette
section devrait suffire.
Un exemple
d'utilisation de cette bibliothèque d'un raffinement
insoutenable est fourni avec le sujet (la
touche "q"
permet de quitter le programme). Il
consiste à afficher le déplacement du caractère ">"
sur toutes les lignes du terminal.
Le fonctionnement de la bibliothèque lux
consiste à
créer une structure représentant l'état global de
l'application. Dans le cas présent, la structure
s'appelle world
, et contient uniquement un
entier tick
représentant le tick d'horloge de
l'application.
Il suffit alors d'implémenter un ensemble de fonctions
agissant ou transformant cet état global, à savoir :
word-fps : |
world -> double |
renvoie le nombre de FPS que le runtime essaie d'assurer / pratique pour débugguer |
word-label : |
world -> string |
renvoie le titre de la fenêtre contenant l'application |
word-event : |
world * event -> world |
à partir d'un état du monde et d'un événement, renvoie un nouvel état du monde |
word-output : |
world -> raart |
à partir d'un état du monde, renvoie un objet graphique à afficher |
word-tick : |
world -> world |
à partir d'un état du monde, crée un nouveau monde apparaissant après un tick d'horloge |
La documentation permet de se faire une idée du fonctionnement de la bibliothèque, mais n'est pas indispensable à la réalisation du projet.
Ecrire des tests et de la documentation
Les tests en Racket s'écrivent avec la
bibliothèque RackUnit.
Le fichier
suivant peut servir de base à l'écriture
de tests pour la bibliothèque d'acteurs. Les tests sont exécutés
comme un fichier Racket standard, et un compte-rendu de test est
produit. Ils sont rangés dans un ou plusieurs ensembles de tests
nommés test suites, chaque test étant
rangé dans un test case.
Le fichier fourni n'est d'ailleurs pas un très bon exemple de
test, parce qu'il mélange différents tests ensemble, alors qu'il
vaudrait mieux les découper en petits cas de tests.
La documentation en Racket s'écrit avec un outil
appelée Scribble. Il
s'agit d'un outil de description de document
ressemblant un peu au langage LaTeX. Concrètement, il
s'agit d'écrire un fichier
.scrbl
, qui
sera ensuite transformé dans un format de documentation à
l'aide de la commande suivante :
scribble --dest doc actors.scrbl
Il n'est pas demandé de construire un
projet compatible avec raco
, le système de gestion
de projets de Racket en ligne de commandes. La documentation
doit simplement servir à préciser le fonctionnement de votre
code à un utilisateur externe. Il n'est
pas non plus demandé d'écrire du code en literate
programming6.
Recommandations
Pour l'ensemble du projet, il est recommandé de
systématiquement :
- Définir formellement les éléments nécessaires pour représenter le jeu, et les relier à leur implémentation.
- Définir formellement les algorithmes proposés, et étudier leur complexité (temps/mémoire).
- Caractériser dans les algorithmes et structures de données ce qui ressort de la programmation fonctionnelle et ce qui n'en est pas.
- Suivre les standards de codage développés dans le cours de programmation fonctionnelle et disponibles à l'adresse suivante.
Contact
Pour toute information complémentaire, contacter : David RENAULT.
1. | Il est clairement indiqué ici que l'objectif du projet n'est pas de réaliser un jeu vidéo, mais si ce n'était pas clair, cette note de bas de page enfonce le clou. |
2. | Le sujet n'impose pas le style de jeu utilisé pour démontrer la bibliothèque, il demande simplement à ce qu'il soit possible d'illustrer le fonctionnement de la bibliothèque. |
3. | Ce qui posera problème lorsqu'il s'agira de détecter les collisions. |
4. | Les structures et
le mot-clé struct sont définis
ici et discutés dans l'exercice 9 de la
feuille sur les fonctionnelles et
l'exercice 7 de la
feuille sur les modules). Il est même possible de faire de l'héritage de code en écrivant les fonctions associées comme des méthodes. |
5. | Finalement, si, malgré toutes mes réticences, ce jeu sera violent. |
6. | Quoique cela ne fera de mal à personne d'aller jeter un coup d'oeil à la documentation. |