{"id":2662,"date":"2026-06-17T10:54:04","date_gmt":"2026-06-17T09:54:04","guid":{"rendered":"https:\/\/coding4phone.com\/?p=2662"},"modified":"2026-06-17T10:54:51","modified_gmt":"2026-06-17T09:54:51","slug":"mogwai-snake-a-complete-game-written-in-rpn-scripting-language","status":"publish","type":"post","link":"https:\/\/coding4phone.com\/?p=2662&lang=en","title":{"rendered":"MOGWAI Snake \u2014 A Complete Game Written in RPN Scripting Language"},"content":{"rendered":"<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"724\" data-attachment-id=\"2647\" data-permalink=\"https:\/\/coding4phone.com\/?attachment_id=2647#main\" data-orig-file=\"https:\/\/i0.wp.com\/coding4phone.com\/wp-content\/uploads\/2026\/06\/snake.gif?fit=800%2C724&amp;ssl=1\" data-orig-size=\"800,724\" data-comments-opened=\"1\" data-image-title=\"snake\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/coding4phone.com\/wp-content\/uploads\/2026\/06\/snake.gif?fit=800%2C724&amp;ssl=1\" src=\"https:\/\/i0.wp.com\/coding4phone.com\/wp-content\/uploads\/2026\/06\/snake.gif?resize=800%2C724&#038;ssl=1\" alt=\"Un \u00e9cran de jeu de serpent en noir avec des carr\u00e9s verts repr\u00e9sentant la nourriture, affichant le score et le meilleur score en haut.\" class=\"wp-image-2647\" style=\"width:820px;height:auto\"\/><figcaption class=\"wp-element-caption\"><em>MOGWAI Snake running inside MOGWAI CLI \u2014 live score, high score, health bar and traps.<\/em><\/figcaption><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">A Snake game\u2026 in RPN?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">MOGWAI is an RPN (Reverse Polish Notation) scripting engine for .NET, inspired by HP RPL from the HP 28S and HP 48 calculators. Postfix notation, a stack-based model, low-level primitives\u2026 none of that sounds like the natural environment for a video game.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And yet, here is MOGWAI Snake: a fully playable Snake game, complete with score tracking, high score, a real-time health bar that drains steadily, randomly placed traps, and a clean restart system \u2014 all inside a TUI terminal, written in pure MOGWAI script.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This project was never meant to become a serious game. It is first and foremost a stress test: can MOGWAI handle something as dynamic and event-driven as a real-time game? The answer is yes, and here is how.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What you see on screen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The status bar permanently displays:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>MOGWAI SNAKE<\/strong> in cyan<\/li>\n\n\n\n<li><strong>SCORE<\/strong> and <strong>HIGH SCORE<\/strong> in blue and magenta<\/li>\n\n\n\n<li><strong>HEALTH<\/strong>: a visual bar <code>\u2593\u2593\u2593\u2593\u2593\u2593\u2591\u2591\u2591\u2591<\/code> that drains over time and refills when a prize is collected<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The play field is framed with Unicode box-drawing characters (<code>\u250c\u2500\u2510\u2502\u2514\u2518<\/code>). <strong>Traps<\/strong> (green squares <code>\u25a0<\/code>) are randomly placed at startup, outside a safety zone around the center. The <strong>snake<\/strong> is drawn in cyan (<code>\u2588<\/code>). <strong>Prizes<\/strong> are red digits (<code>1<\/code> through <code>9<\/code>) \u2014 their value determines both the score gained and how much the snake grows.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Architecture: 5 key mechanics<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. The <code>$SCREEN<\/code> buffer \u2014 single source of truth<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The play field is modelled as a flat data array of size <code>WIDTH \u00d7 HEIGHT<\/code>. Each cell encodes its state:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">0  \u2192  free<br>1  \u2192  snake body<br>2  \u2192  border (forbidden zone)<br>8  \u2192  trap<\/pre>\n\n\n\n<pre class=\"wp-block-preformatted\">to 'setScreenValue' with [x: .number y: .number v: .number] do<br>{<br>    v &amp;$SCREEN y $WIDTH * x + set<br>}<br><br>to 'getScreenValue' with [x: .number y: .number] do<br>{<br>    &amp;$SCREEN y $WIDTH * x + get<br>}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Collision detection is reduced to a single test: if the value at the head&rsquo;s position is greater than <code>0<\/code>, game over. Borders, snake body and traps are all handled by the same mechanism.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">if ($SX $SY getScreenValue 0 >) then { gameOver }<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note the use of the <code>&amp;<\/code> sigil to access the array by direct reference \u2014 avoiding an expensive copy on every access.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. <code>$HEADING<\/code> \u2014 the direction queue for the tail<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">This is the central mechanic of any Snake game: the tail does not follow the head immediately, it retraces its path with a delay. The solution here is elegant: at each cycle, the current direction is <strong>appended to the list<\/strong> <code>$HEADING<\/code>. When it is time to move the tail, the <strong>first entry<\/strong> of the list is consumed.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># Store the current direction<br>$HEADING $CURDIR + -> '$HEADING'<br><br># ...later, for the tail:<br>$HEADING first -> 'heading'<br>$HEADING butfirst -> '$HEADING'<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>$HEADING<\/code> list acts as a natural FIFO queue using MOGWAI&rsquo;s <code>first<\/code> and <code>butfirst<\/code> primitives. The longer the snake, the longer the list \u2014 and the tail traces the exact path of the head.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3. <code>$WAITING_LOOP<\/code> \u2014 snake growth<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When the snake eats a prize of value <code>n<\/code>, it must grow by <code>n \u00d7 2<\/code> cells. The approach is minimalist: a counter <code>$WAITING_LOOP<\/code> is set to <code>n \u00d7 2<\/code>. As long as it remains above <code>0<\/code>, the tail <strong>does not move<\/strong> \u2014 the snake grows naturally, with no additional data structure required.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># When a prize of value $PRIZE_VALUE is caught:<br>$PRIZE_VALUE 2 * -> '$WAITING_LOOP'<br><br># At the end of each cycle:<br>'$WAITING_LOOP' --<br><br>if ($WAITING_LOOP 0 &lt;=) then<br>{<br>    1 -> '$WAITING_LOOP'<br>    hideSnakeTail<br>    # ... tail movement<br>}<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4. Timer + flag \u2014 health without race conditions<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The health bar drains every 2 seconds via a <a href=\"https:\/\/coding4phone.com\/?p=2060&amp;lang=en\" target=\"_blank\" rel=\"noreferrer noopener\">MOGWAI timer<\/a>. But the timer does not modify <code>$HEALTH_LEVEL<\/code> directly \u2014 it simply <strong>sets a flag<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">timer 'HEALTH_TIMER' every 2000 do<br>{<br>    'HEALTH_CHANGE_FLAG' flag.set<br>}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The main game loop consumes that flag at each cycle:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">if ('HEALTH_CHANGE_FLAG' flag.isSet) then<br>{<br>    'HEALTH_CHANGE_FLAG' flag.clear<br>    '$HEALTH_LEVEL' --<br><br>    if ($HEALTH_LEVEL 1 &lt;) then { gameOver }<br>}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This decoupling is good practice in any environment with asynchronous callbacks: the timer signals, the loop decides. State is never modified outside the main execution thread.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5. <code>post { startGame }<\/code> \u2014 clean restart<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When the player presses <code>Y<\/code> at the GAME OVER prompt, the game restarts. The temptation would be to call <code>startGame<\/code> directly. But <code>startGame<\/code> contains the main game loop (<code>forever do<\/code>), which would create unbounded recursion.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The solution is <code>post<\/code>, which schedules the execution of <code>startGame<\/code> <strong>after<\/strong> the current call returns. The call stack stays clean, regardless of how many games are played.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">if (\" NEW GAME (Y\/N) ? \" waitForYesOrNoChoice) then<br>{<br>    post { startGame }<br>}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Startup checks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The script verifies two prerequisites before launching the game. First, the host must have the <code>TERMINAL<\/code> skill \u2014 a new feature introduced in MOGWAI 8.11 that lets a host declare its capabilities:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">if ('TERMINAL' hasSkill not) then<br>{<br>    \"Unable to run the game.\" ?<br>    \"The host must have the 'TERMINAL' skill\" ?<br>    mogwai.exit<br>}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then, the runtime version is checked:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">if (mogwai.info->version: \"8.11\" ver>= not) then<br>{<br>    \"Unable to run the game.\" ?<br>    \"The MOGWAI runtime must be at least version 8.11\" ?<br>    mogwai.exit<br>}<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These two guards illustrate a core MOGWAI philosophy: a script should be able to validate its execution environment and fail cleanly if the required conditions are not met.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What this project demonstrates<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">MOGWAI Snake is not a serious game \u2014 it is a proof of concept. In just over 600 lines of RPN script, you get:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Dynamic TUI rendering with precise character-level positioning<\/li>\n\n\n\n<li>A real-time game loop with non-blocking keyboard input<\/li>\n\n\n\n<li>An asynchronous timer system decoupled from the main logic<\/li>\n\n\n\n<li>Unified collision detection (body, borders, traps) on a single buffer<\/li>\n\n\n\n<li>A snake growth mechanism with no dedicated data structure<\/li>\n\n\n\n<li>A recursion-free restart system<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">All of this with no framework, no game library, no graphics engine \u2014 just the MOGWAI runtime and the TUI primitives of MOGWAI CLI.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Try MOGWAI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">MOGWAI is open source (Apache 2.0), available on GitHub and NuGet.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GitHub: <a href=\"https:\/\/github.com\/Sydney680928\/mogwai\" target=\"_blank\" rel=\"noreferrer noopener\">github.com\/Sydney680928\/mogwai<\/a><\/li>\n\n\n\n<li>Project site: <a href=\"https:\/\/mogwai.eu.com\" target=\"_blank\" rel=\"noreferrer noopener\">mogwai.eu.com<\/a><\/li>\n\n\n\n<li>NuGet: <code>dotnet add package MOGWAI<\/code><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/github.com\/Sydney680928\/mogwai\/tree\/main\/examples\/Scripts\/games\/snake.mog\" target=\"_blank\" rel=\"noreferrer noopener\">The <code>snake.mog<\/code> script<\/a> is included in the repository examples. It requires MOGWAI CLI v8.11 or later with the <code>TERMINAL<\/code> skill enabled.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A Snake game\u2026 in RPN? MOGWAI is an RPN (Reverse Polish Notation) scripting engine for .NET, inspired by HP RPL from the HP 28S and HP 48 calculators. Postfix notation, a stack-based model, low-level primitives\u2026 none of that sounds like the natural environment for a video game. And yet, here is MOGWAI Snake: a fully &#8230; <a title=\"MOGWAI Snake \u2014 A Complete Game Written in RPN Scripting Language\" class=\"read-more\" href=\"https:\/\/coding4phone.com\/?p=2662&#038;lang=en\" aria-label=\"En savoir plus sur MOGWAI Snake \u2014 A Complete Game Written in RPN Scripting Language\">Lire la suite<\/a><\/p>\n","protected":false},"author":1,"featured_media":2657,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"D\u00e9couvrez MOGWAI Snake, un jeu dynamique cr\u00e9\u00e9 avec le moteur RPN MOGWAI, alliant convivialit\u00e9 et logique de programmation.","jetpack_seo_html_title":"MOGWAI Snake : Un jeu classique en script RPN dynamique","jetpack_seo_noindex":false,"jetpack_seo_schema_type":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_wpcom_ai_launchpad_first_post":false,"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"{title}\n\n{excerpt}\n\n{url}","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_wpas_customize_per_network":false,"jetpack_post_was_ever_published":false},"categories":[127],"tags":[],"class_list":["post-2662","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-mogwai-en"],"jetpack_publicize_connections":[],"jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pfoe4s-GW","jetpack-related-posts":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/coding4phone.com\/wp-content\/uploads\/2026\/06\/coding-4-phone-6a326a0fd4eff.png?fit=1024%2C1024&ssl=1","_links":{"self":[{"href":"https:\/\/coding4phone.com\/index.php?rest_route=\/wp\/v2\/posts\/2662","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coding4phone.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coding4phone.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coding4phone.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coding4phone.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2662"}],"version-history":[{"count":6,"href":"https:\/\/coding4phone.com\/index.php?rest_route=\/wp\/v2\/posts\/2662\/revisions"}],"predecessor-version":[{"id":2668,"href":"https:\/\/coding4phone.com\/index.php?rest_route=\/wp\/v2\/posts\/2662\/revisions\/2668"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/coding4phone.com\/index.php?rest_route=\/wp\/v2\/media\/2657"}],"wp:attachment":[{"href":"https:\/\/coding4phone.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2662"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coding4phone.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2662"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coding4phone.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2662"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}