mrly.net

Skins

Representation leaves game logic. An app's board is a grid of role integers (0 = void); a skin is the one dictionary that says what each role wears; faces just apply it.

The law

`mrlyapps` or in web views - ever.

`mrlyui::skin::<app>` presets, keyed by the app's `skin` setting.

grid, the canvas, the CLI, and PNG all read the same one.

The shape

`mrlyui::skin` defines the closed enum:

pattern (bg doubles as its color), an optional face on top.

atlas exists (`FACE.md`), so canvas surfaces skip it.

On the wire, state gains two keys: `ids` (rows of role integers) and `skin` (an array indexed by role; each entry `{ bg?, motif?, face?: { as, value | rows } }`).

`Skin::tileset(k, ink)` builds the canvas `TileSet` from the same dictionary: solid or motif base, glyph faces baked in ink, emoji faces skipped. Web views use `src/skin.tsx` (`visual()`, `face()`).

Why

Same seed, same verbs, N skins: identical id grids, N renders. One replayed episode times N skins is N perfectly labeled samples with identical semantics - the gym's augmentation story lives in this layer.