Pour avoir les idées claires sur le véritable but recherché lors de l'élaboration de ce jeu, il est indispensable de préciser deux choses:
Les critères retenus tout au long de l'élaboration du jeu sont aux antipodes de la jouabilité, de l'aspect visuel, ou bien encore du côté attrayant recherchés dans les jeux classiques: le choix a été fait de ne pas perdre de vue que dans ce projet, l'intérêt principal est d'abord algorithmique.
En effet, il est ici question de déterminer dans quelles mesures l'approche réactive permet de se passer des thread pour la programmation de tâches concurrentes, ce qui nécessite une certaine rigueur: essayer de garder un sens critique et objectif, et utiliser au maximum la programmation réactive, même si cela paraît superflu dans certains cas. Il est ainsi possible d'évaluer de manière plus perspicace les situations où la programmation réactive apporte un gain appréciable par rapport à d'autres approches plus " traditionnelles" .
Le postulat servant de point de départ à la réflexion repose sur l'idée qui la conception d'un jeu de billard est intéressant pour mettre en application le modèle de programmation réactive.
En effet, l'avantage notoire de ce type de jeu est qu'on évite de mettre en oeuvre des algorithmes trop évolués comme la gestion avancée d'animation d'objets ou de personnages, les détections de collisions avec des surfaces géométriques complexes, etc... ce qui n'est pas le but du projet. En effet, dans l'absolu, simuler un billard consiste uniquement à faire évoluer des boules (formes primitives) dans un environnement suffisamment simple et restreint. Il est ainsi possible de se focaliser entièrement sur l'objectif principal du projet, à savoir contrôler de manière simple l'évolution d'entités dans un jeu, et non pas de parfaire l'aspect visuel ou ludique de ce dernier.
![]() | Le choix de faire un billard est donc judicieux: grâce au nombre de boules qu'il met en jeu, il permet de tester les facultés de synchronisation permises par la programmation réactive. De plus, les différents types de collisions pouvant intervenir au cours de la partie (avec des boules, avec les bords du billard, ...), qui impliquent autant d'algorhitmes différents à concevoir, permet de bien se rendre compte de l'étendu des facilités qu'offre le langage pour résoudre des problèmes concrets. |
Si l'on réfléchit à la manière de simuler un billard, on s'aperçoit que toutes les interactions du jeu sont déclenchées par les boules, et ont pour effet de modifier le " comportement" d'autres boules.
![]() | On entend par comportement la faculté de modifier l'état d'une boule à un instant donné, en fonction des différents scénarios susceptibles de se produire durant la partie. On peut ramener le comportement général d'une de ces boules à la composition de plusieurs comportements de base:
|
Quel est l'avantage d'une telle vision de l'architecture d'un billard ? Cela permet de mettre en exergue certaines propriétés communes aux boules:
Elles ont toutes un comportement identique. Seule la boule frappée par le joueur a un comportement légèrement différent, en ce sens qu'elle enclenche le processus de collisions avec les autres boules, et qu'elle doit revenir sur le billard si elle tombe dans un trou.
Elle peuvent toutes se comporter de manière autonome. Dans l'idéal, les boules doivent modifier leur état courant les unes après les autres. La modification de l'état d'une boule ne doit pas entrainer la modification de l'état d'une autre boule. Au contraire, un choix judicieux consisterait à éviter au maximum les interactions entre les boules. Ce cloisonnement volontaire découle d'une approche orientée objet, et permet de tirer profit de ce type de programmation, sans augmenter de manière excessive les coûts en terme de performance d'exécution.
![]() | D'un point de vue algorithmique, la détection de collision entre n boules est un algorithme polynomial d'ordre O(n2) en temps. |
Après les caractérisations de la section précédente, on voit bien qu'il aurait été dommage de procéder à une implémentation brutale consistant à imbriquer des boucles pour détecter toutes les collisions. Cela aurait exhibé les passes nécessaires au fonctionnement de l'algorithme, rendant du coup le code source plus dur à déchiffrer. De plus, il aurait fallu connaître à l'avance le nombre de boules pour effectuer les tests, ce qui aurait rendu le code encore plus diffus et difficile à maintenir.
Une autre solution aurait consisté à mettre en oeuvre une implémentation à base de thread. Cette solution semble mieux convenir aux propriétés extraites du modèle comportemental d'un billard. Les propriétés d'encapsulation des comportements d'une balle aurait pu être respectées beaucoup plus facilement, et une même fonction de test de collision aurait pu être utilisée pour chaque boule, faisant disparaître une imbrication de boucle dans la routine principale.
Mais cette possible implémentation reste très théorique, puisque les contraintes d'ordonnancement des thread aurait rendu la gestion des collisions quasi ingérable. En effet, il faut que toutes les boules puissent bouger de manière précise et puissent avoir connaissance des positions exactes de chaque boule à chaque instant. Dans la pratique, ce genre de tests a déjà été effectué par Frédéric Boussinot, l'auteur de Junior, et il a été démontré que de telles exigeances étaient impossible à assurer. Or, la gestion de collision exige un ordonnancement parfait entre tous les thread pour fonctionner, et il est impossible de pour contrôler l'ordre d'activation ou même le temps d'éxécution des thread. Enfin, le point le plus défavorable de cette technique provient du fait qu'il aurait fallu rendre l'algorithme de collision très complexe pour éviter tous les problèmes de dead-locks occasionnés par la communication entre les boules.En résumé, cette implémentation est bien trop contraignante pour être retenue.
L'approche de la programmation réactive semble conjuguer tous les avantages de l'architecture cloisonnée (objet), sans subir les contre coûts de la programmation concurrente classique. Les problèmes de synchronisation sont résolvables intrinsèquement grâce aux facilités de communication offertes par la bibliothèque réactive.
Si l'on se remémore la description proposée précédemment du modèle d'un jeu de billard, on se rend compte que les idées fortes sont bien la réaction, en communiquant à l'aide d'événements et de manière cloisonnée. Ce type de modèle est donc précisément le domaine algorithmique où la programmation réactive propose des solutions simples et efficaces à implémenter.
Il apparaît donc que ce jeu se positionne dans le bon créneau: il est suffisamment évolué pour poser des problèmes coûteux en terme de complexité en temps, et le nombre d'entités à gérer simultanément est suffisamment important pour avoir affaire à des problèmes de synchronisation non triviaux.
A posteriori, la plupart des points sensibles de la simulation du billard on effectivement trouvé une solution de conception simple, rapide à implémenter, et souvent bien plus courte que ce qui avait été envisagé lors de la phase de réflexion initiale de développement. En particulier, ce jeu a réellement permit d'utiliser toute la palette d'expressions qui découle de la dualité programmation réactive + lambda calcul, à travers le moteur Senior 2.0.