Level builder
Players can build their own levels and share them as links. There is no server: the whole level travels inside the URL. This page covers the routes, the drawing board, the validation, and the link format.
Routes
| Route | What it is |
|---|---|
/build | A new level. The draft is restored from this browser when one is saved. |
/build/<payload> | Editing a shared level; the payload is decoded into the draft. |
/shared/<payload> | Playing a shared level, through the regular level screen. |
Entry points: the paintbrush button in the app header (always there), and "Build a level" in the win modal once all 20 built-in levels are completed.
The drawing board
- Layout, like playing a level: the level settings (size, theme, start direction) are one inline row directly above the drawing tools, which sit above the playground; the block palette sits below the playground exactly like a level's palette, and the solution editor takes the whole right column with the name and creator fields above it and the action buttons below it. Every field keeps its label on top and its normal size; fields never squeeze or clip. When the row is too narrow for all three, whole fields wrap to the next line, still centered, and the row below is pushed down rather than overlapped.
- Reset clears the whole builder back to the default starting level (a 5 by 5 ground board with the mouse and the cheese placed), after a confirmation; there is only one title for the page, "Level builder", whether you arrived with a fresh draft or from a shared level's Edit.
- Tools: ground, wall, ice, lava, hole, the mouse and the cheese, each a small translated chip in its own colour family. The pen cursor follows the pointer (fine pointers only): a nib whose point is exactly on the pointer, with the block being painted beside it, no chip around either.
- Painting: press and drag. Every cell reports its coordinate, so a fast drag paints everything the pointer crosses.
- Player and target are unique: painting one onto the other swaps them, and the cell under either is ground when the level is saved, so the mouse and the cheese stand on the floor. Ice is the exception: ice may stay under either, because a level can start or finish on a slide.
- Resize and theme can change at any time. Cells live in a sparse map: shrinking clips the view, and the cut cells wait in the buffer, so growing back restores them.
- Work survives a reload. The work in progress is saved, encoded, under
algorithy:builder-draft, and decoded when the builder opens. - Save commits one level. Pressing Save stores the level under
algorithy:builder-level(always one level, overwritten, no gallery), even when it is empty or unfinished, and the button then reads Share until the next edit.
The solution, trying and validation
The creator writes the level's ideal program with the regular block palette and drop zones; that program is what gets stored and shown to players.
Try runs the program right on the builder page, through the same
simulator as the game, so the creator sees the mouse walk, slide, win or die
without leaving the page. Outcomes arrive as toasts, exactly like on a level.
Open level page plays the finished level on its real page instead.
Validation and the draft save run 300ms after the last change, so continuous
painting is never interrupted while slower edits are checked almost
immediately. Save does not validate: any state can be the saved builder level.
Nothing is reported inline: pressing Try, or Share when the level cannot be
published, explains itself through a toast. A level is shareable when:
- it has a name,
- the program has at least one block and at most 20,
- the program is representable in the authored solution format (no empty
fororwhilebodies), - the program actually wins on the draft board,
- and the encoded link fits the budget.
Share copies the link; the link itself is never shown. The publish button
reads Save while the work differs from the saved builder level, and Share
once the two match. Share stays disabled until the level is shareable, with
the missing piece as its tooltip, so an unplayable level can never be
published.
The link format
The link is base64url over a compact byte payload, never JSON:
version | random salt (4 bytes) | width | height | terrain | player | target
| cells (3 bits each) | title | author | solution | checksum (2 bytes)
- The salt is fresh for every draft, so saving the same level twice produces different links, and an edited copy (even just the author) can never be handed back as the original link.
- The checksum rejects any tampered or truncated payload; decoding never throws, it returns null and the page shows a friendly error.
- The identity of a shared level is a deterministic hash of its board (map, player position and facing, target, theme, action palette), never of the link. The salt changes on every save while the board does not, so the same board keeps the same stored player program and completion; editing only the title keeps it too, while changing the board gets a fresh identity.
Limits, chosen so an over-long link is practically impossible:
| Thing | Limit |
|---|---|
| Board | 16 by 16 |
| Solution | 20 blocks |
| Level name | 60 characters |
| Creator name | 40 characters (empty means anonymous) |
| Link | 2000 characters (a worst case is under 400) |
Playing a shared level
A shared level is adapted into the same LevelData a built-in level uses:
the Playground, the simulator, the export and the outcome modal work
unchanged. The differences:
- the level picker is replaced by a header with the creator's title, the author below it, and an Edit button that opens the level in the builder as a new draft;
- every level offers all actions (the creator does not curate a palette);
- the block and cycle counts come from the creator's solution, so the outcome modal shows how the player compares to it.
Not in this version
There is no automatic path search: the creator authors the solution, and that is the validation. There is also no DOM test harness, so the builder's UI itself is covered by hand, not by tests; the codec, the schema, the adapter and the validation all have direct tests (see Testing).
Next: Algorithy Documentation.