<!-- Auto-generated by claude-code. Edit the prompt, not this file. -->

# CoGames Simulation Guide

## 1. What is CoGames

CoGames is a framework for composable multi-agent cooperative and competitive games built on the MettaGrid simulation engine. It defines games through a layered hierarchy: **Game → Mission → Variant**. Games register missions, and missions compose variants that layer on mechanics (resources, territory, roles, etc.).

The primary game is **Cogs vs Clips (CvC)** — a team-based strategy game where cogs cooperate to gather resources, craft items, and control territory while competing against an autonomous adversary faction called "clips." Cogs must specialize into roles, manage limited energy and health, and expand a connected junction network to score points.

### Architecture

- **CoGame** — a registered game (e.g., `cogs_vs_clips`).
- **CoGameMission** — a specific scenario with map size, cog count, step limit, and a default variant. Missions define the *what*.
- **CoGameMissionVariant** — a composable modifier that adds or configures mechanics (damage, energy, roles, territory, etc.). Variants define the *how*. They declare dependencies on other variants and are resolved in topological order.

Built-in CvC missions:

| Mission | Map Size | Cogs | Steps | Notes |
|---------|----------|------|-------|-------|
| `machina_1` | 88×88 | 8 (up to 20) | 10,000 | Full game: clips, day/night, damage, gear |
| `arena` | 50×50 | 8 (up to 20) | 1,000 | Compact training map |
| `tutorial` | 35×35 | 4 | 1,000 | No clips, role-specific sub-missions |
| `four_score` | 120×120 | 32 (4 teams of up to 80) | 10,000 | Four-corner team layout, no clips |
| `empty` | 50×50 | 8 | 1,000 | Minimal baseline, no variants |

## 2. Game Objective and Scoring

The primary objective in Machina-1 is to **hold as many junctions as possible for as long as possible** through a connected network. Scoring is cumulative over time — sustained control matters more than peak count.

### Reward Function

Each cog receives a per-tick reward proportional to the number of junctions in its team's network:

```
reward_per_tick = (junctions_in_network - root_count) / max_steps
```

The `root_count` subtracts the hub itself (1 for cog teams, `clips_ship_count` for clips). Cumulative reward over a full episode equals the time-averaged number of held junctions. A team controlling 5 junctions for the entire episode earns a total reward of 5.0.

## 3. Roles and Abilities

Cogs start as generalists. By visiting a **Gear Station** at their team hub and spending resources, a cog acquires a role. Each cog can hold **one gear item** at a time. Gear is **destroyed on death**.

### Gear Costs (Machina-1)

Each role costs 6 total elements, weighted toward a primary element:

| Role | Carbon | Oxygen | Germanium | Silicon |
|------|--------|--------|-----------|---------|
| **Aligner** | 3 | 1 | 1 | 1 |
| **Scrambler** | 1 | 3 | 1 | 1 |
| **Miner** | 1 | 1 | 3 | 1 |
| **Scout** | 1 | 1 | 1 | 3 |

Costs are deducted from the **team hub's** shared inventory, not the cog's personal cargo.

### Role Details

| Role | Stat Modifiers | Ability |
|------|---------------|---------|
| **Miner** | +40 cargo capacity | Extracts 10× resources per use (vs 1× default) |
| **Aligner** | — | Can align neutral/unowned junctions to team control |
| **Scrambler** | +200 HP | Can scramble enemy-controlled junctions back to neutral |
| **Scout** | +400 HP, +100 energy | Extended survivability and mobility for exploration |

### Vibes (Role Signaling)

Cogs can broadcast a **vibe** — a visible emoji signal to communicate intent. The default CvC vibes:

| Vibe | Symbol | Purpose |
|------|--------|---------|
| `default` | 😐 | No signal |
| `heart` | ❤️ | Heart-related activity |
| `gear` | ⚙️ | Gear-related activity |
| `scrambler` | 🌀 | Scrambler role signal |
| `aligner` | 🔗 | Aligner role signal |
| `miner` | ⛏️ | Miner role signal |
| `scout` | 🔭 | Scout role signal |

The full vibe catalog contains 152 vibes across categories: emotion, resource, gear, station, identity, navigation, combat, numerals, hearts, and misc. CvC missions use the 7-role default unless a caller explicitly overrides the vibe list.

## 4. Resources and Economy

### Elements

Four base elements exist in the world, obtained from extractors scattered across the map:

- **Oxygen** — extracted from `oxygen_extractor`
- **Carbon** — extracted from `carbon_extractor`
- **Germanium** — extracted from `germanium_extractor`
- **Silicon** — extracted from `silicon_extractor`

Each extractor starts with **200 units** and is removed when depleted. Cogs interact with objects by **bumping** them — walking into an adjacent object to trigger its use action. Default extraction yields **1 unit** per bump; miners extract **10 units** per bump.

### Cargo

Cogs have a **cargo limit of 4** elements (miners: 44). This forces frequent trips back to deposit resources.

### Hearts

Hearts are crafted at the team hub by spending elements. In Machina-1, each heart costs **7 of every element** (28 total). Hearts are consumed to:

- **Align** a junction (1 heart) — converts it to team control
- **Scramble** a junction (1 heart) — removes enemy control

Cogs can carry up to **10 hearts**. Hearts are **destroyed on death**.

### Hub Economy

The team hub acts as a shared bank:

- Cogs **deposit** gathered elements into the hub (up to 100 per resource per deposit)
- Heart crafting draws from the hub's element stockpile
- Gear station purchases draw from the hub
- Hub starts with `num_cogs × 3` of each element and **5 hearts**
- Hub element capacity: **10,000** per resource

### Cooperation Loop

The core economic loop requires cooperation:

1. **Miners** gather elements efficiently (10× extraction, 44 cargo)
2. Elements are deposited at the hub (or at aligned junctions, which forward to the hub)
3. Hub resources fund **heart crafting** and **gear purchases**
4. **Aligners** spend hearts to claim junctions
5. **Scramblers** spend hearts to disrupt enemy junctions
6. **Scouts** explore and survive in hostile territory

## 5. Territory and Area-of-Effect

### Junction Network

Territory is defined by a **connected network of junctions** radiating from the team hub:

- **Junctions** are objects scattered across the map (Poisson distribution in Machina-1)
- An aligner can claim a junction within **15 tiles** of an existing junction, or **25 tiles** of the hub
- The network is computed via materialized closure query (BFS) through connected junctions
- Only junctions **connected back to the hub** count toward the team's network

### Territory Control

Each junction projects an **area of influence**:

- Junctions: **10-tile radius** (strength 10, decay 1)
- Hubs: **20-tile radius** (strength 20, decay 1)

At each tile, the tag with the highest aggregate influence strength wins. Influence = `max(0, strength - decay × distance)`.

### AOE Effects

Within friendly territory (per tick):
- **+100 energy** (effectively full restore)
- **+100 HP** (effectively full restore)

Within enemy territory (per tick):
- **-1 HP** to non-team cogs

These effects make territory strategically critical — friendly territory is a safe zone, while pushing into enemy territory slowly drains health.

## 6. Observations

Each cog observes the world as a sparse array of up to **300 tokens**, where each token is a 3-element tuple:

```
[packed_location, feature_id, value]
```

### Spatial Tokens

- The cog sees a **13×13 egocentric grid** centered on itself (cog at packed location `0x66`)
- `packed_location` encodes `(row, col)` with upper 4 bits = row, lower 4 bits = column
- Only non-zero features are emitted (sparse representation)
- Empty/padding tokens use marker value `0xFF` (255)

### Global Tokens

Non-spatial state is encoded with `packed_location = 0xFE` (254):

| Feature | Description |
|---------|-------------|
| `episode_completion_pct` | Fraction of episode elapsed |
| `last_action` | Action taken last step |
| `last_reward` | Reward received last step |
| `last_action_move` | Whether location changed (optional) |
| `local_position` (N/S/E/W) | Directional offset from spawn (optional) |

### Spatial Features

| Feature | Description |
|---------|-------------|
| `agent:group` | Team/group ID of observed entity |
| `vibe` | Current vibe of entity |
| `tag` | Tag value |
| `territory:here`, `territory:*` | Sparse territory observations; see `mettagrid/docs/territory.md` |

### Inventory Features

Each resource is encoded as multi-token values using base-256 encoding:
- `inv:{resource}` — low byte (`value % 256`)
- `inv:{resource}:p1` — high byte (`value / 256`)

### Hub Observations

When enabled (`hub_observations` variant), cogs can also observe their team hub's resource inventory as additional global tokens.

## 7. Actions

Actions are split into two streams: **primary actions** and **vibe actions**.

### Primary Actions

| Action | Description |
|--------|-------------|
| `noop` | Do nothing |
| `move_north` | Move one tile north |
| `move_south` | Move one tile south |
| `move_west` | Move one tile west |
| `move_east` | Move one tile east |

**5 primary actions.** Invalid actions result in a silent noop.

### Vibe Actions

Each vibe has a corresponding `change_vibe_{name}` action. With the default 7 CvC vibes, there are 7 vibe actions; with the full 152-vibe catalog (used in training), there are 152.

**Total: 12 actions** in the default configuration (5 primary + 7 vibes), or **157** with the full vibe catalog.

### Movement

- Each move costs **4 energy**
- Movement is cardinal only (no diagonals by default, though 8-directional movement is configurable)
- Moving onto an object triggers **use handlers** (extraction, deposition, gear acquisition, junction alignment, etc.)

### Energy and Health

- **Energy**: limit 20, starts at 100 (clamped to limit). Regenerated via solar each tick.
- **HP**: limit 100, starts at 50. Drains at **-1 per tick** passively. Restored in friendly territory.
- **Solar**: converts to energy each tick. Day = 3 solar, Night = 1 solar. Day/night cycle: 200 steps per full cycle (100 day, 100 night).

### Death

When HP reaches 0, the cog's **hearts** and **gear** are cleared. Death does not remove the cog from the game — it persists, loses its role and carried items, and can return to its hub to equip a new rig and restart.

## 8. Variants

Variants are composable modifiers that missions mix-and-match. Each variant declares dependencies on other variants (required or optional), and the system resolves them in topological order. Variants can modify the environment config during two phases: `configure()` (cross-variant parameter wiring) and `modify_env()` (final environment mutation).

The default game (**machina_1**) is composed of these variants: Vibes, Team, HubObservations, Territory, Elements, Heart, TeamJunction, JunctionDeposit, Damage, Energy, CargoLimit, Extractors, Gear, Aligner, Scrambler, Miner, Scout, Clips, Days, TeamGearStations, DamageStrangers, HealTeam.

### Clips Behavior

**clips** — Adds the clips faction: a non-player team that spreads across junctions via timed events. Four clips ships are placed at map corners and act as root nodes for the clips network. Initial clips placement starts at step 10. Starting at step 100, clips periodically align neutral junctions (every 200 steps) and scramble cog-owned junctions (every 200 steps) within a 15-tile radius of their connected frontier. By default, clips expand greedily from each ship, targeting the nearest valid junction within a 120-tile search radius.

**greedy_clips** — Explicitly forces greedy expansion from each ship, always targeting the nearest valid junction. One junction per event, per ship lane. `scramble_radius=15`.

**adaptive_clips** — Enables dominance-aware spreading. When cogs hold at least 2× as many junctions as clips, clips enter burst mode and claim up to 3 junctions per lane per event. When neither side is dominant, clips spread at a balanced single-target rate. Disables greedy and angry modes.

**angry_clips** — Clips blitz toward the enemy hub. Spreading starts later (step 400) but runs at double frequency (every 100 steps). Junctions closest to the enemy hub are prioritized rather than junctions closest to the ship. Disables greedy expansion.

**no_clips** — Sets the clips ship count to zero, completely disabling clips spreading.

### Roles

**aligner_role** — Registers the aligner gear item. When a cog equips aligner gear and steps onto a neutral junction, it spends 1 heart to convert that junction to team control. Only works within 15 tiles of an existing junction or 25 tiles of the hub.

**miner_role** — Registers the miner gear item. Miners extract 10 units per use instead of 1 and receive +40 cargo capacity (total 44), allowing them to gather resources far more efficiently.

**scout_role** — Registers the scout gear item. Scouts receive +400 HP and +100 energy, giving them the survivability and mobility to explore deep into enemy territory without dying.

**scrambler_role** — Registers the scrambler gear item. Scramblers receive +200 HP and can scramble enemy-controlled junctions back to neutral for 1 heart, disrupting the opponent's network.

### Resources and Economy

**elements** — Adds the four base resources: oxygen, carbon, germanium, and silicon.

**energy** — Gives cogs an energy pool (limit 20, initial 100) consumed by movement (4 per move). Without energy, cogs cannot move.

**solar** — Adds a solar resource that converts to energy each tick. Initial solar is 1 per cog. Requires the energy variant.

**cargo_limit** — Caps how many element resources a cog can carry at once (default 4). Miners get +40 via modifier.

**extractors** — Places element extractors on the map at 0.3 density. Each extractor holds 200 units of a single element and is removed when depleted. Also sets the hub compound to include corner extractors.

**heart** — Adds craftable heart items used for junction alignment and scrambling. Limit 10 per cog, destroyed on death. Cost set by the mission (7 per element in Machina-1).

**gear** — Manages the gear slot system. Cogs can hold 1 gear item at a time. Gear is destroyed on death. Costs are paid from the team hub's inventory.

### Territory

**territory** — Adds the territory system. Team-tagged objects (hubs, junctions, ships) project influence in a radius. Junctions project 10-tile influence; hubs project 20-tile influence. Materialized closure queries track which junctions form each team's connected network.

**heal_team** — Cogs inside friendly territory receive +100 energy and +100 HP per tick, effectively restoring them to full. Only applies to cogs sharing the territory's team tag.

**damage_strangers** — Non-team cogs inside territory take -1 HP per tick. Combined with passive HP drain (-1/tick from the damage variant), cogs in enemy territory lose 2 HP per tick total.

### Terrain Biomes

**desert** — Sets biome weights to desert only (caves, forest, and city weights set to 0). Produces open terrain with sparse features.

**forest** — Sets biome weights emphasizing dense forest terrain with heavy obstacle coverage.

**city** — Produces grid-like urban structures with corridors and rooms. Uses a single large biome zone covering 95% of the map.

**caves** — Generates enclosed, winding cave paths.

Biome variants configure the `MachinaArena` scene builder's `biome_weights` parameter. The default includes forest, desert, city, and plains (caves weight is 0 by default).

**distant_resources** — Adjusts building placement patterns to push extractors farther from the hub. Uses bimodal distribution with 0.01 coverage.

**quadrant_buildings** — Concentrates each element's extractors in one map quadrant using normal distribution (std=0.18).

**machina_terrain** — Sets map width/height (default 88×88) and compound placements. Used by four_score to configure 120×120 maps.

### Gear Stations

**team_gear_stations** — Creates per-team gear stations at the hub compound. Each role has a station (symbols: 🔗 aligner, 🌀 scrambler, ⛏️ miner, 🔭 scout). Stepping onto a station acquires or changes gear, with costs charged to the team hub.

**wild_gear_stations** — Alternative placement that scatters gear stations as buildings across the map at 0.1 density rather than concentrating them at the hub.

**gear_stations** — Universal (non-team) gear stations scattered on the map; cost charged directly from the cog rather than the team hub.

### Vibes

**vibes** — Adds the `change_vibe` action, letting cogs broadcast an emoji signal. The default set includes 7 core vibes (default, heart, gear, scrambler, aligner, miner, scout). The full catalog supports 152 vibes across categories (emotion, resource, navigation, combat, numerals, hearts, misc).

**forced_role_vibes** — Assigns per-cog roles by cycling through miner/aligner/scrambler/scout per team. Automatically sets the cog's vibe to match their assigned role. Adds `role_id` as an inventory resource and global observation.

**talk** — Enables a speech-bubble talk action (`max_length=140`, `cooldown_steps=50`). Disables `change_vibe` when active. Mutually exclusive with vibes in practice.

### Day/Night Cycle

**days** — Oscillates solar regeneration between day (3 solar → 3 energy/tick) and night (1 solar → 1 energy/tick). The cycle period is 200 steps: the first 100 steps are daytime, the next 100 are night. During day, cogs can move more frequently; at night, energy is scarce.

### Multi-Team Support

**multi_team** — Splits the map into separate team instances with independent hub compounds. Supports 2 teams by default (cogs_green, cogs_blue) placed side-by-side.

**four_score** — Extends multi-team to support 4 teams (cogs_red, cogs_blue, cogs_green, cogs_yellow) placed in corner compounds at NW, NE, SW, and SE positions. Map size is set to 120×120 with 1.5× building coverage. Requires no_clips. Each corner compound includes a hub, extractors, gear stations, and a nearby junction.

### Other Variants

**damage** — Adds HP resource with passive drain of -1 per tick. HP limit is 100, starting at 50. When HP reaches 0, specified items (hearts, gear) are cleared.

**hub_observations** — Cogs can observe their team hub's element inventory as additional global observation tokens.

**junction_deposit** — Allows cogs to deposit elements at aligned junctions. Deposits are forwarded to the matching team's hub (up to 100 per resource per deposit).

**buildings** — Controls which building types appear on the map and their relative weights/densities.

**randomize_spawns** — Randomizes cog spawn positions within the hub compound.

**small_50** — Resizes the map to 50×50.

**empty_base** — Removes all four extractors from hub corners.

**overrun** — All junctions start clips-aligned. Disables clips events. Used by the scrambler tutorial sub-mission.

**junction** — Adds bare junction objects to the map at 0.3 building density.

## 9. Missions

### Mission System

The mission system follows a **Game → Mission → Variant** composition hierarchy:

- A **Game** (e.g., `cogs_vs_clips`) registers a set of missions and a variant catalog.
- A **Mission** defines the scenario: map size, cog count, step limit, and a default variant name.
- A mission's **default variant** pulls in a tree of variant dependencies that configures the full game mechanics.
- Additional variants can be layered on via CLI (`-v`) or programmatic API (`.with_variants()`).

### Machina-1 (aka CogsGuard)

The flagship mission. Default variant `machina_1` pulls in: vibes, team, hub_observations, territory, elements, heart, team_junction, junction_deposit, damage, energy, cargo_limit, extractors, gear, aligner_role, scrambler_role, miner_role, scout_role, clips, days, team_gear_stations, damage_strangers, and heal_team.

This produces a full game with resource gathering, heart crafting, gear acquisition, junction network expansion, clips pressure, day/night energy cycles, and territory-based healing/damage. The map is 88×88 with an asteroid mask, Poisson-distributed junctions, and a central hub compound.

**Sub-mission**: `clips` — runs the machina_1 scenario from the clips perspective.

### Four Score

A multi-team variant of machina_1 gameplay. Uses the `machina_1` default variant plus `four_score`, which adds `no_clips` and `machina_terrain`. Four teams (red, blue, green, yellow) each get a corner compound on a 120×120 map. Each compound includes a team hub, extractors, gear stations, and a nearby junction. With 32 cogs (8 per team), this mission tests multi-team competition without clips.

### Tutorial

A small 35×35 map with 4 cogs and no clips, designed for learning individual roles. Uses the `tutorial` default variant, which includes most machina_1 mechanics but without clips or damage_strangers.

**Sub-missions** provide role-specific reward shaping:
- `aligner` — rewards for collecting hearts and aligning junctions
- `miner` — rewards for resource extraction and deposits
- `scout` — rewards for exploration and visiting stale cells
- `scrambler` — requires the `overrun` variant (all junctions start clips-aligned), rewards for scrambling enemy junctions

The tutorial hub is a larger 15×15 compound with corner extractors and all four gear stations.

### Arena

A compact 50×50 training map with 8 cogs (up to 20) and 1,000 steps. Uses the `machina_1` default variant, producing a smaller version of the full game suitable for faster training iteration.

### Empty

A 50×50 map with 8 cogs, 1,000 steps, and no default variant. Produces a minimal baseline with only movement and noop actions — useful as a starting point for custom variant composition.

## 10. Map Generation

Maps are procedurally generated using a layered scene composition system.

### Composition Order

1. **Base biome** — sets the terrain foundation (`plains`, `forest`, `desert`, `city`, `caves`)
2. **Biome overlay zones** — BSP-partitioned areas filled with randomly selected biome scenes (weighted by `biome_weights`)
3. **Dungeon overlay zones** — BSP-partitioned areas filled with maze (DFS 60% / Kruskal 40%) or radial (8-arm) structures
4. **Asteroid mask** — auto-enabled for maps ≥ 80 tiles wide/tall, creating an irregular organic boundary
5. **Building/resource placement** — extractors and junctions distributed with configurable coverage density and distribution type
6. **Hub compound** — team hub with optional corner extractors, cog spawns, and gear stations
7. **Connectivity pass** — `MakeConnected` ensures all areas are reachable
8. **Perimeter/corner placements** — objects placed at map edges or corners (e.g., clips ships)
9. **Hub-junction guarantee** — `EnsureHubReachableJunction` places a junction within 4–15 tiles of each hub if none exists

### Biomes

| Biome | Character |
|-------|-----------|
| `plains` | Open terrain, default base biome |
| `forest` | Dense obstacles |
| `desert` | Open with sparse features |
| `city` | Grid-like structures |
| `caves` | Enclosed, winding paths (weight 0 by default) |

### Default Biome and Dungeon Weights

Biomes: forest 1.0, desert 1.0, city 1.0, plains 1.0, caves 0.0.
Dungeons: maze 1.0 (DFS 60%, Kruskal 40%), radial 1.0.

Zone counts auto-scale based on map area, with biome zones capped at 27% of map dimension and dungeon zones at 20%.

### Hub Compound

Each team's hub compound is configured via `CompoundConfig`:
- `hub_object` — the hub station (e.g., `c:hub`)
- `corner_bundle` — `"extractors"` places one extractor per element at corners, `"none"` skips
- `stations` — gear stations placed around the hub
- `spawn_count` — cog spawn points within the compound
- `cross_distance` — spacing for cross-pattern objects (junctions, etc.)

### Default Map (Machina-1)

- **88×88 tiles**, up to 20 spawn points
- Automatic asteroid mask (irregular boundary)
- Poisson-distributed junctions, 0.3 density extractors per element
- Central hub compound with corner extractors and 4 gear stations (one per role)
- Connectivity pass ensures all regions are walkable

## 11. Running a Game

### CLI

```bash
# Install
pip install cogames

# List available missions and variants
cogames missions
cogames variants
cogames variants --dependencies

# Interactive play (GUI mode)
cogames play -m machina_1

# With a random policy
cogames play -m machina_1 -p class=random

# Baseline policy with 4 cogs
cogames play -m machina_1 -c 4 -p class=baseline

# Terminal (unicode) mode
cogames play -m machina_1 -r unicode

# Specify steps
cogames play -m machina_1 -s 2000

# Save a replay
cogames play -m machina_1 --save-replay-file ./latest.json.z

# Apply variant modifiers
cogames play -m machina_1 -v city -v no_clips

# Tutorial
cogames tutorial play
```

### Key Options

| Flag | Description |
|------|-------------|
| `-m`, `--mission` | Mission to play (e.g., `machina_1`, `arena`, `tutorial`) |
| `-v`, `--variant` | Apply a variant modifier (repeatable) |
| `-c`, `--cogs` | Number of cogs |
| `-p`, `--policy` | Policy controlling cogs (`noop`, `random`, `baseline`, or path) |
| `-r`, `--render` | Render mode: `auto`, `gui`, `unicode`, `log`, `none` |
| `-s`, `--steps` | Max steps per episode |
| `--game` | Game to play (default: `cogs_vs_clips`) |
| `--seed` | RNG seed for reproducibility (default: 42) |
| `--map-seed` | Separate seed for map generation |
| `--autostart` | Start simulation immediately without waiting |
| `--save-replay-dir` | Save replay to directory with UUID filename |
| `--save-replay-file` | Save replay to fixed file path |

### Mission Name Syntax

- `machina_1` — the base mission with its default variant
- `machina_1.clips` — machina_1 with clips sub-mission applied
- `tutorial.aligner` — tutorial with aligner sub-mission
- Can also be a file path (`.yaml`, `.yml`, `.json`, `.py`)

### Programmatic Use

```python
from cogsguard.missions.machina_1 import make_machina1_mission

# Create a mission
mission = make_machina1_mission(num_agents=8, max_steps=10000)

# Apply variants
mission = mission.with_variants(["city", "no_clips"])

# Get the MettaGrid environment config
env_cfg = mission.make_env()
```

### Interactive Controls

In GUI mode, click on a cog to manually control it. In unicode mode, press **M** to enter manual control, then use **arrow keys** or **WASD** to move.
