Game of Life: Tribes

#Rules Engine Internals

#Rule Semantics

Each generation evaluates every real grid cell independently against the current grid. Edge behavior depends on the selected topology:

  • Toroidal: neighbor reads wrap across opposite edges.
  • Bounded: neighbor positions outside the grid resolve to the selected virtual boundary tribe.

Bounded boundary cells are virtual. They are not stored in the frame and are not evaluated as cells; they only affect off-grid neighbor reads.

Rules are evaluated in order:

  1. Muted rules or rules with probability 00 are skipped.
  2. A rule whose clause matches performs its probability roll.
  3. The first matching rule whose probability roll passes assigns the next state.
  4. If no rule matches, or if matching probabilistic rules all fail their rolls, the cell becomes dead.

Rule evaluation flow

A rule has 33 behavioral parts:

  • Clause: boolean expression over the current cell and its neighbors.
  • Probability: percentage chance that a matched rule applies.
  • Outcome: expression that computes the next tribe id.

For the full selector, clause, outcome, tribe, and rule JSON reference, see Rule expressions. This page focuses on evaluation behavior and shader generation.

Probability uses deterministic randomness. The roll is derived from the cell coordinates, generation, rule index, and ruleset random seed, so the same snapshot, rules, seed, and generation reproduce the same outcomes. Rules with probability 100%100\% do not roll; once their clause matches, they apply through the same direct path as a deterministic rule. Failed probability rolls fall through to later rules instead of ending the first-match chain.

Top-level rules use bounded 3232-branch chains, to prevent reaching shader chaining length limit. An applied flag is checked only between chunks, so when a matching branch sets it, later chunks are skipped. For probabilistic rules, the probability roll is part of the branch condition, so a failed roll continues to later rules.

#Expression Handling

Selectors, clauses, and outcomes are normalized before comparison, persistence, and shader generation. The normalized form keeps equivalent editor states stable and gives shader generation predictable inputs.

  • Explicit tribe selector signatures sort and deduplicate selected tribe IDs for stable lookup keys.
  • Count-style clauses operate on the 88 Moore neighbors, and count values are clamped to 00 through 88.
  • Count-style clause bounds compile to the smallest equivalent boolean expression: none and exactly use equality, min uses only a lower-bound check, max uses only an upper-bound check, partial count intervals use a two-sided range, and always-true ranges such as count 0..8, min 0, and max 8 compile to true.
  • Empty clauses are editor placeholders. They compile as false, and the editor rejects applied rules that still contain empty placeholders.

#Dynamic Outcomes

Ranked outcomes evaluate eligible neighbor tribes selected by the outcome selector:

  • majority chooses the most common eligible neighbor tribe.
  • minority chooses the least common eligible neighbor tribe with a non-zero count.

The generated shader tracks the best candidate, best count, and tie count. A single winner writes that candidate. A tie evaluates the configured tie outcome. If no candidate exists, the fallback outcome is evaluated. Either branch may keep the current tribe, write a fixed tribe, or use a Combine lookup.

Combine outcomes build a bit mask of participating non-dead inputs. A row requires every listed input to be present and rejects every non-dead input it does not list. dead remains a separate presence requirement, so it can coexist with any matching non-dead combination. Lookup rows are sorted so rows explicitly requiring dead are checked before less specific rows. Rows are emitted, like rules, in bounded 3232-row first-match chains, so large lookup tables remain compilable without giving up early-match performance. If no row matches, the default outcome is evaluated.

#Shader Generation

generateComputeWgsl converts the normalized rule list and grid topology into WGSL:

  • It specializes neighbor reads for toroidal or bounded topology.
  • In toroidal topology, it emits the fast wrapping reads.
  • In bounded topology, it emits virtual off-grid reads that return the selected boundary tribe.
  • In bounded topology, packed words whose valid cells are all interior use direct neighbor reads for every lane. Edge-containing words keep per-cell interior checks and use boundary-aware reads only where needed.
  • It collects unique count selectors to avoid repeating the same neighbor-count expression. Always-true count clauses do not collect a selector unless another clause needs the same count.
  • It collects comparison selectors and reuses count variables when possible.
  • It emits a local variable for each unique count selector.
  • It emits clause expressions as optimized WGSL boolean expressions, including direct equality or one-sided comparisons for count clauses when a full two-sided range is unnecessary.
  • It emits outcomes as assignments to result.
  • It emits bounded first-match-wins branch chains over active rules, with deterministic probability guards where needed.
  • It waits for simulation shader diagnostics and asynchronous pipeline creation, surfacing compiler warnings separately from pipeline failures.
  • It emits direct assignment branches for 100%100\% rules, even when other active rules in the same shader are probabilistic.
  • It emits a random-seed constant and hash helper only when active probabilistic rules require probability rolls.

Unknown rule tribe references log an error and fall back to the dead tribe index. The editor tries to prevent those cases before rules are applied.

Game of Life: Tribes · Simulation v1.0.0 · Open source