Part of epic #112.
Depends on: nothing — standalone change in arcane-demos.
Change
All changes in crates/arcane-demo/src/cluster_demo.rs (and exported from src/lib.rs):
1. Add group_id: Option<Uuid> to DemoAgent (None = ungrouped, identical to current behaviour). create_demo_agents continues to set group_id: None.
2. Add GroupState struct:
pub struct GroupState {
pub centroid_x: f64,
pub centroid_z: f64,
pub encounter_target: Option<(f64, f64)>,
pub encounter_ticks_remaining: u32,
}
3. Add EncounterTracker struct with new(), tick(), is_in_encounter(group_id), start_encounter(group_id, tick). Tracks per-group encounter cooldowns across ticks using a HashMap<Uuid, u32>.
4. Add create_grouped_agents(count, group_size, stress_radius) -> Vec<DemoAgent> — assigns consecutive agents to groups; group UUID is Uuid::from_u128(group_index as u128); group members spawn adjacent on the same circle as create_demo_agents.
5. Add compute_group_states(agents, tracker) -> HashMap<Uuid, GroupState> — computes centroid from positions of agents sharing a group_id; fills encounter fields from tracker.
6. Add tick_grouped_agents(agents, tick, group_states, tracker, stress_radius) — for agents with group_id = Some(...): blend direction 60% toward group centroid / 40% personal random; if in encounter, steer toward encounter target centroid. Agents with group_id = None behave identically to tick_demo_agents. Encounter triggers deterministically from group seed + tick.
Acceptance criteria
Spec
See §3, §4.1–4.6, §5.1 in demo_agent_group_behaviour.md.
Part of epic #112.
Depends on: nothing — standalone change in
arcane-demos.Change
All changes in
crates/arcane-demo/src/cluster_demo.rs(and exported fromsrc/lib.rs):1. Add
group_id: Option<Uuid>toDemoAgent(None = ungrouped, identical to current behaviour).create_demo_agentscontinues to setgroup_id: None.2. Add
GroupStatestruct:3. Add
EncounterTrackerstruct withnew(),tick(),is_in_encounter(group_id),start_encounter(group_id, tick). Tracks per-group encounter cooldowns across ticks using aHashMap<Uuid, u32>.4. Add
create_grouped_agents(count, group_size, stress_radius) -> Vec<DemoAgent>— assigns consecutive agents to groups; group UUID isUuid::from_u128(group_index as u128); group members spawn adjacent on the same circle ascreate_demo_agents.5. Add
compute_group_states(agents, tracker) -> HashMap<Uuid, GroupState>— computes centroid from positions of agents sharing agroup_id; fills encounter fields from tracker.6. Add
tick_grouped_agents(agents, tick, group_states, tracker, stress_radius)— for agents withgroup_id = Some(...): blend direction 60% toward group centroid / 40% personal random; if in encounter, steer toward encounter target centroid. Agents withgroup_id = Nonebehave identically totick_demo_agents. Encounter triggers deterministically from group seed + tick.Acceptance criteria
create_demo_agents+tick_demo_agentssignatures and behaviour are byte-for-byte identical to beforegroup_id: Noneticked viatick_grouped_agentsproduce same distribution astick_demo_agentstick_grouped_agentscargo test -p arcane-demopassescargo clippy -- -D warningspassesSpec
See §3, §4.1–4.6, §5.1 in
demo_agent_group_behaviour.md.