Available nodes

 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. '(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)
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, 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

  1. É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
  2. É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.
  3. É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)
  4. 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.
  5. 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.
  6. 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 fonction 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.
    Un 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).
  7. Adapter le code de manière à permettre de remonter le temps, comme présenté dans le message du développeur de Terminal Phase.
  8. 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 :
    /               -- Base directory
      Makefile      -- Global Makefile
    /doc            -- Generated documentation files (should be empty in the repository)
    /src            -- Source files
    /src/main.rkt   -- Main file
Le 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
Le paramètre passé à l'option -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 :
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 "Hello".
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 :
#lang racket
(require raart)

(draw-here
  (matte 50 20
    (frame
      (text "This is just ART !")))
)
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 à 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 :
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.