Skip to content

World

The world block defines the simulation environment - the spatial structure, the entities that populate it, how those entities interact with agents, and the autonomous systems that run independently of any brain. The world is a fully declarative construct: all world mechanics are expressed in .quale.

A route-topology world with waypoints, warning markers, and speed zones.

world OperatorRoute {
topology: route
length: 10.0 km
max_speed: 60.0 km/h
tick: 0.5 s
-- Waypoints the operator must stop at
entity waypoint {
properties { position: km, scheduled_time: float }
spawn: 5
on_enter(threshold: 0.16 km, max_speed: 1.5 m/s) {
agent.stopped_at_waypoint = true
agent.waypoints_served += 1
record waypoint_visit {
stopped: 1.0,
arrival_time: agent.elapsed
}
}
on_pass {
record waypoint_visit {
stopped: 0.0,
arrival_time: agent.elapsed
}
}
}
-- Warning markers along the route
entity warning_marker {
properties { position: km, severity: 0..1 }
on_cross {
when severity > 0.5 {
agent.warning_active = true
agent.warning_acknowledged = false
agent.cognitive_load += 0.05
}
}
}
-- Speed restriction zones
entity speed_zone {
properties { start: km, end: km, limit: km/h }
}
query nearest_ahead(entity_type, position) -> distance, index, properties
query speed_zone_at(position) -> limit
import entities from "route-data.csv"
}

The topology defines how space works in the world. The engine provides three spatial primitives.

TopologySyntaxDescription
Routetopology: route1D scalar dimension (distance along a path). Position is a single float
Gridtopology: grid(W, H)2D integer grid with optional walls. Position is two integers (x, y)
Graphtopology: graphNode-edge network. Position is a node index

Route topology is used when the agent traverses a linear path. Both Human Factors and Network Security use route topology.

-- Human Factors
world OperatorRoute {
topology: route
length: 10.0 km
max_speed: 60.0 km/h
tick: 0.5 s
}
-- Network Security
world TrafficStream {
topology: route
length: 1000.0 km
max_speed: 1.0 km/h
tick: 0.1 s
}
PropertyDescription
lengthTotal length of the 1D route
max_speedMaximum speed in the world (used by perception for normalization)
tickReal-time seconds per simulation tick

Grid topology is used for 2D spatial environments. The Survival demo uses a grid.

world ForestFloor {
topology: grid(15, 15)
walls: border
tick: 1.0 s
}
PropertyDescription
wallsWall configuration. Currently only border is supported
tickReal-time seconds per simulation tick

Entities are the objects that populate the world. Each entity type declares its properties and interaction handlers.

Properties are named, typed values that each instance of the entity carries. All properties are accessible to interaction handlers and spatial query results.

-- Human Factors: waypoints with position and schedule
entity waypoint {
properties { position: km, scheduled_time: float }
}
-- Network Security: packet flows with multiple metric properties
entity packet_flow {
properties {
position: km,
packet_rate: 0..1,
entropy: 0..1,
syn_ratio: 0..1,
age: 0..1,
malicious: bool
}
}
-- Survival: food with observable properties
entity food {
properties { color: 0..1, smell: 0..1, texture: 0..1 }
}

For route topology, every entity must have a position property. For grid topology, entities have a grid cell position managed by the engine.

When an agent’s position update sweeps past an entity, the engine fires the appropriate handler.

HandlerFires whenParametersUse case
on_crossAgent’s position sweep crosses the entity positionnonePoint-of-passage events
on_enter(threshold, max_speed)Agent is within threshold distance AND below max_speedthreshold, max_speedControlled stops
on_passAgent crosses entity without meeting on_enter conditionsnoneMissed stops

Inside handlers, entity properties are accessible by name (e.g., severity, malicious, color). Agent state is accessible via agent.*.

Fires whenever the agent crosses the entity. Used for point-of-passage events in route worlds and cell-overlap events in grid worlds.

Route example (Network Security): Entity properties are loaded into agent state and a record is emitted for fitness scoring.

entity packet_flow {
properties {
position: km,
packet_rate: 0..1,
entropy: 0..1,
syn_ratio: 0..1,
age: 0..1,
malicious: bool
}
spawn: 100
on_cross {
-- Load connection properties into agent state
agent.packet_rate = packet_rate
agent.payload_entropy = entropy
agent.syn_ratio = syn_ratio
agent.connection_age = age
agent.is_threat = malicious
agent.connections_seen += 1
-- Record the decision for fitness scoring
when actuator.block > 0.5 {
when malicious {
agent.true_positives += 1
agent.threats_blocked += 1
}
when not malicious {
agent.false_positives += 1
}
record classification {
blocked: 1.0,
was_threat: malicious ? 1.0 : 0.0,
correct: malicious ? 1.0 : 0.0
}
}
when actuator.block <= 0.5 {
when malicious {
agent.threats_missed += 1
}
record classification {
blocked: 0.0,
was_threat: malicious ? 1.0 : 0.0,
correct: malicious ? 0.0 : 1.0
}
}
}
}

Route example (Human Factors): A conditional handler that fires only for high-severity markers.

entity warning_marker {
properties { position: km, severity: 0..1 }
on_cross {
when severity > 0.5 {
agent.warning_active = true
agent.warning_acknowledged = false
agent.cognitive_load += 0.05
}
}
}

Fires when the agent is within a distance threshold AND below a maximum speed. Requires both parameters. If the agent crosses the entity without meeting these conditions, on_pass fires instead.

-- Human Factors: operator must slow down to stop at waypoints
entity waypoint {
properties { position: km, scheduled_time: float }
spawn: 5
on_enter(threshold: 0.16 km, max_speed: 1.5 m/s) {
agent.stopped_at_waypoint = true
agent.waypoints_served += 1
record waypoint_visit {
stopped: 1.0,
arrival_time: agent.elapsed
}
}
on_pass {
record waypoint_visit {
stopped: 0.0,
arrival_time: agent.elapsed
}
}
}

In grid worlds, on_cross fires when the agent occupies the same cell as the entity. The consume() function removes the entity instance and starts a respawn timer.

-- Survival: safe food restores hunger and energy
entity food {
properties { color: 0..1, smell: 0..1, texture: 0..1 }
spawn: 8
respawn: 20 ticks
on_cross {
when actuator.eat > 0.5 {
agent.hunger -= 0.3
agent.energy += 0.2
agent.health += 0.05
agent.food_eaten += 1
consume()
}
}
}
-- Survival: poisonous food causes nausea and health loss
entity poison {
properties { color: 0..1, smell: 0..1, texture: 0..1 }
spawn: 3
respawn: 30 ticks
on_cross {
when actuator.eat > 0.5 {
agent.nausea += 0.4
agent.health -= 0.3
agent.energy -= 0.1
consume()
}
}
}

Interaction handlers can emit typed event records using the record keyword. These records are appended to the scenario event log and used by the fitness block for per-record metric aggregation.

-- Human Factors: track waypoint stops with timing data
record waypoint_visit {
stopped: 1.0,
arrival_time: agent.elapsed
}
-- Network Security: track classification decisions
record classification {
blocked: 1.0,
was_threat: malicious ? 1.0 : 0.0,
correct: malicious ? 1.0 : 0.0
}

Record fields can be bare names (resolved from handler scope) or name: expression pairs. Record types are defined implicitly by their first emission - all subsequent record statements of the same type must have the same fields.

Grid-world entities can declare spawn counts and respawn timers. Route-world entities can declare spawn counts for procedural placement.

-- Survival: grid entities with spawn and respawn
entity food {
properties { color: 0..1, smell: 0..1, texture: 0..1 }
spawn: 8 -- place 8 instances at random positions
respawn: 20 ticks -- consumed entities reappear after 20 ticks
}
-- Network Security: route entities with spawn count
entity packet_flow {
properties { position: km, ... }
spawn: 100 -- 100 packet flows along the traffic stream
}
-- Human Factors: route entities with spawn count
entity waypoint {
properties { position: km, scheduled_time: float }
spawn: 5
}

Entities without interaction handlers define passive spatial data. Speed zones are a common pattern for route topology.

-- Human Factors
entity speed_zone {
properties { start: km, end: km, limit: km/h }
}

Speed zones are accessed via the speed_zone_at spatial query rather than interaction handlers.


Spatial queries provide efficient lookups against entity collections. They are declared in the world block and callable from perception, action, machines, and entity handlers.

-- Route topology queries (Human Factors)
query nearest_ahead(entity_type, position) -> distance, index, properties
query speed_zone_at(position) -> limit
-- Route topology queries (Network Security)
query nearest_ahead(entity_type, position) -> distance, index, properties

Spatial queries return a result struct. Entity properties are promoted to top-level fields on the result.

-- Human Factors: query a waypoint
let chk = nearest_ahead(waypoint, agent.position)
-- chk.distance - distance to the entity (built-in)
-- chk.index - ordinal position in the entity list (built-in)
-- chk.position - promoted from waypoint.properties.position
-- Human Factors: query the current speed zone
let zone = speed_zone_at(agent.position)
-- zone is the speed limit value (scalar result)

When a spatial query finds no matching entity (e.g., past the last waypoint on the route):

FieldFallback value
.distancePositive infinity (large float)
.index-1
Any property0.0

Perception code should account for these fallback values.

QuerySignatureDescription
nearest_ahead(type, pos)-> distance, index, propertiesNext entity of type ahead of position
speed_zone_at(pos)-> limitSpeed limit at position. Returns world.max_speed if no zone contains the position

Route queries use a 10m (0.01 km) deadband: entities at or behind the agent’s current position are excluded to prevent re-triggering.

QuerySignatureDescription
nearest(type, pos, direction)-> distance, propertiesNearest entity of type in a specific direction
at(type, pos)-> bool, propertiesWhether an entity of type exists at the given grid cell
QuerySignatureDescription
neighbors(pos)-> count, listAdjacent nodes from current position
connected(from, to)-> boolWhether an edge exists between two nodes
shortest_path(from, to)-> distance, next_hopShortest path length and next node toward target

World-scoped state machines are declared inside the world block. They run at step 1 of the tick loop (before perception), so they can update world state that perception reads. See also agent machines in the body block.

FeatureSyntaxDescription
Scopescope: world or scope: agentDetermines tick loop placement and visibility
Initial stateinitial: <state>Starting state. Defaults to first declared state
State blockstate <name> { ... }Per-tick logic that runs while in this state
Entry actionon_enter { ... }Runs once on transition into the state
Exit actionon_exit { ... }Runs once on transition out of the state
Transitiontransition A -> B: when <cond>State transition rule
TimertimerPer-machine local variable, initialized to 0
Elapsedelapsed_in_stateSeconds since entering current state (resets on transition)
Machine-level letlet x = expr at machine levelEvaluated once per tick, visible to all states
Entity iterationfor entity in world.<entities> { ... }World machines only
  • Bottom transitions (declared at machine level) are evaluated after all state logic, in declaration order
  • When a transition fires: on_exit runs for the old state, then on_enter runs for the new state, all within the same tick
  • The new state’s per-tick logic does not run in the transition tick; it begins on the next tick
ScopeCan readCan write
scope: worldWorld state, entity properties, spatial queriesWorld state, entity properties
scope: agentAgent state, actuator outputs, world state (read-only), spatial queriesAgent state only

Entity instances can be loaded from external CSV files. The engine validates that every imported instance has the required properties declared in the entity type definition.

-- Human Factors
import entities from "route-data.csv"
-- Network Security
import entities from "traffic-data.csv"

The CSV file path is relative to the .quale file’s directory. Each row becomes an entity instance; columns must match entity type property names. The entity type is determined by a type column in the CSV.

Inline entity instances are also supported for small datasets:

waypoint "Checkpoint Alpha" { position: 2.0, scheduled_time: 120.0 }

All keywords and constructs available inside a world block.

FieldSyntaxRequiredDescription
topologytopology: route or topology: grid(W, H)YesSpatial structure of the world
lengthlength: N unitRoute onlyTotal length of the 1D route
max_speedmax_speed: N unitRoute onlyMaximum speed (used for perception normalization)
wallswalls: borderGrid onlyWall configuration
ticktick: N sYesReal-time seconds per simulation tick
entityentity <name> { ... }NoEntity type definition
queryquery <name>(...) -> ...NoSpatial query declaration
importimport entities from "file.csv"NoExternal data import
statestate <name>: <type> = <value>NoWorld state variable
machinemachine <Name> { ... }NoWorld-scoped state machine
entity <name> {
properties { <name>: <type>, ... }
spawn: <count>
respawn: <count> ticks
on_cross { <statements> }
on_enter(threshold: <value> <unit>, max_speed: <value> <unit>) { <statements> }
on_pass { <statements> }
}
Sub-blockRequiredDescription
propertiesNoNamed, typed values each entity instance carries
spawnNoNumber of instances to place at scenario start
respawnNoTicks until a consumed entity reappears (grid worlds)
on_crossNoHandler fired when agent crosses the entity
on_enterNoHandler fired when agent is within threshold distance AND below max_speed
on_passNoHandler fired when agent crosses entity without meeting on_enter conditions

All sub-blocks are optional and may appear in any order.

Inside entity interaction handlers (on_cross, on_enter, on_pass), the full statement language is available:

StatementSyntaxDescription
letlet x = exprLocal variable binding
whenwhen cond { stmts }Conditional guard
when/else when/elsewhen cond { } else when cond { } else { }Exclusive chain
when (single-line)when cond: stmtSingle-line conditional
Assignmentagent.X op exprModify agent state (=, +=, -=, *=, /=)
recordrecord type { fields }Emit a typed event record
consumeconsume()Remove the entity from the world

Inside handlers, entity properties are accessible by bare name. For example, in an on_cross handler for an entity with properties { severity: 0..1 }, the name severity resolves to the current entity instance’s property value.

Spatial queries return a result struct. Use dot access to read fields.

Struct result (e.g., nearest_ahead):

FieldTypeDescription
.distancefloat64Distance from the agent to the entity
.indexfloat64Ordinal position in the domain’s entity list, or -1 if no match
.<property>float64Entity properties are promoted to top-level fields
let chk = nearest_ahead(waypoint, agent.position)
-- chk.distance - built-in: distance to entity
-- chk.index - built-in: entity ordinal index
-- chk.position - promoted from waypoint properties
-- chk.scheduled_time - promoted from waypoint properties

Scalar result (e.g., speed_zone_at):

let zone = speed_zone_at(agent.position)
-- zone is the speed limit value directly (not a struct)

No-match fallback values:

FieldFallback
.distancePositive infinity (very large float)
.index-1
Any property0.0