Quickstart

Quickstart

Run your first autonomous simulation in under 2 minutes. Start with the engine-owned example — no Ollama, no API keys, no Unity required. Then layer in LLM cognition or Unity integration when you're ready.

Prerequisites

RequirementMinimumNotes
Python3.11+Required for match statements and tomllib
pip23+For editable installs with [extras]
gitanyTo clone the repository
OllamaoptionalOnly needed for OllamaLLMBrain. Not required for this quickstart.
UnityoptionalUnity 6 only. Only needed for the C# SDK integration.

Step 1 — Clone and install

bash
git clone https://github.com/smithd36/biomata-engine.git
cd biomata-engine

# Core only — sufficient for this quickstart
pip install -e .

# Or: core + WebSocket server (for game engine integration)
pip install -e ".[websocket]"

Verify the install:

bash
python -c "from src.engine.simulation import Simulation; print('OK')"

Step 2 — Run the engine-owned demo

The engine-owned example runs three agents (two guards, one merchant) with a deterministic brain. No external dependencies. The backend ticks whether or not a game client is connected.

bash
biomata-ws --config examples/engine_owned/sim.yaml --port 8765

You should see agent decisions streaming tick by tick:

text
Tick 1 | Aldric | patrol → gate_post    | holding position at gate
Tick 1 | Berna  | patrol → gate_post    | holding position at gate
Tick 1 | Silas  | trade  → market_stall | idle at market stall
Tick 2 | Aldric | patrol → gate_post    | holding position at gate
...
No Ollama needed

The engine-owned example uses IdleBrain — a deterministic brain that follows scripted patterns. It produces real autonomous behavior without any LLM. Swap in OllamaLLMBrain in sim.yaml when you're ready to add language model cognition — no Unity or client changes needed.

Both examples

Two examples ship with the engine. Both use IdleBrain by default — no Ollama required.

bash
# Engine-Owned — 3 pre-declared agents; backend runs with or without Unity
biomata-ws --config examples/engine_owned/sim.yaml --port 8765

# Host-Owned — backend starts empty; Unity registers agents at runtime
biomata-ws --config examples/host_owned/sim.yaml --port 8765

Step 3 (optional) — Enable LLM cognition

Install Ollama, pull a model, then switch any agent's brain class in the YAML config:

bash
# Pull a model (first time only)
ollama pull qwen2.5:14b

# Start Ollama (if not running as a service)
ollama serve

Then in your YAML config, change the brain class and add an llm top-level block:

yaml
llm:
  model: qwen2.5:14b
  base_url: http://localhost:11434
  temperature: 0.8

agents:
  - id: agent_001
    name: Guard-A
    brain:
      # From:
      # class: src.plugins.builtin.idle_brain.brain.IdleBrain

      # To:
      class: src.plugins.builtin.ollama.brain.OllamaLLMBrain
      personality:
        traits: [vigilant, loyal]
        goals: [protect the gate, report threats]

Python API

Run simulations directly from Python without the CLI:

python
import asyncio
from src.engine.simulation import Simulation

sim = Simulation.from_config("examples/engine_owned/sim.yaml")
asyncio.run(sim.run())

WebSocket server

The WebSocket server exposes a running simulation over JSON-over-WebSocket so Unity, Unreal, browsers, or test harnesses can connect and drive agent cognition in real time.

Default host

The server binds to 127.0.0.1 (loopback) by default. For LAN or remote access pass --host 0.0.0.0 explicitly.

Host-driven mode (default)

The client sends a tick request when ready. Use this when you need to synchronize cognition with your game's physics or render loop (Unity FixedUpdate).

bash
biomata-ws --config examples/engine_owned/sim.yaml --port 8765

Autonomous mode

The backend drives its own tick loop. Clients subscribe to events and use pause/resume to control the loop. Use for headless research or batch runs.

bash
biomata-ws --config examples/engine_owned/sim.yaml --port 8765 --mode autonomous

YAML configuration

Every simulation is described by a YAML config. The loader uses importlib to dynamically import the class at each class: path and instantiate it with the remaining fields as keyword arguments.

yaml
engine:
  ticks: 9999        # number of ticks to run
  seed: 42           # RNG seed — same seed = deterministic replay
  scheduler: simultaneous  # or "sequential"

world:
  class: src.plugins.external.world.HostedWorld

agents:
  - id: gate_guard_left
    name: Aldric
    capabilities: [patrol, authority]
    brain:
      class: src.plugins.builtin.idle_brain.brain.IdleBrain
FieldRequiredDescription
engine.ticksno (default 20)Total ticks for sim.run(). Ignored by run_tick().
engine.seedno (default 42)Seeds random.Random. Same seed → identical run.
engine.schedulernosimultaneous (default) or sequential.
engine.scheduler_ordernoExplicit agent_id list for sequential mode; defaults to agent declaration order.
world.classyesFully-qualified Python dotted path to a World implementation.
registry.classnoDotted path to a factory function returning an ActionRegistry. Defaults to an empty registry.
observations.classnoDotted path to a factory returning an ObservationRegistry.
llmnoTop-level LLM config dict (model, base_url, etc.) forwarded to brain constructors.
rolesnoNamed role definitions with capabilities and brain block. Agents reference roles via agents[].role.
agents[].rolenoReference to a declared role; inherits capabilities and brain config.
agents[].brain.classyes (unless role supplies one)Dotted path to a Brain implementation.
agents[].state_ext.classnoDotted path to an AgentStateExtension implementation.
agents[].capabilitiesnoCapability tags (additive union with any role capabilities).
agents[].metadatanoArbitrary dict attached to agent; not consumed by tick loop.

Game engine integration (host-authoritative)

For Unity or any other engine, the typical setup is:

  1. Start the Python server: biomata-ws --config your_sim.yaml --port 8765
  2. Connect the Unity SDK (see Unity SDK) or implement Protocol v1 directly (see Transport)
  3. Register agents via register_agent with a brain class
  4. Each game tick: send observations, call tick, read decisions and apply engine_commands

Minimal Python-side integration using HostedWorld:

python
import asyncio
from src.plugins.external.world import HostedWorld
from src.engine.simulation import Simulation
from src.engine.registry import ActionRegistry
from src.contracts.action import ActionSchema, ActionResult
from src.engine.agent import Agent
from src.plugins.builtin.idle_brain.brain import IdleBrain
from src.plugins.builtin.simple_memory.memory import SimpleMemory

class MoveHandler:
    def execute(self, agent, intent, context) -> ActionResult:
        direction = intent.parameters.get("direction", "north")
        return ActionResult(
            success=True,
            outcome_text=f"moving {direction}",
            engine_commands=[{"type": "navigate", "direction": direction}],
        )

world = HostedWorld()
registry = ActionRegistry()
registry.register(
    ActionSchema("move", "Move in a direction.", {"direction": "string"}),
    MoveHandler(),
)

agent = Agent(
    id="npc_001", name="Guard",
    brain=IdleBrain(action="move", parameters={"direction": "north"}),
    memory=SimpleMemory(),
)

sim = Simulation(agents=[agent], world=world, registry=registry)

async def game_loop():
    for _ in range(3):
        world.push_metadata({"time_of_day": "noon"})
        world.push_observation("npc_001", {"location": "gatehouse", "health": 100})
        summary = await sim.run_tick()
        commands = summary.engine_commands()
        print(f"Tick {summary.tick}: {commands}")

asyncio.run(game_loop())

Next steps

  • Docker Quickstart — run the full stack with docker compose up
  • Unity SDK — C# quickstart for Unity 6
  • Examples — engine_owned, host_owned
  • Core Engine — understand the tick loop, schedulers, and event bus
  • Contracts — implement your own World, Brain, or ActionHandler
  • Transport — WebSocket protocol reference for building your own client
  • Contributing — how to write and submit plugins