Task 5 of 5

One Kernel, Every Universe

B3/S23 is one point in a whole family. Any outer-totalistic rule is fully described by two 9-entry tables: born[n] — does a dead cell with n live neighbors come alive? — and stay[n] — does a live cell with n survive? Conway is born[3] = 1, stay[2] = stay[3] = 1, zeros everywhere else. HighLife adds born[6] = 1 and suddenly the world contains a pattern that builds copies of itself.

Here's the move that matters: pass the tables as kernel arguments. The rulebook stops being code and becomes data — one compiled kernel runs every universe in the family, and switching physics is just passing different arrays. No if per rule, no recompile: alive cells look up stay[count], dead cells look up born[count].

Goal: finish the evolve kernel so it applies whatever rule tables it's handed — then let the wired-up code count where Life and HighLife disagree about the same world's next tick.

Requirements

Hint 1 — arrays index like anywhere else

count is a number from 0 to 8, and born is a 9-entry array — born[count] is already the answer for a dead cell. The lookup is the rule.

Hint 2 — a single return
let fate = born[count];
if (self === 1) fate = stay[count];
return fate;

Same idea elsewhere

Shipping small lookup tables to a fixed kernel instead of recompiling is how GPUs stay fast when behavior changes: CUDA and ROCm keep them in __constant__ memory, WebGPU and Metal bind them as uniform buffers — same shader, new physics, zero pipeline rebuilds.

All tasks in Cellular Automata

  1. The Neighbor Census
  2. One Tick of Life
  3. Generations: Feed It Back
  4. Watch the Glider Fly
  5. One Kernel, Every Universe

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.