Il y a quelques semaines, en regardant des vidéos sur YouTube, je suis tombé sur une vidéo du YouTuber CODEBH qui m’a particulièrement intrigué :
https://www.youtube.com/watch?v=MTcXW94V838
Dans cette vidéo, il explique comment il a utilisé un algorithme génétique pour entraîner une IA à jouer au jeu Geometry Dash.
Le principe est assez fascinant : plutôt que de programmer explicitement une stratégie pour éviter les obstacles, on crée une population d’agents qui apprennent progressivement en évoluant génération après génération.
Malheureusement, la vidéo ne proposait ni dépôt GitHub ni explication détaillée de l’algorithme utilisé. J’ai donc décidé de recréer le projet moi-même en me basant uniquement sur les informations données dans la vidéo et j’ai décidé de le faire dans mon langage de prédilection : JavaScript.
Recréer le jeu
La première étape était évidemment de recréer le jeu lui-même.
Mon objectif n’était pas seulement d’entraîner une IA. Je voulais aussi construire un petit environnement complet permettant :
- de jouer manuellement au jeu
- de créer des niveaux
- d’entraîner l’IA
- et de rejouer les meilleurs génomes trouvés par l’algorithme
J’ai donc réfléchi à l’architecture globale de l’application.
Le projet devait inclure :
- un moteur de jeu
- un éditeur de niveaux
- un dashboard d’entraînement
- un mode simulation pour l’IA
- un mode joueur pour tester la physique du jeu
Comme recréer un clone complet de Geometry Dash pouvait prendre beaucoup de temps, j’ai décidé d’accélérer cette première étape.
J’ai demandé de l’aide à une IA pour générer un clone du jeu.
Et honnêtement, le résultat était assez impressionnant : l’IA a réussi à recréer un jeu fonctionnel assez rapidement.
Bien sûr, tout n’était pas parfait. J’ai dû corriger plusieurs bugs, notamment sur la physique du joueur et la détection de collision, mais la base était déjà là.
Ensuite, toujours avec l’aide d’une IA, j’ai généré un éditeur de niveaux. Après quelques ajustements, j’avais un outil utilisable.
Au final, en environ une heure, j’avais déjà :
- un clone jouable du jeu
- un éditeur de niveaux
- une base solide pour commencer à entraîner l’IA
Architecture du projet
Une fois le jeu en place, la partie intéressante pouvait commencer : l’algorithme génétique.
J’ai structuré mon projet autour de plusieurs classes principales :
GA Manager
orchestre les générations et la populationGA Worker
exécute les simulations dans un Web WorkerSimulator
crée un environnement virtuel pour tester un joueurAI Controller
pilote le joueur virtuelGenome
représente le “cerveau” de l’IAGenetic Algorithm
gère sélection, reproduction et mutation
L’objectif était de pouvoir simuler beaucoup de joueurs simultanément.
Pour entraîner efficacement l’IA, je voulais pouvoir tester 1000 joueurs par génération. Pour éviter de bloquer le thread principal du navigateur, j’ai donc exécuté les simulations dans un Web Worker.
Chaque joueur virtuel joue une partie complète et obtient un score basé sur la distance parcourue.
Comment fonctionne le “cerveau” de l’IA
Plutôt que d’utiliser un réseau de neurones classique, j’ai utilisé un système beaucoup plus simple basé sur des capteurs (triggers).
Chaque génome est composé de plusieurs réseaux de capteurs.
Dans mon implémentation :
- un génome contient plusieurs réseaux
- chaque réseau contient plusieurs capteurs
La logique est la suivante :
- tous les capteurs d’un réseau doivent être actifs (AND)
- si au moins un réseau est actif (OR)
→ le joueur saute.
Chaque capteur possède :
- une position relative autour du joueur
- un type de détection
Les capteurs peuvent détecter :
- la présence d’air
- la présence d’un bloc
- la présence d’un pic
Et aussi l’absence de ces éléments.
Au total, cela donne 6 types de capteurs différents.
Lorsqu’un objet entre dans la zone du capteur, celui-ci s’active.
Si toutes les conditions d’un réseau sont validées, l’IA déclenche un saut.
L’algorithme génétique
Pour l’évolution, j’ai utilisé un algorithme génétique assez simple.
Chaque génération commence avec 1000 joueurs.
Chaque joueur possède un génome généré aléatoirement.
Une simulation est ensuite exécutée pour chaque joueur afin d’obtenir son fitness, qui correspond simplement à la distance parcourue dans le niveau.
Une fois tous les joueurs évalués :
- la population est triée par score
- les 50 meilleurs joueurs sont conservés (les élites)
- chaque élite produit plusieurs enfants mutants
Le calcul est simple :
(1000 - 50) / 50 = 19Chaque élite produit donc 19 enfants mutants afin de recréer une population totale de 1000 joueurs.
Les mutations
Chaque enfant reçoit une seule mutation.
Les probabilités sont les suivantes :
- 50 % → modifier un capteur existant
- 25 % → ajouter un capteur
- 25 % → supprimer un capteur
Lorsqu’un capteur est modifié, plusieurs mutations sont possibles :
- changer son type (air, bloc, pic…)
- modifier sa position
- le déplacer vers un autre réseau
Ces petites modifications permettent d’explorer progressivement l’espace des solutions.
Quand la simplicité gagne
Au départ, j’ai essayé de faire quelque chose de plus sophistiqué.
J’ai testé plusieurs techniques classiques utilisées dans les algorithmes génétiques :
- croisement de génomes
- pénalité pour les comportements inutiles
- pénalité pour les génomes trop complexes
- pénalité pour les élites trop similaires
- taux de mutation variable
Par exemple, j’avais essayé de pénaliser les joueurs qui sautaient trop souvent afin d’éviter les comportements absurdes.
Le problème, c’est que ces modifications rendaient la fonction de fitness beaucoup trop complexe.
Résultat : l’IA ne terminait jamais le niveau.
Après plusieurs essais infructueux, j’ai pris une décision simple : tout supprimer et revenir à l’approche la plus basique possible.
Et c’est là que ça a marché.
Dès la première tentative avec une fitness simplifiée, l’IA a réussi à terminer le niveau.
la solution la plus simple était la meilleure.
Le comportement de l’entraînement
L’évolution de la fitness suit une courbe assez classique.
Au début, les progrès sont rapides.
Ensuite, l’algorithme atteint parfois des plateaux.
Cela arrive souvent lorsqu’un nouveau type d’obstacle apparaît dans le niveau. L’algorithme doit alors découvrir une nouvelle combinaison de capteurs permettant de passer cet obstacle.
Après quelques générations supplémentaires, une nouvelle solution apparaît et la progression reprend.
Limites de l’approche
Même si l’expérience fonctionne bien, cette approche a plusieurs limites.
Contrairement à un réseau de neurones entraîné par rétro-propagation, un algorithme génétique n’apprend pas directement de ses erreurs.
Chaque génération repose en grande partie sur le hasard des mutations.
Dans ce projet, cela fonctionne plutôt bien car :
- le nombre d’actions possibles est très limité (sauter ou attendre)
- l’espace des solutions est relativement restreint
- les capteurs sont confinés dans une zone autour du joueur
Mais sur des problèmes plus complexes, cette approche peut devenir inefficace.
Il existe d’ailleurs de nombreuses techniques avancées pour améliorer les algorithmes génétiques :
- NEAT
- speciation
- mutation adaptative
- co-évolution
Mais leur implémentation devient rapidement beaucoup plus complexe.
Axes d’amélioration
Même si l’IA arrive à terminer le niveau, plusieurs points pourraient être améliorés.
Par exemple, les génomes que j’obtiens sont souvent assez complexes avec de nombreux capteurs, alors que dans la vidéo originale ils semblent beaucoup plus propres.
Plusieurs pistes pourraient être explorées :
- introduire une pénalité douce pour les génomes trop complexes
- permettre à un capteur d’appartenir à plusieurs réseaux
- améliorer la sélection des élites
- ajouter un mécanisme de speciation pour préserver la diversité
- limiter la zone de recherche des capteurs
Il serait aussi intéressant d’expérimenter avec d’autres types de capteurs ou d’actions.
Conclusion
Ce projet était avant tout une expérience technique.
Au final, j’ai réussi à :
- recréer un clone jouable de Geometry Dash
- construire un éditeur de niveaux
- implémenter un algorithme génétique complet
- entraîner une IA capable de terminer un niveau
Et surtout, j’ai appris une chose importante :
dans ce type de système, la simplicité est souvent la clé.
Si le projet vous intéresse, voici le code GitHub afin que vous puissiez tester ou expérimenter vous-même :
https://github.com/alexisgrau/gd-genetic-algo