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), locallet/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.