Skip to main content

Moonlight Drift — terminal version

The third version of the game, alongside web/ (browser) and wii/. Runs on the magmacrunch.engine engine's terminal backend.

pipx install magmacrunch-moonlight-drift
moonlight-drift

pipx rather than pip because it puts the command on your PATH in its own virtualenv; plain pip install only reaches your PATH inside an activated venv. It is also a cabinet in the magmacrunch arcade — pipx install magmacrunch gets this and the other two — and plays identically either way.

For working on it:

pip install -e ".[dev]"
python -m drift

Tap SPACE or ↑ to climb, hold to keep climbing, release to fall. P pauses; the clock stops and a flap taken while it is stopped does nothing, so pausing suspends a run rather than changing it.

Thrust is a press, not a held key — a terminal cannot report that a key is still down, only that it was pressed, and a keyboard goes silent for about half a second before it starts repeating. Reading held state through that silence gives a boost that cuts out exactly when you press it. So each press is a hop whose arc outlasts the repeat delay: holding reads as a continuous climb, and a single tap is a crisp one. See drift/config.py. It is also a cabinet in the magmacrunch arcade, and plays identically either way.

On the title screen

Choose a pilot opens the roster: all 24, with the highlighted one's thrust, gravity and top speed underneath. They fly differently enough to notice in the first few seconds, and who you are flying is shown on the title screen so a choice that went nowhere is obvious.

How to play is the browser build's instructions plus the one thing a terminal has to explain that a browser does not — a keyboard reports presses and never releases, so thrust is inferred from auto-repeat and lets go about a tenth of a second after you do.

Esc here goes back to the arcade when the arcade started this, and the screen says so — but only then. Launched as its own command there is nothing behind it and the same key ends the session, which Q already covers, so the line would be a lie half the time if it were printed unconditionally.

Both scroll: 24 pilots and the rules are each longer than a standard terminal is tall.

moonlight-drift --list-characters       the roster, without opening the game
moonlight-drift --character fire-toad   fly as someone in particular
moonlight-drift --ascii                 no block or arrow glyphs

In a run

key
SPACE / ↑ / W thrust
P pause
R restart
Esc back to the title
Q quit

Pause stops the simulation and nothing else: the screen keeps redrawing, so a window resized while paused comes back laid out for the size it is now. It is refused once you have crashed, there being no clock left to stop, and R clears it along with everything else.

When the terminal cannot draw the glyphs

Not every console can encode what these games draw. Windows' two common codepages are the ones that bite: cp1252 has none of the block elements, arrows or suits, and cp437 has the blocks and none of the arrows, stars or suits. The engine asks the terminal what it can encode and substitutes what it cannot, one group at a time -- so a set of related glyphs never comes back half translated, and a terminal that can draw the blocks keeps them even though it has lost the arrows.

Detection is automatic. Two ways to override it, for the case no probe can see -- an encoding that accepts the character in a font that has no picture for it:

moonlight-drift --ascii              this game, this run
MAGMACRUNCH_ASCII=1      every cabinet, always

Every substitute is exactly one cell wide, so a plain screen has the same layout as a fancy one rather than a reflowed approximation of it.

Seeing your ship

The sky used to be louder than the ship. A white star sat at 18.3:1 contrast against the playfield and the dimmest pilot at 2.8:1 — sixty bright marks and one dim one, the dim one being the thing you steer.

Stars are dimmed on the way to the screen (the palette itself is the browser's SNES_STAR_COLORS and is left alone), the ship is drawn as a filled block with its glyph punched out of it, and each pilot's flight colour is lifted until it clears a contrast floor. Ships now sit at 7:1 or better against a brightest star of 3.8:1, and a test asserts the hierarchy rather than any particular pair of colours.

Lifting is measured, not guessed at lightness: blue contributes 0.0722 to luminance where green gives 0.7152, so Roderick Tron's perfectly respectable "light" blue was still dark. Only flight is lifted — the roster keeps each sprite's true colour beside its portrait.

Portraits

The roster shows each pilot's actual sprite, generated from the 48 PNGs the Wii port exported from the browser's canvas draw() calls. A cell is two stacked pixels — with its own foreground and background — which makes the pixel grid square in a terminal whose cells are twice as tall as they are wide, so a 14-cell portrait is a real 14×16 image rather than a pictogram.

pip install -e ".[tools]"
python tools/make_portraits.py     # rewrites drift/portraits.py
python tools/show_portraits.py     # look at them, in truecolor

drift/portraits.py is generated and committed, so playing needs no image library and decodes nothing at runtime — Pillow is a tool here, not a dependency. Regenerate after a character is redrawn and re-exported.

The accent colours come from the same place, saturation-weighted. Picked by eye first, several were simply wrong: Fire Toad had been coloured from its flames rather than the toad, and the SVFP Van was cyan when the van is magenta.

Portraits want a truecolor terminal; on 256 colours they approximate. The in-play glyph stays hand-authored — at two cells wide there is nothing to downsample a sprite to.

High scores

Kept on disk, so a record outlives the session. Filed under the same key the browser build posts to (moonlight-drift), which is what would make a shared board later a shared board rather than two boards with the same name.

A run that makes the table is asked for three initials, over the wreck rather than instead of it. One that does not is recorded anyway — it just has not earned being asked about. Each entry remembers the pilot who flew it.

MAGMACRUNCH_DATA_DIR=/somewhere/else moonlight-drift

The board lives under your platform's user data directory unless that says otherwise. Nothing is sent anywhere: see magmacrunch.engine.scores.

What is here

drift/config.py the browser's 1280×720 world, and every measurement in it
drift/projection.py that world onto character cells
drift/player.py gravity, thrust, and the two lethal edges
drift/obstacles.py the columns, their tapered silhouettes and collision
drift/characters.py 24 pilots — physics, hitboxes, a glyph and a colour
drift/stars.py the starfield, twinkling on a four-phase cycle
drift/scenes.py the title screen and the run

None of it imports the engine. tests/test_physics.py runs with nothing but pytest on the machine, which is what proves that claim.

Drawing a column

The columns taper and wander sideways on a sine. In the browser that wander is a fine ripple down a 300-pixel column — surface texture. A terminal row covers about 33 world units, and the sine completes two and a half turns inside one of them, so sampling once per row lands on an arbitrary phase and draws noise: the edge jitters a cell either way with no relation to the shape.

So a cell asks what the column does across the whole span it covers and takes the outermost answer. The wobble collapses into a stable envelope and the taper survives, because the taper varies over the column's length rather than within a cell. The wobble is not lost to a simplification — it was never resolvable at this size, and what was being drawn instead was aliasing.

The two things a terminal changes

A character cell is about twice as tall as it is wide. The Wii port has the same problem from the other direction — non-square framebuffer pixels — and solves it in wii/source/projection.c by deriving a pixel-aspect ratio and fitting the world's height to the safe area. Here that ratio is a constant 0.5, and the same fit applies: the gap and the player keep exactly the share of the screen they have in the browser, and the width is cropped. A short wide terminal sees less of the 1280 than a tall one, exactly as a 4:3 television sees less than a 16:9 one.

The player is about two cells wide and one tall. So a character cannot carry its art — on the web each is canvas primitives with hue cycling, on the Wii a baked PNG, and draw_image is a no-op in the terminal backend. What survives is a glyph, an accent colour from its web palette, and its physics — which is the part you actually feel.

Running the tests

pip install -e ".[dev]"
python -m pytest -q

The simulation suite needs no engine at all:

pip install pytest
python -m pytest tests/test_physics.py -q

Licence

PolyForm Noncommercial 1.0.0 — see LICENSE and NOTICE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

magmacrunch_moonlight_drift-0.5.0.tar.gz (67.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

magmacrunch_moonlight_drift-0.5.0-py3-none-any.whl (53.6 kB view details)

Uploaded Python 3

File details

Details for the file magmacrunch_moonlight_drift-0.5.0.tar.gz.

File metadata

File hashes

Hashes for magmacrunch_moonlight_drift-0.5.0.tar.gz
Algorithm Hash digest
SHA256 e1d4e0d7f46e064180968109903b8cc89949171a0a4d50750caac69283931cdf
MD5 920cb8708dfcca46d550185051421017
BLAKE2b-256 a059ec5e3e2a9bd5367d82bf47ec22075758273f723f8f84b7fb213e55537b6c

See more details on using hashes here.

Provenance

The following attestation bundles were made for magmacrunch_moonlight_drift-0.5.0.tar.gz:

Publisher: release.yml on magmacrunch-media/moonlight-drift

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file magmacrunch_moonlight_drift-0.5.0-py3-none-any.whl.

File metadata

File hashes

Hashes for magmacrunch_moonlight_drift-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 72b913cb97690b03f5353e745583d67a30e38530dcc2c1f4816532047f033058
MD5 bb2ae7981e59cfece3d533e467a428f5
BLAKE2b-256 275d117c1633c38e23213d52fcd26981457306fe534b8b15fc3bbf1746c72a89

See more details on using hashes here.

Provenance

The following attestation bundles were made for magmacrunch_moonlight_drift-0.5.0-py3-none-any.whl:

Publisher: release.yml on magmacrunch-media/moonlight-drift

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page