# Brain execution for swarm populations

A `defineSwarm` population can run the same Brain definitions as actors. Each
member owns execution state; one population owner supplies the immutable program,
sensing, target positions, scheduling and movement. Brain assets and code-authored
Brains use identical task and transition semantics.

Swarm rows are not actors. They have no actor messages, attached components or
individual actor inspector. Use a World-owned component or subsystem to bridge
those services, then operate on the population's columns in bulk. The Example's
`src/ai_crowd.zig` demonstrates this with an embedded `crowd_ai` owner and a separate
`ai_crowd_member` swarm. No actor is created for each crowd member.

## Program and execution storage

`hi.ai.population.Program.bind(definition)` validates and copies a definition
into population-owned value storage. When loading an asset, call it once with the
callback-scoped result of `world.brainProgram(asset)`. Keep the copy, never the
borrowed pointer. The owner must belong to one World; destroying it releases that
population's state. A new Play loads the newly cooked asset.

`hi.ai.brain.CompactInstance` uses the same executor as `Instance`, retaining the
machine and latest decision record, without an override array or history ring.
On the tested macOS arm64 build it occupies **48 bytes**, versus **560 bytes** for
`Instance`. Position, targets, observations and routes are additional storage.

```zig
const hi = @import("hikari_game");

pub const Crowd = hi.defineSwarm(.{
    .name = "crowd",
    .capacity = 1000,
    .data = struct {
        x: f32 = 0,
        y: f32 = 0,
        z: f32 = 0,
        execution: hi.ai.brain.CompactInstance = .{},
        search_seconds: ?f32 = null,
    },
});
```

The owner starts each new row with `execution.start(definition)` before spawning
it. During simulation, call `decide(definition, facts)` and
`stepWithParameters(definition, position, targets, dt, overrides)`. The override
slice is indexed by the definition's parameter slots: null or an absent slot
inherits the shared default. Resolve named slots when binding the definition.
This lets populations keep overrides in separate fields instead of reserving all
16 parameters in every execution instance.

`Runtime(parameter_capacity, history_capacity)` provides intermediate storage
choices. A local override can only address a slot below `parameter_capacity`.
`Instance` remains `Runtime(16, 16)`; `CompactInstance` is `Runtime(0, 0)`.
All definitions keep the existing Brain capacities and supported task set.

## Scheduling, sensing and events

- `population.Decisions` performs round-robin admission over `liveIndices()` and
  a due-time column. `begin(dt, limit)` advances simulation time and resets the
  work budget. `next(live, due)` admits at most that limit, scanning at most the
  live population once. The caller sets each admitted member's next due time.
  Zero, negative or non-finite dt admits no work.
- Keep the previous service time per member and pass accumulated simulation time
  to task execution. If a condition enters a new state now, do not charge that
  new state's timer for time before its entry. The sample starts its next task
  step at the following admission. Movement follows cached routes every frame.
- `population.Subject` distinguishes actor identities from swarm identities.
  Swarm identity includes population ID, slot and generation. It is World-local;
  do not move these identities or service state between Worlds.
- `scheduler.Scheduler(population.Subject).Service(...)` supplies the existing
  spatial candidate discovery, bounded observations, fairness and query budgets
  for mixed actor/swarm identities. `scheduler.Service(...)` remains the actor
  specialization. The game supplies snapshots and publishes results into columns.
- `Vision.querySegment` allows a swarm observer with no actor collider to cast an
  occlusion segment. Pass an invalid actor as the exclusion. Subject identities
  do not create physics bodies; adapters still choose how to resolve any target
  collider. The sample uses swarm observers and ordinary actor targets.
- `population.Stimuli(capacity)` receives stimuli once through the owner's
  `onMessage`. Each admitted member checks the bounded inbox using distance and
  category filtering. Its sequence cursor prevents duplicate delivery. The
  newest audible event wins; old events expire, overflow replaces the oldest,
  and `overwritten` exposes inbox pressure. Start new members at the inbox's
  current sequence so recycled slots do not inherit earlier noises.

Budgets bound admitted work, not CPU milliseconds. Snapshot gathering, sorting,
memory maintenance, rendering and motor integration still cost work proportional
to population size. Admission is currently synchronous; it does not dispatch jobs.
The sample reserves separate budgets for its actor and swarm sensing services.

## Playable test

Open **Tutorials → AI Swarm Population** (`scenes/ai_population.json`) and start Play.
The dedicated scene starts with **100** members. Choose **1000** to scale up or
**0** to retire the population. It contains no stationary load guards or door encounter. Creation
and removal are limited to 32 members per simulation tick.

All members share `assets/brains/crowd.brain.json`:

- Blue members patrol; hearing noise turns them amber while investigating.
- Purple members search for the shared `search_seconds` duration.
- Detecting a target takes priority over noise and turns members red as they flee.
- Members have 6 m or 11 m hearing and different speeds. All initially inherit
  the Brain's search duration; the data model also supports individual overrides.

Use **Noise** to place a stimulus or **Move player** to move a visible target.
Use **Inspect**, then click near a member to select it. The panel displays
its slot/generation, state, transition reason, hearing, speed, selected target and
bounded decision history. The yellow outline marks the selection. History starts
with the latest record at selection and records subsequent decisions; selecting
another generation clears it. **Retire selected / respawn** replaces that member
with freshly initialized data while maintaining the requested population size.

The owner actor also exposes those diagnostics through the ordinary live actor
inspector. This is an explicit population inspection adapter; there is no general
editor hierarchy or inspector for arbitrary swarm rows yet.

The panel reports state counts, decisions, rays, route plans, CPU time, decision
lateness, observation age, inbox overwrites and reserved data bytes. Reserved data
counts the owner's runtime and member field payloads at capacity, excluding swarm
bookkeeping, GPU resources and the actor's general overhead.

Change the Brain's search-duration default, let it cook, then start a new Play to
apply it to the whole population. Pause freezes decisions and movement. Scene
unload or owner disable retires members and clears the visual binding.

The fixture admits at most **128 decisions**, **128 perception steps**, **32
sight rays** and **8 route plans** per tick. It uses the existing flat-ground
planner and shared scene obstacle snapshot. Members route around static walls;
**member-to-member avoidance, crowd packing, navmeshes and animation are not
implemented by this sample**. Rendered members are small instanced markers.

## Headless verification

From `src/hikari`:

```sh
zig build test -Dtest-filter=population -Dtest-filter=brain -Dtest-filter=scheduler -Dtest-filter=vision -Dgame-src=<absolute example/src>
zig build ai-population-bench -Dgame-src=<absolute example/src> -Doptimize=fast
```

See the [measured benchmark results](/docs/reports/ai-population-benchmark.md).

The benchmark runs 100 and 1,000 moving members sharing a code-authored Brain,
with parameter overrides, a noise event, actor-target perception and selected
member tracing. It checks admission limits and delivery to every member, and
reports mean/p95 CPU time, worst decision lateness, noise delivery delay,
observation age and storage sizes. Analytic visibility replaces physics;
these numbers exclude the playable adapter, navigation, rendering and GPU work.
The game-package test separately drives 1,000 real swarm rows through the sample's
Brain/movement loop and checks route budgets and retirement/recycling.

See [Brain assets](/docs/guides/brain-assets.md), [component storage](/docs/guides/component-storage.md),
and [Game AI](/docs/guides/game-ai.md) for the underlying contracts.

## Custom gameplay tasks

Populations can implement [gameplay tasks](/docs/guides/ai-tasks.md) with a borrowed member adapter and `Registry.Driver(0)`. Share the bound table, issue an owner for each member lifetime using `Subject.swarm`, and cancel before retirement. Capability masks reject actor-only actions during binding. The existing moving crowd fixture keeps its movement/wait behavior and compact execution storage.
