Skip to content

feat(#291): worlds and patterns carry boundaries and walls - #304

Merged
octalide merged 7 commits into
devfrom
feat/291
Oct 4, 2026
Merged

octalide merged 7 commits into
devfrom
feat/291

Conversation

@octalide

@octalide octalide commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

Closes #291

Plan

World files: cellar 7 (cellar.persist).

  • save_world always writes version 7. The header's new cells key says states or words, which the version digit said before (4, 5, 6).
  • The body may hold two record kinds beside the chunks:
    • bound <n> and n lines, the drawn boundary a band a line: y0 y1 x0 x1 ..., an unbounded side at boundary.FAR. Bands are already compact.
    • wall <cx> <cy> and 64 lines of 16 hex digits, one per walled chunk, a row's bit x the cell at column x. A wall's state or value is its cell's word in the chunk records, so dead walls cost one bit each and a chunk of dead walls has no chunk record.
  • Load mirrors sim.restore: clear, drop a drawn boundary the file lacks, take the rule, take the file's boundary, take the wall layer (sim.fill_walled), write the cells, then the walls (sim.fill_walls), so each wall keeps its cell's word.
  • Versions 2 to 6 and the dense pre-0.4 file load unchanged, as worlds without a boundary or walls.
  • A boundary or walls the world cannot take (a wrapping or foreign rule) load without them. Loaded.dropped and Opened.dropped carry why, and the toast says so.

The format contract carries walls (cellar.format).

  • Format.walls, Info.walls, Sink.walls, Source.walls and emit_walls.
  • A decoder emits wall runs before every cell run, row by row, so a sink knows them before writing the cells they hold. A sink or source without walls passes nil and trades in cells as before.

RLW keeps walls (cellar.formats.rlw).

  • After the cell body comes an optional [walls] section, an RLE-style body of o (wall) and b (not a wall) runs, ended by !. A wall holds its cell's word from the body.
  • The magic stays [RLW 1] and [RLW 2 n]. A file without walls is byte for byte what it was, and an older reader stops at the body's !.
  • PatternSink marks walls. WorldSink takes up the wall layer as it opens, writes the walls dead as they come, then writes the cells inside them as walls (sim.edit_as). PatternSource offers its walls.
  • WorldSource offers walls too. It lists the walled chunks as it is made, sorted as its cell runs are, and widens its extent to every wall, so cellar run --out x.rlw and every other whole-world export keep them.

Patterns (cellar.pattern).

  • A pattern holds a wall mask and a domain mask, the cells inside the boundary it carries.
  • capture reads walls (sim.read_walls). capture_bound takes the boundary inside the box.
  • place writes wall stretches as walls (dead ones too, holding their cells' states or words) and passes the world's walls by elsewhere, in one edit.
  • stamp first gives the world the pattern's boundary over its box (place_bound: the world's boundary, or every cell, less the box, plus the pattern's). It returns why it could not take the boundary or walls.
  • clone, orient, resize and clear handle both masks. pattern.rich covers words, walls or a boundary.

Copy and paste.

  • Copy carries the selection's walls. Cut takes its walls with it (an erase edit), since the copy carries them.
  • A "Copy keeps the boundary" toggle under the selection's operations makes copy and cut keep the boundary inside the selection.
  • The clipboard remembers a rich copy beside its RLE text, as it already did for words, so pasting that text back takes walls and boundary.

Primitives. boundary.append, sim.fill_walled, sim.fill_walls, sim.walled_chunks, sim.read_walls, sim.edit_as.

Ran

  • mach fmt --check ., mach build ., mach build . -p release.
  • Under a headless kwin: the persist, pattern, exec, region, formats, saves, journal and boundary tests, then the full mach test .. New tests:
    • load_world__round_trips_boundaries_of_several_regions_and_walls_of_every_kind: Life, a B0 rule, Generations, Lenia and reaction-diffusion, with dead, live and value walls and two regions. Also Life with walls alone and with a boundary alone, each loaded over a world with the other.
    • load_world__an_older_file_is_a_world_without_a_boundary_or_walls: cellar 2 to 6 and the dense file.
    • load_world__a_bound_or_wall_record_that_is_not_one_is_refused_untouched.
    • rlw__a_patterns_walls_round_trip_and_land_with_its_cells: the same five rules, through RLW, placed, and opened as a file.
    • run__a_copy_keeps_its_walls_and_boundary_and_a_stamp_lands_them: copy, clipboard, stamp, cut, and a cells-only paste.
    • frame__a_stamp_lands_its_walls_on_the_gpu_as_on_the_cpu: Life and Lenia, large and cell-by-cell stamps.
    • export__a_walled_world_written_as_rlw_loads_back_exactly: the five rules, a whole walled world through WorldSource and back.
    • run__a_stamp_that_carries_a_boundary_undoes_and_redoes_whole: boundary, cells and walls after undo and after redo.
  • A capture of the selection section with "Copy keeps the boundary" at 1280 px under kwin (out/selection-toggle-1280.png in the worktree, taken with an uncommitted patch that selects the World tab and scrolls it). The toggle fits its row inside the dock.
    • The pattern clone and orient test extended to walls and a boundary.

Decided

  • Copied and pasted patterns carry walls. This reverses feat: walls with a fixed state or value #289's decision, as feat: worlds and patterns carry boundaries and walls #291 asks. The cells-only paste is the clipboard's RLE text when another program wrote or changed it, and saving a pattern under a format other than RLW (RLE and the rest record no walls).
  • Cut takes walls with it. feat: walls with a fixed state or value #289 had cut pass walls by. Now that the copy carries them, leaving them in place would duplicate them on the next stamp. Clear, fill and invert still pass walls by.
  • The boundary travels only in memory. The stamp and the clipboard's remembered copy carry it. RLW carries walls alone, as the issue asks.
  • The boundary toggle is off by default. Copying it is the choice the issue names. With it off, a copy is cells and walls as before.
  • No RLW version bump. The walls section is self-identifying and comes after the body, so files without walls are unchanged and older readers still read the cells.
  • Always cellar 7. The writer always writes 7, rather than the oldest version that would hold the world, so there is one current format. An older cellar refuses it with a version mismatch, as it refused any newer version before.

…dit take them

boundary.append builds a region a band at a time, refusing one that does not
fit. sim gains fill_walled and fill_walls for a load that brings walls,
walled_chunks for a save, read_walls for a copy, and edit_as so one edit
writes a pattern's walls and its other cells alike.
…alls

A pattern marks its walls and the cells inside the boundary it carries.
place writes its walls as walls holding their cells' states or words, and
stamp gives the world its boundary over the pattern's box first.

The format contract gains walls: a format that records them emits them ahead
of every cell, a sink takes them (Sink.walls) and a source offers them
(Source.walls). RLW writes them after its body as a [walls] section of o and
b runs, which a reader that knows none stops before. A pattern with walls
defaults to RLW.
…ellar 7

A cellar 7 file's header says whether its cells are states or words, and its
body holds a bound record, a band a line, and a wall record for each walled
chunk, a row of 16 hex digits a line. A wall holds its cell's word, so dead
walls cost a bit each. Every older file loads as a world without a boundary
or walls, dropping a drawn boundary the world had. A boundary or walls the
world cannot take load without them, and the load says so.
…lection when asked

A copy carries the selection's walls, and a cut takes them with its cells.
With the selection's "Copy keeps the boundary" on, it carries the boundary
inside the selection too, which a stamp gives the world over its box. The
clipboard remembers such a copy beside its RLE, so that text pastes back
whole, while text another program wrote pastes as its cells. A stamp that
cannot take its boundary or walls says why.
@octalide
octalide marked this pull request as ready for review October 4, 2026 00:45
WorldSource lists the walled chunks as it is made, ordered as its cell runs
are, widens its extent to every wall, a dead one too, and offers them as runs
of their own (Source.walls), so cellar run --out x.rlw and every other
whole-world export to RLW keeps them.
@octalide
octalide merged commit c6bbecc into dev Oct 4, 2026
2 checks passed
@octalide
octalide deleted the feat/291 branch October 4, 2026 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: worlds and patterns carry boundaries and walls

1 participant