Game of Life: Tribes

#Rule Cost Model

#Purpose

This page defines the rough per-cell worst-case cost model used to compare rule shapes.

The units are comparative estimates for generated shader work, not timing guarantees. 11 unit represents 11 simple comparison, boolean operation, arithmetic operation, branch test, or assignment.

Costs are worst-case estimates for a cell that evaluates the expression or reaches the rule branch being described. They are not necessarily paid by every cell in the whole ruleset: first-match-wins rule ordering can skip later branches, and some outcome paths such as ranked tie/fallback handling or combine rows only run when their branch reaches that path.

For selector, clause, and outcome semantics, see Rule expressions. For the WGSL generation pipeline, see Rule engine internals.

#Shared Work

A 11-condition count over the 88 Moore neighbors costs 88 units.

Clause count selectors are precomputed once per unique selector and reused by every clause that needs the same count. Cost estimates for a standalone expression include the count unless otherwise stated; when a count is already available, only the expression-specific check is additional.

Direct assignments and simple current-cell checks cost 11 unit.

#Cost Terms

Term Meaning
selectedTribeCount Number of explicit tribe IDs in a tribes or different-in selector.
countCost Cost of computing 11 selector's neighbor count. A 11-tribe selector, same, or different costs 88 when not reused.
leftCountCost Count cost for the left side of a comparison.
rightCountCost Count cost for the right side of a comparison.
tribeCount Active tribe count, including dead.
candidateCount Candidate tribe IDs considered by a ranked outcome.
rankingOverhead 4candidateCount4\cdot\texttt{candidateCount}. Per candidate: eligibility check, non-zero check, best-count comparison, and tie-count path.
selectedTieOrFallback Cost of the tie or fallback outcome path that actually runs after ranking.
nonDeadTribeCount Active tribe count excluding dead.
rowInputCost Cost of evaluating lookup-row input selectors in a combine outcome.
rowCount Number of lookup rows in a combine outcome.

#Rule Branches

A deterministic 100%100\% rule branch costs: 1+clause+outcome1 + \texttt{clause} + \texttt{outcome}. The leading 11 is the rule branch test.

A probabilistic rule with probability greater than 00 and less than 100100 adds 1010 units for the deterministic hash/threshold guard.

Muted rules and rules with probability 00 do not emit active rule branches. Outcome work is only paid on the branch that executes.

#Selector Costs

#tribes

As a count selector: 8selectedTribeCount8 \cdot \texttt{selectedTribeCount}. Each neighbor is compared against the selected tribe IDs.

As a ranked selector, the explicit list is the candidate list: 8selectedTribeCount+rankingOverhead8 \cdot \texttt{selectedTribeCount} + \texttt{rankingOverhead}.
For this selector form, candidateCount=selectedTribeCount\texttt{candidateCount}=\texttt{selectedTribeCount}.

Explicit selector signatures are sorted and deduplicated for stable reuse.

#same

As a count selector: 88.

As a ranked selector: 8tribeCount+rankingOverhead8 \cdot \texttt{tribeCount} + \texttt{rankingOverhead}.

The ranked implementation still iterates the full tribe list as candidate IDs and uses candidate == selfTribe as the eligibility check.

#different

As a count selector: 88.

As a ranked selector: 8tribeCount+rankingOverhead8 \cdot \texttt{tribeCount} + \texttt{rankingOverhead}.

The ranked implementation still iterates the full tribe list and uses candidate != selfTribe as the eligibility check.

#different-in

As a count selector: 8selectedTribeCount8\cdot\texttt{selectedTribeCount}.

Each neighbor is compared against the selected tribe IDs and against the current cell tribe.

As a ranked selector: 8selectedTribeCount+rankingOverhead8 \cdot \texttt{selectedTribeCount} + \texttt{rankingOverhead}.

For this selector form, candidateCount=selectedTribeCount\texttt{candidateCount}=\texttt{selectedTribeCount}; per-candidate eligibility also excludes candidate == selfTribe.

#tie

Inside ranked tie handling, tie reuses the current ranked state: 00.

When used in a combine row, each tied candidate referenced by the row can add 88 units for a candidate neighbor count.

Outside a ranked tie branch, the compiler has no tie state and treats the selector like its source.

#Clause Costs

Clause Cost
is selectedTribeCount\texttt{selectedTribeCount}
count countCost+2\texttt{countCost} + 2
none countCost+1\texttt{countCost} + 1
exactly countCost+1\texttt{countCost} + 1
min countCost+1\texttt{countCost} + 1
max countCost+1\texttt{countCost} + 1
comparison leftCountCost+rightCountCost+1\texttt{leftCountCost} + \texttt{rightCountCost} + 1
not childClause+1\texttt{childClause} + 1
and sum(childClauses)+(childCount1)\text{sum}\left(\texttt{childClauses}\right) + \left(\texttt{childCount} - 1\right)
or sum(childClauses)+(childCount1)\text{sum}\left(\texttt{childClauses}\right) + \left(\texttt{childCount} - 1\right)
xor sum(childClauses)+(2childCount)+1\text{sum}\left(\texttt{childClauses}\right) + \left(2\cdot\texttt{childCount}\right) + 1
empty 00

#Simplified Constant Clauses

Some clause shapes compile to constants and avoid unnecessary count work:

  • count with [0, 8] costs 00;
  • min 0 costs 00;
  • max 8 costs 00;
  • is covering every known tribe can cost 00 as true;
  • is resolving to no known tribe can cost 00 as false;
  • empty costs 00 and always compiles to false.

#Reused Counts

When a selector count has already been precomputed, only the final test is additional:

count:+2none:+1exactly:+1min:+1max:+1\begin{aligned} \texttt{count}&:\quad +2 \\ \texttt{none}&:\quad +1 \\ \texttt{exactly}&:\quad +1 \\ \texttt{min}&:\quad +1 \\ \texttt{max}&:\quad +1 \end{aligned}

A comparison likewise reuses either side independently when its count is already available.

#Outcome Costs

#fixed

A direct assignment to 11 tribe index costs 11 unit.

#same

A direct assignment from the current cell value costs 11 unit.

#majority and minority

Ranked outcomes cost: 8candidateCount+rankingOverhead+selectedTieOrFallback8\cdot\texttt{candidateCount}+\texttt{rankingOverhead}+\texttt{selectedTieOrFallback}, where: rankingOverhead=4candidateCount\texttt{rankingOverhead}=4\cdot\texttt{candidateCount}.

For a tribes selector, candidateCount is the explicit selector size, so candidateCount=selectedTribeCount\texttt{candidateCount}=\texttt{selectedTribeCount}.

For same and different, candidateCount is the full tribe list size. For tribes and different-in, candidateCount is the selected tribe count. Per-candidate eligibility checks determine which candidates can participate for the current cell.

Only the tie or fallback path that actually runs is counted in selectedTieOrFallback.

#combine

Combine outcomes cost: 8nonDeadTribeCount+8+rowInputCost+rowCount8\cdot\texttt{nonDeadTribeCount}+8+\texttt{rowInputCost}+\texttt{rowCount}.

The terms are:

8nonDeadTribeCountbuild the base non-dead input mask8detect whether any dead neighbor is presentrowInputCost8 units per non-dead candidate referenced by row inputsrowCount1 mask comparison per lookup row\begin{aligned} 8\cdot\texttt{nonDeadTribeCount}&\quad &&\text{build the base non-dead input mask} \\ 8& &&\text{detect whether any }\texttt{dead}\text{ neighbor is present} \\ \texttt{rowInputCost}& &&8\text{ units per non-dead candidate referenced by row inputs} \\ \texttt{rowCount}& &&1\text{ mask comparison per lookup row} \end{aligned}

Rows that explicitly include dead add a cheap deadPresent condition.

Large tribe lists or combination tables can make generated shaders noticeably larger and slower.

This is the worst-case cost once the combine outcome runs. A row that matches early can avoid later row checks at runtime, but the formula counts all rows so lookup tables can be compared conservatively.

#Whole-Ruleset Considerations

#Rule Count and Order

More rules increase branch work and shader size.

Rule order matters because the engine uses first-match-wins evaluation. Earlier rules can prevent later branches from being evaluated.

A fixed-dead rule often adds work without changing the result because result already starts as dead. It is generally preferable to narrow a later positive rule unless the earlier branch deliberately exists to block it.

#Probabilistic Rules

Failed probabilistic rolls continue to later rules.

Overlapping probabilistic rules can therefore pay multiple clause and branch costs for the same cell before one applies or the chain ends.

#Bounded Topology

Bounded topology can add overhead near grid edges because off-grid neighbor reads must resolve to the virtual boundary tribe.

Interior bounded cells use a faster direct path, so this extra cost is concentrated on packed words that contain grid edges.

#Worked Examples

#Conway Birth

The standalone rule:

is dead
AND exactly 3 Alive neighbors
→ fixed Alive

costs 1313 units:

Cost Component
88 Alive count
11 is dead
11 exactly 3
11 Boolean AND
11 Rule branch
11 Fixed outcome
1313 Total

#Conway Survival

The standalone rule:

is Alive
AND count Alive in [2, 3]
→ same

costs 1414 units:

Cost Component
88 Alive count
11 is Alive
22 Range check
11 Boolean AND
11 Rule branch
11 same outcome
14\mathbf{14} Total

When paired with Conway birth, the Alive count is already available. Adding survival therefore costs only 66 additional units.

#Probabilistic Spread

A rule that matches Paper, requires at least 11 Fire neighbor, and becomes Fire with probability 6.25%6.25\% costs 2323 units:

Cost Component
88 Fire count
11 is Paper
11 min Fire 1
11 Boolean AND
11 Rule branch
1010 Probability hash/threshold guard
11 Fixed outcome
23\mathbf{23} Total

A failed roll continues to later rules.

#Ranked Majority

For:

different count >= 1
→ majority different
→ same on tie or fallback

the cost is: 11+8tribeCount+rankingOverhead11+8\cdot\texttt{tribeCount}+\texttt{rankingOverhead}.

The fixed 1111 contains the different count clause, rule branch, and selected same tie/fallback path.

#Combine With Dead Priority

For the example with:

[Red, Blue, dead] → Ash
[Red, Blue]       → Purple
default           → dead

the cost is: 45+8nonDeadTribeCount45+8\cdot\texttt{nonDeadTribeCount}.

The fixed 4545 contains:

Cost Component
22 Rule branch + is dead
88 dead-presence count
22 Lookup-row comparisons
3232 44 non-dead row-input references
11 Default fixed outcome
45\mathbf{45} Total

The base mask adds 8nonDeadTribeCount8\cdot\texttt{nonDeadTribeCount}.

The explicit-dead row must be checked first because it has the same non-dead mask as the Red + Blue row.

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