Rule
Rules define how elements evolve over time. At each simulation step, every cell is checked against the rules of its current element (and any kinds it extends). The first rule whose conditions are satisfied is applied.
You create a rule using the to method on an element or kind.
Evolve to element
Section titled “Evolve to element”The most common form of rule transitions a cell to a specific element:
const space = vi.element("space", "blue");const alien = vi.element("alien", "green");
space.to(alien);This means: every cell that is currently space becomes alien. Without any conditions, this happens unconditionally at every step.
Evolve to neighbor reference
Section titled “Evolve to neighbor reference”Instead of passing a predetermined element, you can pass a neighbor reference. The cell will become whatever element is at that position in its neighborhood.
space.to(vi.neighbor.TOP);In this example, every space cell becomes a copy of whatever element is directly above it. This is useful for simulating gravity, flow, or other directional effects.
You can also use vi.neighbor.SELF to keep the cell as itself, which can be combined with conditions:
alien.to(vi.neighbor.SELF).count(alien, 2, 3);Adding conditions
Section titled “Adding conditions”Rules become interesting when combined with conditions. Chain condition methods after to to control when the rule is applied:
space.to(alien).count(alien, 3);See Condition for all available condition types.
Rule ordering
Section titled “Rule ordering”Rules are evaluated in the order they are declared, from first to last. The first rule whose conditions pass wins — no further rules are checked for that cell during that step.
This means you should declare specific rules first and general fallback rules last:
// Specific: alien stays if it has 2 or 3 alien neighborsalien.to(alien).count(alien, 2, 3);
// Fallback: otherwise the alien disappearsalien.to(space);If you swapped the order, the unconditional alien.to(space) would always match first and the conditional rule would never be reached.
Rules on kinds
Section titled “Rules on kinds”Rules can also be defined on kinds. When a rule is on a kind, it applies to all elements that extend that kind, but with lower priority than rules defined directly on the element itself.
const creature = vi.kind("creature");
const empty = vi.element("empty", "black");const fish = vi.element("fish", "blue", [creature]);const bird = vi.element("bird", "orange", [creature]);
// Birds have their own specific rulebird.to(bird).count(bird, 2, 3);
// This rule applies to both fish and bird, after their own rulescreature.to(empty).count(creature, 0);In this example, bird checks its own rule first — if it has 2 or 3 bird neighbors, it stays. If not, it falls back to the kind rule: if there are no creatures nearby, it dies. fish has no element-level rules, so it uses the kind rule directly.
See Kind for more details.