Reference

cruxVM Runtime

The deterministic simulation engine that brings worlds to life.

Overview

cruxVM is the runtime engine that executes world simulations. It guarantees deterministic execution — the same inputs always produce the same outputs.

🔄

Deterministic

Same IR + seed = identical results every time

Fast

Optimized for thousands of ticks per second

🌐

Edge-Ready

Works in browsers, Node.js, and edge runtimes

🔌

Extensible

Add custom actions and conditions

Creating a VM

Create a VM instance with validated cruxIR. The VM clones the initial state, so you can safely reset to the original.

Basic Usage
import { CruxVM } from '@cruxos/vm';
import { validate } from '@cruxos/ir';

// Validate IR first
const result = validate(worldJson);
if (!result.success) {
  throw new Error('Invalid IR');
}

// Create VM
const vm = new CruxVM(result.data);

// Run simulation
const output = vm.run(100); // Run 100 ticks

console.log(`Executed: ${output.ticksExecuted} ticks`);
console.log(`Events: ${output.events.length}`);
console.log(`Final state:`, output.finalState);

Configuration Options

VMConfig
const vm = new CruxVM(ir, {
  maxTicks: 1000,           // Maximum ticks to run

  onTick: (state) => {     // Called after each tick
    console.log(`Tick ${state.tick}`);
  },

  onEvent: (event) => {    // Called when events occur
    console.log(`Event: ${event.type}`);
  }
});

Tick Execution

Each tick follows a precise execution order to ensure determinism.

1

Clear Events

Previous tick's events are cleared

2

Sort Rules

Rules sorted by priority (higher first)

3

Execute Rules

For each rule: select agents → check conditions → run actions

4

Deliver Messages

Move outbox → inbox for all agents

5

Increment Tick

Advance tick counter, call onTick callback

Manual Tick Execution
// Execute single ticks manually
for (let i = 0; i < 10; i++) {
  vm.tick();
  console.log(`After tick ${i}: ${vm.getState().agents.length} agents`);
}

// Or use run() for multiple ticks
const result = vm.run(100);

State Access

Access the current world state and query agents.

State Methods
// Get full state
const state = vm.getState();
console.log(`Current tick: ${state.tick}`);
console.log(`Agents: ${state.agents.length}`);

// Find specific agent
const agent = vm.getAgent('visitor_1');
console.log(`Position: (${agent.position.x}, ${agent.position.y})`);

// Find agents by type
const visitors = vm.getAgentsByType('visitor');
console.log(`${visitors.length} visitors in the world`);

// Emit custom event
vm.emit('custom_event', { data: 'value' }, 'source_id', 'target_id');

// Reset to initial state
vm.reset();

Deterministic RNG

cruxVM uses a seeded pseudo-random number generator. The same seed always produces the same sequence of random numbers.

Never Use Math.random()

Using Math.random() breaks determinism. Always use the VM's RNG.

Using the RNG
const rng = vm.getRNG();

// Random float [0, 1)
const float = rng.next();

// Random integer in range [min, max]
const roll = rng.range(1, 6);

// Random float in range [min, max)
const percent = rng.rangeFloat(0, 100);

// Random choice from array
const direction = rng.choice(['north', 'south', 'east', 'west']);

// Shuffle array (in place)
const items = ['a', 'b', 'c'];
rng.shuffle(items);

RNG Algorithm

cruxVM uses a Mulberry32 PRNG — fast, deterministic, and with good statistical properties. The 32-bit seed is set from the world metadata.

Actions

Actions are functions that modify agent state. They receive a context object with everything needed for deterministic execution.

Action Context
interface ActionContext {
  agent: Agent;              // Current agent
  rng: DeterministicRNG;     // Random number generator
  zones: Zone[];             // All zones
  agents: Agent[];           // All agents
  globals: Record;           // Global variables
  params: Record;            // Rule parameters
  tick: number;              // Current tick
  emit: (type, data?) => void; // Emit event
}

Built-in Actions

wander

Random movement within bounds

speed: number
move_toward

Move toward a target position

target: {x, y}, speed: number
set_state

Update agent state properties

Any key-value pairs
deplete_energy

Reduce agent's energy

amount: number
rest

Recover energy and set resting state

recovery: number
fly_random

Random flight within bounds

speed, maxX, maxY: number

Custom Actions

Registering Custom Actions
import { registerAction } from '@cruxos/vm';

registerAction('teleport', (ctx) => {
  const { agent, params, rng } = ctx;

  // Teleport to random position
  agent.position.x = rng.range(0, params.maxX);
  agent.position.y = rng.range(0, params.maxY);

  // Emit event
  ctx.emit('teleported', {
    to: { x: agent.position.x, y: agent.position.y }
  });
});

Conditions

Conditions are expressions that evaluate against agent state to determine if a rule should execute.

Supported Operators

OperatorDescriptionExample
==Equalmood == "happy"
!=Not equalstatus != "dead"
>Greater thanenergy > 50
<Less thanhealth < 20
>=Greater or equallevel >= 5
<=Less or equalage <= 100
&&Logical ANDalive && energy > 0
||Logical ORflying || swimming
Complex Conditions
{
  "condition": "energy >= 30 && resting != true && mood == \"happy\""
}

Condition Context

Conditions can access:

  • agent.state.* — Agent's state properties (directly by name)
  • globals.* — Global world variables
  • tick — Current tick number
  • Zone proximity (via custom conditions)

Next Steps