The DSL

The when and do fields accept a tiny expression language. It's deliberately small; the goal is one-line readability. Drop into a CodeBlock (next chapter) when you need anything more.

Operators

+ - * /              arithmetic
== != < <= > >=      comparison (=== and !== work too, same meaning)
&& || !              boolean
=                    assignment (only inside do)
;                    statement separator

Built-in functions

random(min, max)        random number in [min, max)
chance(p)               true with probability p
random_int(min, max)    integer in [min, max]
sqrt(x)  abs(x)  floor(x)  ceil(x)  round(x)
min(a, b)  max(a, b)

Movement & geometry (agents only)

forward(d)              walk d patches along current heading
right(degrees)          turn right
left(degrees)           turn left
face(target)            point at an agent or patch
distance(target)        distance to an agent or patch

World queries

neighbors()             the 8 patches around the agent or patch
neighbors4()            the 4 cardinal neighbours
agents_here()           every agent on this patch
patches_in(r)           patches within radius r
neighbors_of_type(name) nearby agents of a given type

Agent lifecycle

create_agents(type, n)  spawn n agents of the given type
hatch(type, n)          spawn n where 'self' currently is
die()                   remove self

Aggregations

count, sum, mean, min, max operate on collections. count takes any array (including agents); the others want an array of numbers, so use .map to project a property first:

count(agents_of_type('sheep'))                          // 47
count(agents_of_type('sheep').filter(a => a.energy > 5))// 33
mean(agents_of_type('sheep').map(a => a.energy))        // 12.4
sum(all_patches().map(p => p.grass))                    // 1280

A common slip is mean(agents_of_type('sheep').energy) — that reads .energy on the array and gets undefined. Always .map first.

Self & globals

Inside agent or patch rules, self is the current entity. You can read properties bare (energy) when there's no name collision, but self.energy always works.

Sliders, switches, and choosers you've added on the Interface tab are available as bare identifiers - so a slider named sheep_repro shows up as just sheep_repro inside expressions.

Try it in the app

Everything described here works in the live editor. Open Stigmery and follow along.

Open the app →