DEV505

Tail Recursion — a snake game where you never steer

You never steer the snake. You program it.

There are no steering keys. The snake moves only when the program you built tells it to — and you build that program by dragging blocks onto a canvas and wiring them together.

Every block is a small piece of thought: Wall Ahead?, Repeat N Times, Turn Right. Wire them into a chain and you have made something that walks a corridor. Wire them into a cycle and you have made something that solves a maze it has never seen.

The twist is where the hardware lives. The snake's body is its computer. A Sensor Segment is what lets it see; a Processor Segment is what lets it think longer each tick. Every segment you add is a capability gained and a cell of manoeuvring room lost. That tension is the game.

What it is

A puzzle game about writing a thing that solves the puzzle

Load a level. Read the objective. Assemble the snake's logic, run it, watch the active block light up, find the tick where it went wrong, fix that block and run again — without ever reloading the level.

01

One tick, one action

A tick ends the moment the program reaches Move Forward, Turn, Wait or Stop. Turning and moving are separate actions, so a full reversal costs two ticks — and a tick that reaches no action at all just leaves the snake standing still. That is a normal outcome, not an error.

02

Execution is resumable

Your program is a thread with a program counter that survives between ticks, not a fresh traversal each frame. A chain of three actions takes three ticks and then loops. Ending a chain is "start again next tick" — which is why most graphs need no loop wiring at all.

03

Ten ops a tick

Thinking is rationed. Most blocks cost one op, scanning costs two, and constants cost nothing. Run out mid-tick and execution suspends exactly where it stood and resumes there — a starved program gets slower, never silent. Want more? Grow a Processor Segment. Now you are longer.

04

Blocks are Lua, all the way down

Every block in the game — including the ones in the tutorials — is a Lua table registered at load. Nothing about a block's meaning is hardcoded in the engine, so a mod is not a lesser citizen: it calls the same register_block the base game does.

05

The same seed, the same run

One seeded generator drives everything, including the resume point. A replay is identical tick for tick — proven byte-identical across MSVC on x64 and clang on arm64. Randomness that is not reproducible would make a puzzle unsolvable rather than interesting.

06

Stop means stop

Stopping a run restores the exact initial state — verified by hash, not by eye. You can iterate on a program forty times in a minute and the fortieth run starts from precisely the same board as the first.

6Tutorials
100Puzzles
37Built-in blocks
10Ops per tick
2Platforms

How it runs

Three blocks that walk any room

This is the whole solution to Perimeter, the fifth puzzle. Wall Ahead? yes to Turn Right, no to Move Forward. Watch which block lights up each tick — that highlight is exactly what the game draws while a program runs.

tick 0 ops 0/10 len 4 stands still

Blocks — the whole program

Why it loops forever

Nothing here wires back to the top. Both branches end at an action, the action ends the tick, and a chain that has run out of wire returns to Start on its own. Only the Stop block ever actually ends a program.

Why it costs two ops

Wall Ahead? is one op, the action it branches to is another. Two of ten spent, eight left over — which is the headroom the tutorials are balanced against. If the block library ever cannot solve a tutorial within the baseline budget, the library is wrong, not the budget.

The body is the computer

Length buys capability and costs room

Sensor Segment
Grants the environmental senses whole: look along a ray to range 3, smell the nearest item within radius 5, and touch all three cells around the head. Lose the segment and every sensing block stops answering on the very next tick.
Processor Segment
+5 ops per tick, on top of the baseline 10. The difference between a reflex and a decision.
Memory Segment
Two register slots each. Registers are shared by the whole graph and survive between ticks — unlike a Counter block, whose count belongs to that node alone.
Standard Segment
Length, and nothing else. Sometimes that is what you want and often it is not what you can afford.

A level decides which modules its snake carries. A block that needs hardware you do not have is not hidden from you — it shows what it needs. A locked block is a goal, not a surprise. Storage, Battery, Drill, Weapon and Signal segments are designed and not yet built.

Screens

The editor, the board and the inspector

Map above, block canvas below, and a live inspector down the right-hand side showing tick, ops spent, body modules and every register while the program runs.

Block library

Thirty-seven blocks, and none of them are magic

Actions end a tick. Sensing blocks turn perception into a branch. Flow blocks count. Source blocks have no exec ports at all and run only when a data wire pulls on them — which is why a sensor sitting unread on the canvas is free.

Global standings

Top players

Leaderboard
Coming soon_ Loading standings…

Where these numbers come from

The game keeps a per-level score, a best tick count and a fewest-blocks record for every puzzle you solve, plus a free-play best. Once Tail Recursion has a Steam app id, those records go up to a Steam leaderboard and land here.

A scheduled job pulls the leaderboard through Steam's Web API and commits the result as a plain JSON file next to this page. The API key stays in the build; the browser only ever reads the snapshot.

Mods

One file, same sandbox, hot reloaded

A mod is a folder with an init.lua in it. It loads after the built-ins, registers blocks and items with the same calls the base game uses, and runs in the same sandbox under the same budget. A broken mod is disabled and named while everything else keeps running.

Both the block files and the active program hot-reload while the game is running, so editing a block or rewiring a graph takes effect without a restart.

mods/example_fear/init.lua
register_block {
  id = "fear_tail",
  name = "Tail Beside Me?",
  category = "sensing",
  cost = 2,
  module = "sensor",
  exec = { "afraid", "calm" },
  execute = function(ctx) … end,
}

Community

Made by DEV505, on his own

Devlogs, build breakdowns and the occasional look at what broke this week go up on YouTube. The Discord and the Steam page are on their way.

Under the hood
Engine
C++ on SDL3's GPU API — Metal on macOS, Direct3D 12 on Windows, behind one renderer interface.
Scripting
Lua 5.4 in a sandbox treated as a security boundary, with an instruction watchdog so one bad block can never hang the app.
Assets
Almost none. The 5×7 font is code, the UI is quads, and every sound effect is synthesized at startup from square and triangle waves.
Testing
A headless self-test suite covering determinism, the action model, collisions, the sandbox, save and restore — run on both platforms on every push.