IA
Illustrer la vitesse du développement logiciel moderne grâce à un algorithme génétique (et à quelques moutons)
L’IA évolue rapidement et le nombre impressionnant d’applications fait en sorte qu’il devient difficile pour une seule personne de tout suivre. C’est pourquoi, chez Osedea, nous misons sur une approche collective pour rester à l’avant-garde. Chaque semaine, un membre de notre équipe IA présente de façon informelle un projet professionnel ou personnel sur lequel il travaille, en plus de partager quelques nouvelles liées à l’IA.
Lors d’une de ces présentations, Jack Tacchi, développeur IA senior, a comparé deux implémentations d’une même idée : la première réalisée dans le cadre de son mémoire de baccalauréat en 2017, et la seconde, une expérimentation assistée par l’IA développée près de dix ans plus tard. La comparaison entre les deux montre de façon frappante à quel point le développement logiciel s’est accéléré. Une version plus courte de cette présentation a également été donnée à AI Tinkerers Montreal!
Mais d’abord, quelle était cette idée?
L’idée
L’objectif principal du projet était de créer un environnement 3D dynamique peuplé d’animats, des animaux virtuels simulés, ici des moutons, qui doivent apprendre à survivre de façon autonome.
Pour rester en vie, chaque mouton doit répondre à deux besoins physiologiques : la faim et la soif. Des sources de nourriture, comme des buissons et des fleurs, ainsi que de l’eau sont réparties sur un terrain généré de façon procédurale. Aucune règle prédéfinie n’indique aux moutons comment trouver de la nourriture ou de l’eau. Leur « cerveau », un réseau neuronal, transforme plutôt les données provenant de leurs capteurs visuels en mouvements. Au fil des générations, les moutons qui réussissent le mieux transmettent leurs gènes, ce qui mène progressivement à de meilleures stratégies de survie.
Voyons maintenant à quoi ressemble le monde dans lequel vivent nos moutons.
1. Terrain procédural
Différentes couches de bruit de Perlin ont été superposées avec des amplitudes variables afin de générer une carte de hauteur fluide et naturelle pour un maillage 3D.

La fonction de génération utilisait plusieurs paramètres faciles à modifier pour ajuster le terrain. Le maillage pouvait ensuite être coloré de façon procédurale en fonction de la hauteur de chacune de ses sections.

2. Eau dynamique
L’eau a été ajoutée à la carte selon une approche semblable, mais simplifiée. L’utilisation d’une fonction sinusoïdale plutôt que du bruit de Perlin permettait de créer des « vagues » plutôt qu’un maillage ressemblant à un terrain. La hauteur et la distance entre les vagues pouvaient également être contrôlées à l’aide de paramètres. Il était aussi possible d’animer l’eau en ajoutant une variable de temps :
y = wave_height * Math.sin(dist_between_waves * (x - speed * time))
3. Générateurs de nourriture
La nourriture apparaissait aléatoirement sur la carte. Les moutons pouvaient manger deux types d’aliments :
- Fleurs : réduisaient considérablement la faim, mais disparaissaient après quelques secondes.
- Buissons : réduisaient moins la faim, mais restaient disponibles plus longtemps.
Avec ce dernier élément en place, le monde est enfin prêt à accueillir nos moutons virtuels!

Les moutons et leurs sens
Pour que les moutons puissent prendre des décisions raisonnables, ils doivent être capables de percevoir leur environnement.
Leur vision a été simulée à l’aide du raycasting 3D. Chaque mouton projette un éventail de 31 rayons horizontaux selon 5 angles d’élévation verticaux, pour un total de 155 rayons.
Lorsqu’un rayon rencontre un objet, deux variables principales sont enregistrées :
- Distance : la distance entre le mouton et l’objet, normalisée entre 0 et 1.
- Type d’objet : représenté par un vecteur one-hot correspondant à l’une des catégories suivantes : terrain, eau, buisson, fleur, autre mouton ou rien.
En résumé, chaque mouton utilise 155 rayons pour percevoir ce qui l’entoure, notamment le type d’objet rencontré par chacun des rayons et sa distance.

Dans la vraie vie, les moutons possèdent un champ de vision impressionnant d’environ 320 degrés, ce qui leur permet de surveiller constamment un large périmètre pour repérer les prédateurs. Nous avons donné aux moutons d’Osedea le même champ de vision.
Voici ce que ça donne en action!

Les cubes représentent les objets que le mouton peut voir. Les lignes blanches correspondent aux rayons ayant atteint leur distance maximale sans rencontrer d’objet.
Des cerveaux, des cerveaux, des cerveaux!
Il faut maintenant transmettre l’information reçue par les « yeux » des moutons à leur « cerveau » : un réseau neuronal artificiel.
Comme leur nom l’indique, les réseaux neuronaux artificiels s’inspirent du fonctionnement du cerveau. Ils sont composés de différentes séries, ou « couches », de nœuds similaires aux neurones biologiques. Ces nœuds sont reliés entre eux par différents poids, un peu comme des synapses. Ces poids déterminent l’influence de chaque entrée sur un nœud. Plusieurs entrées ayant un poids élevé entraîneront généralement une activation plus forte.

La première couche du réseau neuronal des moutons est la couche d’entrée. Elle regroupe toutes les données provenant de leur vision :
155 rayons pour les distances + (155 rayons × 6 types d’objets) = 1 085 nœuds dans la couche d’entrée.
Ensuite, deux « couches cachées » réduisent le nombre total de nœuds à 64, puis à 32. La deuxième couche cachée reçoit également les niveaux de faim et de soif provenant de l’état interne du mouton. Des fonctions d’activation Leaky ReLU introduisent de la non-linéarité dans ces couches tout en permettant à de petites valeurs négatives de subsister. Le réseau neuronal issu de l’évolution génétique peut ainsi produire des comportements plus complexes.
Finalement, la couche de sortie génère deux valeurs : une pour contrôler la direction du mouton, à gauche ou à droite, et une autre pour contrôler sa vitesse, vers l’avant ou l’arrière. Ces valeurs passent ensuite par une fonction tanh afin de rester comprises entre -1 et 1.
Voici un diagramme de la structure complète du réseau neuronal.

Cette configuration nous permet aussi d’enregistrer sous forme de liste les poids des connexions du cerveau de chaque mouton. Ces valeurs sont tout ce dont nous avons besoin pour recréer un mouton en particulier. On peut les voir comme l’équivalent de l’ADN d’un véritable animal.
Dans la prochaine section, nous verrons comment utiliser ces poids pour faire évoluer des comportements mieux adaptés à notre monde virtuel.
Lumières, caméra, action
Nos moutons sont maintenant prêts à manger, boire et se déplacer dans leur petit univers. Malheureusement, puisque les poids de leurs connexions sont initialement attribués de façon aléatoire, leur comportement l’est lui aussi en grande partie.
Mais si nous définissons une mesure permettant d’évaluer la capacité de chaque mouton à survivre, nous pouvons classer les poids d’une génération et favoriser les meilleurs lors des générations suivantes. Cette mesure est souvent appelée « fonction objectif » ou simplement « fitness ».
En ajoutant occasionnellement des mutations aléatoires aux nouvelles générations, on peut s’attendre à ce que leurs capacités de survie s’améliorent graduellement. Le principe ressemble beaucoup à l’évolution dans la nature, mais dans une version beaucoup, beaucoup plus simple.
Quelques détails techniques : chaque génération comptait 120 moutons. La toute première génération recevait des poids aléatoires à l’aide de l’initialisation de Kaiming. Pour chacune des générations suivantes, les trois moutons les plus performants transmettaient automatiquement leurs poids à la prochaine génération, une fois sous forme de copie exacte et une autre fois avec des mutations. Les places restantes étaient attribuées en sélectionnant le meilleur individu parmi des groupes de trois choisis aléatoirement, une approche combinant un élitisme pseudo-déterministe et une sélection par tournoi.
Résultats
Après 200 générations de simulation, nous pouvions déjà observer des résultats intéressants.
Génération 0 : la durée de vie moyenne était d’environ 60 secondes, avec un maximum de 122 secondes. La majorité des décès étaient causés par la soif, soit 88 moutons sur 120.
Génération 200+ : la durée de vie moyenne avait grimpé à environ 140 secondes, avec des durées maximales dépassant 1 812 secondes, soit plus de 30 minutes. Le nombre de décès causés par la soif avait considérablement diminué, passant à 54 sur 120. La faim était devenue la principale cause de décès, probablement parce que l’emplacement des plantes était moins prévisible.
Comment survivre comme un mouton
Au fil de ces 200 générations, les moutons ont développé quelques comportements particulièrement intéressants et faciles à observer :
- La stratégie « rester près de l’eau » : puisque les sources d’eau apparaissaient toujours à certains endroits précis, dans les zones plus basses du terrain, les moutons ont appris à rester à proximité de l’eau et à ne s’en éloigner que pour aller chercher de la nourriture, qui pouvait apparaître n’importe où sur la carte.
- La stratégie du « requin », ou ne jamais arrêter de bouger : les moutons pouvaient avancer plus rapidement qu’ils ne pouvaient reculer. Comme il n’y avait aucune pénalité associée au fait de bouger constamment, ils ont fini par courir continuellement à vitesse maximale afin de couvrir le plus de terrain possible.
Comment le développement logiciel a-t-il changé en près de dix ans?
Comme mentionné au début de l’article, l’idée était de recréer un projet datant de près de dix ans afin d’obtenir un exemple concret de l’évolution de la programmation au cours de cette période.
J’ai créé la première version de ce simulateur d’évolution en 2017 dans le cadre de mon mémoire de baccalauréat. Il m’avait fallu environ six mois pour le compléter en travaillant à temps partiel.
Évidemment, la deuxième fois, je partais avec une bien meilleure compréhension des algorithmes évolutionnaires et de la programmation en général. Même en tenant compte de cette importante nuance, compléter la nouvelle version en seulement 11 heures offre un contraste frappant avec les six mois nécessaires pour réaliser l’originale.
De plus, la version de 2026 a été développée en 3D, avec l’aide d’un moteur de jeu, alors que celle de 2017 reposait sur une simple carte 2D. Le passage à la 3D a non seulement rendu la génération de la carte beaucoup plus complexe, mais a aussi eu un impact sur l’implémentation de la vision des moutons, qui devait désormais fonctionner dans un environnement tridimensionnel.
À titre de comparaison, voici une image tirée de mon projet de 2017. Je pense qu’on peut s’entendre pour dire que la version de 2026 est un peu plus agréable à regarder!

Ce qu’on retient
Les assistants de programmation basés sur les LLM ont clairement été très utiles. Ils ont toutefois rencontré certaines difficultés :
- J’ai remarqué que les assistants de programmation tentaient régulièrement d’utiliser des fonctions Unity désormais obsolètes. Des tests rigoureux demeuraient donc essentiels.
- Ils avaient aussi parfois de la difficulté à gérer la complexité du projet. Plus le nombre d’éléments interconnectés augmente, comme l’architecture du réseau neuronal, les entrées et sorties, la génération de la carte dans Unity ou encore la documentation Unity, plus le contexte dont le LLM a besoin devient important. Cela peut éventuellement réduire ses performances. Des modèles plus avancés pourraient atténuer ce problème à l’avenir, mais pour l’instant, la meilleure solution reste de bien gérer le contexte et l’information transmis au LLM.
- Parfois, ils créaient aussi des bogues franchement très simples. Au bout du compte, les LLM reposent sur la reconnaissance de motifs, ce qui signifie qu’ils peuvent passer à côté d’erreurs qui sont pourtant très évidentes pour un humain. C’est notamment ce qui s’est produit lors de la première tentative de l’algorithme évolutionnaire : le LLM effaçait les poids de tous les moutons avant de les copier vers la génération suivante, empêchant ainsi toute amélioration du comportement.
Ces observations montrent toute la valeur de combiner le développement assisté par l’IA avec l’expertise humaine. L’IA peut accélérer considérablement certaines parties du travail, mais le jugement humain demeure essentiel pour gérer le contexte, repérer les erreurs et prendre de bonnes décisions techniques.
C’est ainsi que nous abordons l’IA chez Osedea.
Nous suivons de près les avancées qui transforment notre industrie, mais c’est en les mettant en pratique que nous apprenons vraiment. Des expérimentations comme celle-ci nous permettent de comprendre non seulement ce que les nouveaux outils peuvent accélérer, mais aussi où l’expertise humaine, le contexte et le jugement continuent de faire toute la différence.
Pour nous, les progrès en IA ne se résument pas à des modèles plus rapides ou plus performants. Il s’agit surtout d’apprendre à transformer ces nouvelles capacités en systèmes utiles, responsables et capables de fonctionner dans le monde réel.
Parlez-nous de votre projet d’IA.



Cet article vous a donné des idées ? Nous serions ravis de travailler avec vous ! Contactez-nous et découvrons ce que nous pouvons faire ensemble.




.png)
