CodeBlock escape

The DSL covers most simple rules. For anything that needs loops, multi-step state changes, or genuine helper code, set the do field to a CodeBlock.

In the editor, toggle the CodeBlock (TS escape) checkbox above the Monaco editor. The body accepts JavaScript-shaped code with the same scope as a DSL expression: bare names resolve to the current agent/patch/globals, all DSL functions are available, and me refers to the current entity.

Example: flocking-style alignment

const others = neighbors(3);            // agents within 3 patches
if (others.length === 0) return;

// average heading of nearby boids
let sx = 0, sy = 0;
for (const b of others) {
  sx += Math.cos(b.heading * Math.PI / 180);
  sy += Math.sin(b.heading * Math.PI / 180);
}
const target = Math.atan2(sy, sx) * 180 / Math.PI;

// rotate 10% of the way toward the group's average heading
turn(0.1 * (target - heading));
move_forward(1);

What's allowed

  • Assignments, conditionals, loops (for, while), local let/const.
  • The full DSL function set (move_forward, neighbors, chance, ...).
  • Standard JS globals: Math, JSON, Array, Object, Number, String, Boolean, parseInt, parseFloat, isNaN, isFinite. These are also available in regular one-line expressions.

What's banned

  • await / async - rules must finish in one tick.
  • eval, new Function(...), import(...) - no dynamic code loading.

Violations throw at parse time; the rule then surfaces as a red ! error badge next to its name in the Code tab.