Add the galaga example: a C engine with akbasic as its enemy brain

A GALAGA-style fixed shooter whose engine is C on libakgl (null physics)
with the interpreter embedded as the scripting engine that owns every
enemy's behavior. One DEF-only script is called per enemy per frame
through a custom akgl_Actor update hook; SELF@, ACTOR@ and GAME@ are host
bindings, so the script reads and writes the engine's real memory -- the
boss even swaps its own damage sprite by raising an actor state bit from
BASIC. Bullets, collision, scoring and screens stay C.

Structure arguments were measured and rejected for the per-frame path:
each pointer parameter spends a value-pool slot the pool never reclaims,
1,015 calls to exhaustion against an unbounded rebind (issue #36).

Built when AKBASIC_WITH_AKGL=ON. Two CTest entries: a 600-frame headless
autoplay run under the dummy SDL drivers, and an interop round-trip test
that links the real script.c and galaga.bas and pins the four boundary
claims, 24,000 sustained calls among them. docs_galaga_figures
regenerates the two checked-in figures. Art is Kenney CC0, byte for
byte, with provenance.

Co-authored-by: andrew <andrew@aklabs.net>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XiGgpHuXUm2mR4Wzndw3dc
This commit is contained in:
2026-08-04 08:47:21 -04:00
parent 743e610f8f
commit 27837aeabc
36 changed files with 2532 additions and 0 deletions

View File

@@ -0,0 +1,39 @@
# Where this art came from
Every PNG in this directory is from **Kenney's Space Shooter (Remastered)**, released
into the public domain under
[Creative Commons Zero](http://creativecommons.org/publicdomain/zero/1.0/).
`License.txt` is the pack's own licence file, copied here unedited.
* Source: <https://kenney.nl/assets/space-shooter-remastered>
* Downloaded: 2026-08-04, `kenney_space-shooter-remastered.zip`
* Author: Kenney (<https://www.kenney.nl>)
* Licence: CC0 1.0. Crediting is not required; it is here because it should be.
The files are the pack's `PNG/` versions, byte for byte — nothing is resized,
recoloured or re-encoded, so the checksum of any of them still matches the
distributed archive. `playerShip1_blue.png` sits at the top of `PNG/`; the enemies
are from `PNG/Enemies/` and the lasers from `PNG/Lasers/`.
| File | Size | Used for |
|---|---|---|
| `playerShip1_blue.png` | 99x75 | the player's ship |
| `enemyBlue1.png` | 93x84 | the bee |
| `enemyRed2.png` | 104x84 | the butterfly |
| `enemyGreen3.png` | 103x84 | the boss, at full health |
| `enemyBlack3.png` | 103x84 | the boss at one hit point — same silhouette, drained colour |
| `laserBlue01.png` | 9x54 | the player's shot |
| `laserRed01.png` | 9x54 | an enemy's shot |
| `laserBlue08.png` | 48x46 | the explosion burst |
The sprites are used at their distributed size: libakgl draws a sprite at the
sprite's own dimensions (`akgl_Actor.scale` is overwritten every frame — libakgl
docs/12-actors.md), so there is no way to draw these smaller, and the game's
1280x960 view is sized to fit a ten-column formation of them instead. The boss's
damage state is the same shape in a different colour deliberately: the swap has to
read at a glance from the top of the screen.
Everything else on screen — the starfield and the HUD — is drawn by the program
with `akgl_draw_point()` and the UI layer. See `../../README.md` for the run
instructions and the two tutorial chapters (docs/20, docs/21) for why only the
things that move are artwork.