
Un Snake… en RPN ?
MOGWAI est un moteur de scripting RPN (Reverse Polish Notation) pour .NET, inspiré du HP RPL des calculatrices HP 28S et HP 48. La notation postfixée, le modèle de pile, les primitives de bas niveau… rien qui ressemble a priori à l’environnement naturel d’un jeu vidéo.
Et pourtant, voilà MOGWAI Snake : un Snake jouable, complet, avec gestion du score, du high score, d’une barre de santé qui se vide en temps réel, des pièges positionnés aléatoirement sur le terrain, et un système de redémarrage propre — le tout dans un terminal TUI, en pur script MOGWAI.
Ce projet n’avait pas pour ambition de devenir un jeu à part entière. C’est avant tout un banc d’essai : est-ce que MOGWAI permet d’écrire quelque chose d’aussi dynamique et événementiel qu’un jeu en temps réel ? La réponse est oui, et voici comment.
Ce que vous voyez à l’écran
La status bar affiche en permanence :
- MOGWAI SNAKE en cyan
- SCORE et HIGH SCORE en bleu et magenta
- HEALTH : une barre visuelle
▓▓▓▓▓▓░░░░qui se vide progressivement et se recharge quand on ramasse un prix
Le terrain est délimité par un cadre Unicode (┌─┐│└┘). Les pièges (carrés verts ■) sont placés aléatoirement au démarrage, en dehors d’une zone de sécurité autour du centre. Le serpent est dessiné en cyan (█). Les prix à ramasser sont des chiffres en rouge (1 à 9) — leur valeur détermine à la fois les points gagnés et la croissance du serpent.
Architecture : 5 mécaniques clés
1. Le buffer $SCREEN — source de vérité unique
Le terrain de jeu est modélisé par un tableau de données linéarisé de taille WIDTH × HEIGHT. Chaque cellule encode son état :
0 → free
1 → snake body
2 → border (forbidden zone)
8 → trap
to 'setScreenValue' with [x: .number y: .number v: .number] do
{
v &$SCREEN y $WIDTH * x + set
}
to 'getScreenValue' with [x: .number y: .number] do
{
&$SCREEN y $WIDTH * x + get
}
La détection de collision est ainsi réduite à un seul test : si la valeur à la position de la tête est supérieure à 0, c’est perdu. Bordures, corps du serpent et pièges sont tous gérés par le même mécanisme.
if ($SX $SY getScreenValue 0 >) then { gameOver }
Notez l’utilisation du sigil & pour accéder au tableau par référence directe — ce qui évite une copie coûteuse à chaque accès.
2. $HEADING — la file de directions pour la queue
C’est la mécanique centrale du Snake : la queue ne suit pas la tête immédiatement, elle retrace son chemin avec un décalage. La solution ici est élégante : à chaque cycle, la direction courante est ajoutée en fin de liste $HEADING. Quand il est temps de déplacer la queue, on consomme la première entrée de la liste.
# Store the current direction
$HEADING $CURDIR + -> '$HEADING'
# ...later, for the tail:
$HEADING first -> 'heading'
$HEADING butfirst -> '$HEADING'
La liste $HEADING agit comme une file FIFO naturelle avec les primitives first et butfirst de MOGWAI. Plus le serpent est long, plus la liste est grande — et la queue suit exactement le chemin de la tête.
3. $WAITING_LOOP — la croissance du serpent
Quand le serpent mange un prix de valeur n, il doit grandir de n × 2 cellules. La solution adoptée est minimaliste : un compteur $WAITING_LOOP est mis à n × 2. Tant qu’il est supérieur à 0, la queue ne bouge pas — le serpent grandit donc naturellement, sans aucune structure supplémentaire.
# When a prize of value $PRIZE_VALUE is caught:
$PRIZE_VALUE 2 * -> '$WAITING_LOOP'
# At the end of each cycle:
'$WAITING_LOOP' --
if ($WAITING_LOOP 0 <=) then
{
1 -> '$WAITING_LOOP'
hideSnakeTail
# ... tail movement
}
4. Timer + flag — santé sans race condition
La barre de santé se vide toutes les 2 secondes via un timer MOGWAI. Mais le timer ne modifie pas directement $HEALTH_LEVEL : il se contente de lever un flag.
timer 'HEALTH_TIMER' every 2000 do
{
'HEALTH_CHANGE_FLAG' flag.set
}
C’est la boucle principale qui consomme ce flag à chaque cycle :
if ('HEALTH_CHANGE_FLAG' flag.isSet) then
{
'HEALTH_CHANGE_FLAG' flag.clear
'$HEALTH_LEVEL' --
if ($HEALTH_LEVEL 1 <) then { gameOver }
}
Ce découplage est une bonne pratique dans tout environnement avec des callbacks asynchrones : le timer signale, la boucle décide. On évite ainsi toute modification d’état en dehors du fil d’exécution principal.
5. post { startGame } — le redémarrage propre
Quand le joueur répond Y au prompt GAME OVER, le jeu redémarre. La tentation serait d’appeler startGame directement. Mais startGame contient la boucle principale (forever do), ce qui créerait une récursion indéfinie.
La solution : post, qui planifie l’exécution de startGame après le retour de l’appel courant. La pile d’appels reste propre, quelle que soit la durée de la session.
if (" NEW GAME (Y/N) ? " waitForYesOrNoChoice) then
{
post { startGame }
}
Vérifications au démarrage
Le script vérifie deux prérequis avant de lancer le jeu. D’abord, le host doit posséder le skill TERMINAL — une nouveauté de MOGWAI 8.11 qui permet à un hôte de déclarer ses capacités :
if ('TERMINAL' hasSkill not) then
{
"Unable to run the game." ?
"The host must have the 'TERMINAL' skill" ?
mogwai.exit
}
Ensuite, la version du runtime est vérifiée :
if (mogwai.info->version: "8.11" ver>= not) then
{
"Unable to run the game." ?
"The MOGWAI runtime must be at least version 8.11" ?
mogwai.exit
}
Ces deux gardes illustrent la philosophie de MOGWAI : un script doit pouvoir valider son environnement d’exécution et échouer proprement si les conditions ne sont pas réunies.
Ce que ce projet démontre
MOGWAI Snake n’est pas un jeu sérieux — c’est une démonstration. En un peu plus de 600 lignes de script RPN, on dispose de :
- Un rendu TUI dynamique avec positionnement précis au caractère près
- Une boucle de jeu temps réel avec gestion des entrées clavier non-bloquante
- Un système de timers asynchrones découplé de la logique principale
- Une détection de collision unifiée (corps, bordures, pièges) sur un seul buffer
- Un mécanisme de croissance du serpent sans structure de données dédiée
- Un système de redémarrage sans récursion
Le tout sans framework, sans bibliothèque de jeu, sans moteur graphique — juste le runtime MOGWAI et les primitives TUI de MOGWAI CLI.
Tester MOGWAI
MOGWAI est open source (Apache 2.0), disponible sur GitHub et NuGet.
- GitHub : github.com/Sydney680928/mogwai
- Site du projet : mogwai.eu.com
- NuGet :
dotnet add package MOGWAI
Le script snake.mog est inclus dans les exemples du dépôt. Il nécessite MOGWAI CLI v8.11 ou supérieur avec le skill TERMINAL activé.