Skip to content

Add the five LD60 Circular Arcade games as VS2 games - #179

Merged
alecu merged 14 commits into
mainfrom
ld60-circular-arcade
Oct 30, 2026
Merged

alecu merged 14 commits into
mainfrom
ld60-circular-arcade

Conversation

@alecu

@alecu alecu commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Ports the five concept games from the Ludum Dare 60 "Circular Arcade" prototype (https://tmtg.nl/ludumdare/ld60/) to the VS2 API, under games/ld60/:

Game Port of
Spiral Tunnel dive down a tunnel that winds round the disc, bombing UFOs
Catch the Robots catch 20 wall-hugging robots in a random maze that wraps round the disc
Rim Runner run and jump round the inside of the rim, collecting eggs and landing on birds
Dodge 'Em five-lane circular track, eat the pills while red cars come the other way
Defender fly over a planet in the middle, shooting the aliens

Each game has a title, a result screen with the time, a saved best time (vs2.saves), sounds and a menu icon of its own. They show up in a new "ld60" menu group; run one with ./vs-emu.sh --game ld60.<name>.

How they work

  • The world is 32 tiles of 8 px on a TUNNEL layer, exactly one trip round the disc, and the camera turns with the player (the tilemap's x).
  • Everything is created in build() and moved afterwards: tile cells are one shared bytearray, sprites are fixed lists, no allocation in update().
  • Things near the middle of the disc are hard to read, so the games with a deep level scroll inward with the player and keep them toward the rim (Rim Runner about 8 LEDs in, Spiral Tunnel a quarter of the way in, Catch the Robots no deeper than halfway), following to the end of the level.
  • Spiral Tunnel uses slanted edge tiles like the prototype, with collision along the same lines. The other walls are solid with a bright edge; Dodge 'Em's outer wall is a single LED on the rim.
  • Art, menu icons and sound effects are generated by tools/ld60_assets/make_art.py and make_sounds.py.

Differences from the prototype

  • Controls are the controller's, not WASD+L.
  • Rim Runner's platforms are four tiles apart instead of five: a jump rises about 4.6 tiles, so the original's could not be reached from below.
  • The tunnel and maze are shorter, and speeds and gravity are scaled from the prototype's 60 fps to the console's 33 ticks per second.
  • Each game ends with its time and restarts on A.

Test plan

  • tests/test_ld60_games.py (73 tests, headless director with a virtual clock): title and result flow, best-time saving, 1500-tick random-input run per game, and each game's rules; also registered in tests/run_tests.py.
  • Full tests/run_tests.py passed (before the last few tuning commits; the LD60 tests and the mpy-cross check were re-run after them).
  • Rendered frames of every game through the emulator's renderer; a lookahead bot flew Spiral Tunnel to the end (only UFOs killed it).
  • Play each game in the desktop emulator and, ideally, on the disc: feel and difficulty are untested by a human, and so are the colours on the real LEDs.

🤖 Generated with Claude Code

alecu and others added 11 commits October 23, 2026 12:40
Spiral Tunnel, Catch the Robots, Rim Runner, Dodge 'Em and Defender, ported
from the prototypes at tmtg.nl/ludumdare/ld60 into games/ld60. The prototypes
bend a flat level into a circle with a shader; here the world is 32 tiles of
8 px on a TUNNEL layer, so it is exactly one trip round the disc and the rim
rows are big, as in the original.

The camera turns with the player (the tilemap's x is the camera), so each
game keeps its player at one end of the display: the top for four of them, the
bottom for Rim Runner, whose ground is the rim and whose "up" is the centre.
Art for the top games is stored turned through 180 degrees, so it needs no
flips. Walls are outlined by an autotile pass at build time, and every game is
built once and then moved, with no allocation in update().

The art, menu icons and sound effects are generated by tools/ld60_assets so
they can be tweaked in one place. Rim Runner's platforms are four tiles apart
rather than five, since a jump rises 4.6 tiles and the original's could not be
reached from below.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Covers the title and result flow, best-time saving, a long random-input run per
game, and each game's own rules: tunnel connectivity, lane gaps, maze
connectivity, jump height and egg collection, alien aiming. Also checks that
the games are listed in their own menu group and have the sounds they play.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The speckled dark walls with only an edge line were hard to tell from the
black passages. Fill them with a flat mid green and keep a light edge line.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Like the prototype, the view follows the player vertically as well as round the
disc. The runner's feet are kept at depth 5, about four LEDs from the rim, where
things are big and readable, instead of letting jumps carry it toward the middle
of the disc. The level is now 40 rows tall so the view keeps following all the
way up the highest platform; it stops only if the level runs out above the
runner. Birds now stay in rows 5 to 16, within reach of a jump.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The speckled dark walls were hard to tell from the track. Fill them with a flat
purple and keep a bright edge line. The track moves one row outward, with the
outer wall as row 0 drawn from depth -7 so only its last line of pixels is on
the disc: a single LED right on the rim, instead of a wall eight rows thick that
pushed the first lane in from the edge.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The camera stopped when the map ran out, so near the bottom the player drifted
toward the middle of the disc, where things are too small to read. Let it go on
following, so the player is never deeper than five rows down the view (about
halfway to the centre) and the space past the maze is left empty.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Same fix as Catch the Robots: the camera stopped when the map ran out, which let
the ship drift toward the middle of the disc near the end of the tunnel. It now
keeps following, so the ship stays about halfway out (depth 40) all the way down.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The runner was kept five pixels from the rim, which left almost no view of what
is below it. Keep it 12 deep (about eight LEDs in) instead, but never scroll the
view outward past the lowest floor, where only four pixels of ground are shown,
so the outer LEDs are not spent on solid ground.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Halfway out, the walls and UFOs ahead were too small to steer by. Keep the ship
at depth 18 (LED 40, about a quarter of the radius from the outside) for the
whole tunnel, and keep following it to the end.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The autotiled walls made the tunnel's edge a staircase, which looked blocky. The
prototype uses solid orange with diagonal edge tiles, so do the same: each row
of the tunnel is a left edge, a width and how far each edge moves sideways by
the next row (2 to 5 tiles), and the tiles carry the straight line between the
rows. Collision follows the same lines instead of the tile grid, so what is
drawn as wall is wall. The ship now starts on the rim in the middle of the
first row, since a lower start could already be inside the slanting wall.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The note still said Rim Runner keeps the runner four LEDs from the rim, and did
not mention the other two games that scroll.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
alecu and others added 3 commits October 30, 2026 07:14
The five games port the Ludum Dare 60 entry "Circular Arcade" by
borisvanschooten. The README only linked the entry in passing and the
game files called them "concept games" without saying whose. Say
plainly, in the README and at the top of every game, whose ideas they
are and where they were first published.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The LD60 games landed in a group the launcher did not know, so they
showed up as a plain "Ld60" label at the very end of "Más
aplicaciones". Give the group a name, a place and its own marquee icon,
and put it first on the main menu, which now opens group tiles the way
"Más aplicaciones" does. Resuming into a group that sits on the main
menu goes back through its own tile, not through a "Más aplicaciones"
it was never opened from.

The marquee has ten frames: its bulbs alternate colours, and the last
frame loses the second A of ARCADE like a failing bulb. That wants a
slow loop, so animated icons now carry their own frame rate: the
marquee holds each frame 300 ms, pollitos and tincho keep the shared
rate. The launcher test fixture now registers extra strips through its
fake ROM loader; a strip added by hand before the ROM loaded had its
metadata replaced by menu.png's, which the pollitos test could not
notice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alecu
alecu merged commit 3389a5f into main Oct 30, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant