# Lumon — Macrodata Refine
A windowed recreation of the Lumon macrodata-refinement game: a vast field of
glowing, breathing numbers. Box them in with the mouse, merge regions that
touch, then **grab your selections and file them as refinement output**.
## Run
```sh
cargo run --release
```
Built with `macroquad` — a real window (1280×800, resizable, hi-dpi), so mouse
input is polled directly by the game every frame.
## Controls
| Input | Action |
|------------------------|-----------------------------------------------------|
| **Left-click drag** | Draw a rectangular box around characters |
| **Right-click drag** | Pan the view (free, not grid-snapped) |
| **Scroll wheel** | Zoom (anchored at the cursor) |
| **Space** | **Grab the selection -> refinement output** |
| **,** (comma) | Settings panel (font, glow, scanlines, sound) |
| **b** | Box-cursor mode (keyboard drawing fallback) |
| **Arrows / WASD** | Pan (`Shift` = faster); move cursor in box mode |
| **z** | Undo the last box |
| **c** | Clear every box |
| **Home** | Reset the view to the origin |
| **q** / `Esc` | Quit (Esc first exits box-cursor mode / settings) |
## Settings
Press **`,`** for the settings panel. Click a row or use arrows to change;
settings save automatically to `lumongame.cfg`.
- **FONT** — built-in pixel bitmap, or four embedded SIL-OFL monos:
JetBrains Mono, Space Mono, IBM Plex Mono, Fira Mono.
- **GLOW** — halo intensity behind glyphs: off / subtle / normal / bright.
- **SCANLINES** — CRT overlay on/off.
- **SOUND** — synthesized tones on/off (see below).
Text is rasterized at integer pixel sizes (with only a sub-pixel scale
residual) so it stays crisp at any font or zoom level.
## The refinement loop
1. **Draw a box** around numbers — left-drag from any corner to any other.
The release position always defines the far corner, so selection is exact.
2. **Boxes that touch or overlap merge** into one region. Two exceptions
carve instead: an **exact re-drag** of a box's rectangle toggles that box
off, and a selection lying **entirely inside** existing boxes removes
only those selected cells — the rest of the box stays, split into
separate boxes if the cut disconnects it.
3. The last-drawn box is the **selection**: it stays highlighted in white,
its border pulses between single and double lines, and the numbers inside
glow hardest and grow largest.
4. **Press Space to grab it.** The selection's data is extracted — digit
sum, digit/letter counts, the dominant character — and its **hidden
temper** is read from an invisible cluster map seeded into the sheet.
Enough tagged cells in the box and the output reports that temper
(WOE / DREAD / MALICE / FROCLIC); otherwise a fallback signature is
filed. Either way you get a calibration score and a cryptic line.
5. The result is filed in the **REFINEMENT OUTPUT** panel with a **sigil** —
an 8×8 silhouette of the exact region you refined. Newest on top, hue
accent from the source box. The box is stamped **R**, dims, and goes
quiet — refined data is spent data.
6. **The quota bar fills.** Filed mass feeds it; each shift's target grows
(400, 550, 700, …). Meet quota and the sheet **settles** — every number
freezes mid-breath, the screen dims, SHIFT COMPLETE — then a fresh sheet
loads with a new hidden temper map. The output log persists.
**The trail**: refine a box that hit a temper cluster and its hidden
siblings elsewhere on the sheet pulse faintly for a few seconds — a map to
follow, never a translation. Nothing in the game ever spells out what the
numbers mean; the meaning only ever emerges from what the output says about
what you boxed.
**Suspect numbers** (the flickering yellow digits) keep their yellow hue even
inside a box — boxing one never repaints it. They cluster toward temper
interiors, so the yellows are where the meaning lives.
Everything is deterministic: the same selection on the same sheet always
yields the same temper, score, sigil, and line.
## Feel
- Numbers **glow** (one tight halo layer under a bright core that pushes
toward white as luminance rises — adjustable in settings) and
**grow/shrink** on independent per-cell phases; boxed data pulses bigger
and brighter than the open field.
- Suspect digits flicker yellow like failing bulbs; the open field only
breathes subtly, so boxed regions pop.
- Committing a box triggers a quick bright grow-pop across the selection.
- Subtle CRT scanlines over a dark blue-charcoal background (toggleable).
## Sound
Tones, not music — every effect is synthesized in code at startup (no asset
files) and played through quad-snd's low-latency mixer:
| Event | Tone |
|--------------------------|---------------------------------------------------------|
| Box drawn | soft blip |
| Boxes merged | rising two-note chime |
| Carve / remove | descending tone / low pop |
| **Refine (Space)** | C-major arpeggio over a sustained fifth — the chime |
| Quota met | warm detuned C-major chord swell |
| New sheet | airy gliss with a sub thump |
| Temper trail started | high bell ping |
| Undo / clear / settings | descending blip / low thud / tiny tick |
If no audio device is available the game runs silently instead of crashing
(the vendored ALSA backend soft-fails; a warning prints to stderr).
## Implementation notes
- `macroquad` immediate-mode rendering at 60 fps; glyph metrics are cached
per character and font, and every visible cell is animated from a hash
phase and a sine — no per-cell state.
- Fonts are embedded with `include_bytes!` and loaded via
`load_ttf_font_from_bytes`, so the binary is self-contained.
- The hidden temper map is generated per sheet: 26 seeded clusters with
distance falloff, suspects biased toward cluster interiors. Mood
classification counts tags in the selection (≥4 tagged, ≥⅔ dominant);
anything else falls back to the hash.
- Quota mass = refined area; targets grow per shift; shift reloads derive
sheet seeds from the base seed (xorshift64\* — non-zero-state only, so
distinct seeds stay distinct).
- Box outlines are derived per cell from exposed edges, so any union of cells
(L-shapes, jags) renders as one correct shape, with a double-line echo
border on the selection.
- Merged regions are `HashSet<(row, col)>` cell sets; touching = 4-adjacency
or overlap; carves split remainders via 4-connected components.
- Input is polled every frame — no dependence on terminal mouse protocols.
The drag preview exists only while the left button is physically held, so
WM focus quirks can never leave a phantom selection on screen.
- Headless logic tests (`cargo test`) cover the temper map, carve/split,
reselect toggle, refinement classification, quota/shift cycle, sigils,
and the WAV synthesis.
- Audio: `macroquad`'s `audio` feature with **quad-snd 0.2.8 vendored** at
`third_party/quad-snd/` (MIT/Apache-2.0, (c) quad-snd contributors) and patched
so the ALSA backend returns silence instead of panicking when no device
exists. Sound effects are synthesized PCM16 WAVs (`src/sfx.rs`).
- Set `LUMON_DEBUG=1` to trace commits, carves, and refines to stderr.
`LUMON_SMOKE=1` runs a few seconds of frames then exits.