Spaces:
Running
Running
Yiyu Tian
tech-production-planning pack: canonical Family 2 (Sequential Dependency Planning)
9b651dc | # OpenRA-Bench — Scenario Brainstorm & Economy Family Spec | |
| Design document. No engine or existing-pack changes. Two runnable | |
| economy packs were drafted and validate OK (see end). | |
| All win conditions below use ONLY leaves/composites that exist in | |
| `win_conditions.py`: `explored_pct_gte, enemies_discovered_gte, | |
| buildings_discovered_gte, units_killed_gte, units_lost_lte, | |
| within_ticks, after_ticks, reach_region, all_units_in_region, | |
| own_units_gte, cash_gte, harvesters_gte, power_surplus_gte, | |
| has_building, buildings_owned_gte, building_total_gte, | |
| building_count_gte{type,n}, building_in_region{type?,x,y,radius,count}`; | |
| composites `all_of / any_of / not`. | |
| --- | |
| ## Verified ground-truth constraints (this drove the design) | |
| 1. **No schema-valid ore source.** The Rust engine seeds an ore patch | |
| in a ~4-cell radius around any actor of type `mine` | |
| (`openra-sim/src/world.rs` ~L4014, `map_actor.actor_type == "mine"` | |
| at ~L3908). **But** the Python `ScenarioDefinition` actor whitelist | |
| (`openra_rl_training.scenario.VALID_ACTOR_TYPES`) has **no `mine` / | |
| `gmine`**, so a pack that spawns one fails validation. The 5 | |
| `.oramap` files (`map.yaml` inspected) embed **no resource tiles** | |
| either — only `mpspawn` actors. | |
| ⇒ **Harvest income is 0 in any runnable pack today.** `harv` + `proc` | |
| spawn fine but find no ore. `cash_gte` is only satisfiable from | |
| `starting_cash`, and `harvesters_gte` only counts the auto-spawned | |
| harvester (it never earns). | |
| 2. **`silo` is schema-valid but inert.** `silo` is in | |
| `VALID_ACTOR_TYPES`, but `PlayerResources.resource_capacity` is | |
| hardcoded `0` (`openra-sim/src/traits/mod.rs:76,172`, | |
| `world.rs:3609`) and the deposit path converts ore straight to cash | |
| ignoring capacity (`world.rs:1467-1473`). Building silos has **no | |
| economic effect**. | |
| 3. **`starting_cash` works** (per-level / pack-wide, schema confirmed), | |
| build/train with tech-tree prerequisites works, `proc` auto-spawns | |
| one `harv` on creation (`world.rs:1154`). | |
| 4. Only `rush-hour-arena` is Rust-loadable today (CONTRIBUTING §5); | |
| the four `singles-*` maps are schema-valid but Phase-3-gated. | |
| **Design consequence:** runnable economy scenarios test the | |
| **spend / allocation** decision over a fixed budget (the part the lead | |
| identified as the real test). Harvest-throughput and silo-storage | |
| variants are fully specced but tagged engine-prereq. | |
| --- | |
| # 1. Requested family — Economy-Time-Box (RUNNABLE TODAY) | |
| **Pack:** `packs/economy-time-box.yaml` — validates **OK**. | |
| **Capability:** reasoning. | |
| **Decision:** within a fixed tick budget, convert a fixed cash budget | |
| into BOTH deployed units AND a standing economy; idle cash and | |
| all-in-on-units both fail. | |
| real_world_meaning — Time-bounded capital deployment: a finite, | |
| non-replenishing budget must become mission capability *and* persistent | |
| infrastructure before a hard deadline. | |
| robotics_analogue — A field robot on a finite energy budget scheduling | |
| expenditure between mission work and standing up persistent | |
| infrastructure before a deadline. | |
| | Level | starting_cash | win_condition (real grammar) | scaling axis | | |
| |---|---|---|---| | |
| | easy | 4000 | `all_of[own_units_gte:4, building_total_gte:4, within_ticks:22000]` | loose clock & budget | | |
| | medium | 2400 | `all_of[own_units_gte:5, has_building:tent, building_total_gte:4, within_ticks:16000]` | adds tech-dep building, tighter | | |
| | hard | 1500 | `all_of[own_units_gte:6, building_total_gte:5, units_lost_lte:0, within_ticks:12000]` | lean budget, short clock, attrition cap | | |
| Difficulty comes from the decision getting harder (less budget slack, | |
| a tech-dependency constraint, an attrition cap + short clock), not | |
| bigger target numbers. Predicates: `own_units_gte`, `building_total_gte`, | |
| `has_building`, `units_lost_lte`, `within_ticks`. Commands: `build`, | |
| `place_building`, `move_units`, `deploy`, `stop`. Map: `rush-hour-arena`. | |
| **Engine status: runnable now.** | |
| ### Follow-up (leaderboard scoring, not a predicate today) | |
| A *final-economy-value* score = `cash + Σ asset_value(owned units & | |
| buildings)` at the deadline would reward "spent it well" beyond the | |
| binary `cash_gte`. No such predicate exists; propose it as a scoring / | |
| leaderboard metric computed from the render state at episode end (sum | |
| buildable costs of `own_buildings` + `units_summary`, add `signals.cash`). | |
| Do **not** add it as a win leaf — pass/fail stays in the grammar above. | |
| ### Harvest-income variant — **needs S0 + S1** (see §Engine-prereqs) | |
| Same shape but income comes from harvesting and `cash_gte` rises over | |
| time: `all_of[cash_gte:N, own_units_gte:K, harvesters_gte:1, | |
| within_ticks:T]`. Requires S0 (schema-valid ore source so harvesting | |
| actually earns) — and S1 only if silo storage is part of the test. | |
| --- | |
| # 2. Requested family — Economy-Investment (allocation decision) | |
| **Pack:** `packs/economy-investment.yaml` — validates **OK** (runnable | |
| variant). | |
| real_world_meaning — Capital allocation under one indivisible budget: | |
| the same cash buys either a *wide* economy (more depot/throughput | |
| capacity) or a *deep* one (more collectors/force); the budget covers | |
| exactly one coherent path, so splitting builds neither. | |
| robotics_analogue — Fleet capital allocation: a fixed budget buying | |
| either added depot/processing capacity or added collector agents — the | |
| throughput-vs-collection trade, where indecision yields neither. | |
| ### (a) Runnable-today variant — RUNNABLE NOW | |
| Income is the fixed `starting_cash`; the allocation is verified by | |
| build predicates (no silo, no harvest dependency). | |
| | Level | starting_cash | win_condition (real grammar) | path tested | | |
| |---|---|---|---| | |
| | easy | 4500 | `all_of[building_count_gte{proc,2}, building_count_gte{powr,2}, within_ticks:22000]` | wide, loose | | |
| | medium | 3200 | `all_of[building_count_gte{proc,2}, building_count_gte{powr,2}, own_units_gte:3, within_ticks:16000]` | wide + utilisation, tighter | | |
| | hard | 2600 | `all_of[own_units_gte:6, building_count_gte{proc,1}, units_lost_lte:0, within_ticks:14000]` | deep (force) under lean budget + attrition | | |
| Difficulty escalates by removing budget slack so the allocation must be | |
| committed (a split budget clears no bar), tightening the clock, and | |
| adding an attrition cap. Predicates: `building_count_gte{type,n}`, | |
| `own_units_gte`, `units_lost_lte`, `within_ticks`. Commands as above. | |
| Map `rush-hour-arena`. | |
| ### (b) Full version — **ENGINE-PREREQ (needs S0 + S1)** | |
| The faithful test the lead asked for: enough cash for **two refineries | |
| OR one refinery + extra harvesters**, scored by **harvested cash at the | |
| deadline**, plus **silos that raise the harvest cap**. | |
| - Levels: easy = budget covers either path comfortably (commit to one); | |
| medium = budget covers exactly one (the bottleneck differs: 2×proc = | |
| unload throughput, 1×proc+harv = gather rate); hard = lean budget + | |
| short clock + silo decision (must build `silo` to avoid capping the | |
| resource pool before a return trip). | |
| - Win grammar (valid leaves, *meaningful only after S0/S1*): | |
| `all_of[cash_gte:N, harvesters_gte:H, within_ticks:T]` for the | |
| allocation; the silo sub-goal is observable as | |
| `building_count_gte{type:silo,n:K}` but only *matters* after S1. | |
| - **Engine status: needs S0 (income) + S1 (storage).** Without them the | |
| two allocations are economically identical (income 0) so the | |
| scenario is not discriminating — hence variant (a) ships instead. | |
| --- | |
| ## Engine-prerequisite specs | |
| ### S0 — schema-valid ore source (smaller, unblocks all harvest income) | |
| Today the only ore mechanism is `actor_type == "mine"`, which the | |
| Python schema rejects. Two non-engine-code options the project could | |
| take (documented here, not implemented): | |
| - Add `mine`/`gmine` to `VALID_ACTOR_TYPES` in | |
| `openra_rl_training/scenario.py` (one list entry; the Rust seed loop | |
| at `world.rs:4014` already handles it), **or** | |
| - Add a `resource_fields: [{x,y,radius,density}]` field to | |
| `ScenarioDefinition` plumbed to `terrain.set_resource(...)` | |
| (`openra-sim/src/terrain.rs:193`) at world init. | |
| Either makes harvest income real and unblocks the §1 harvest variant | |
| and §2(b) allocation scoring (without silos). | |
| ### S1 — silo / resource-storage modeling (the part to flag) | |
| Goal: harvested resource enters a **capped pool**; cash is realised | |
| from the pool gradually (or on cap pressure), and `silo` buildings | |
| raise the cap. Precise change (Rust): | |
| 1. **`openra-sim/src/traits/mod.rs`** — `TraitState::PlayerResources { | |
| cash, resources, resource_capacity }` already has the fields; stop | |
| hardcoding `resource_capacity: 0` at the two constructor sites | |
| (`mod.rs:172`, `world.rs:3609`). Give players a small base capacity | |
| (e.g. from `fact`/`proc`). | |
| 2. **`openra-sim/src/world.rs` deposit path (~L1467-1473, `Unloading` | |
| arm of `tick_harvesters`)** — instead of `player.set_cash(current + | |
| value)`, add to `PlayerResources.resources`, clamped to | |
| `resource_capacity`; overflow is lost (matches OpenRA). Add a | |
| per-tick drain that converts `resources → cash` at a fixed rate | |
| (the realisation of stored ore). | |
| 3. **Silo capacity contribution** — when an actor of type `silo` | |
| (and `proc`, in real OpenRA) becomes owned/active, add its | |
| `Storage` amount to the owning player's `resource_capacity`; | |
| subtract on loss. Implement as a recompute over owned buildings in | |
| the building add/remove path (mirror how `PowerManager` | |
| provided/drained is recomputed), reading a per-building storage | |
| constant (silo ≈ 3000, proc ≈ 2000 in RA defaults). | |
| 4. **(optional) win/score** — no `resources_gte` predicate exists; | |
| either add one to `_PREDICATES` in `win_conditions.py` (one entry, | |
| pure: `c.signals.resources >= int(v)`) and expose `resources` on | |
| `EpisodeSignals`, or keep pass/fail on `cash_gte` and surface | |
| stored-value only in the leaderboard score. | |
| With S1, building silos genuinely changes optimal play (don't cap; | |
| don't over-invest in storage you can't fill), making §2(b) | |
| discriminating. | |
| --- | |
| ## 3. Broader brainstorm — 13 scenario ideas across capabilities | |
| Each: capability · real_world_meaning / robotics_analogue · win | |
| (real grammar) · predicates+commands · map · engine status. | |
| None duplicate the existing 13 packs. | |
| ### Perception | |
| **P1 — Resource-Patch Discovery Under Fog** | |
| - perception. RWM: locating *where* to forage before committing | |
| collectors is the real foraging problem, not the path to a known | |
| patch. RA: SLAM-time frontier selection for a resource map. | |
| - win: `all_of[explored_pct_gte:55, within_ticks:9000]` | |
| - uses `explored_pct_gte`,`within_ticks`; `move_units`,`observe`,`stop`. | |
| Map `rush-hour-arena`. | |
| - engine status: runnable now. | |
| **P2 — Count-the-Threats Before Acting** | |
| - perception. RWM: estimating force size from partial sightings before | |
| engaging is a recon-quality problem distinct from combat. RA: | |
| multi-target detection/enumeration under occlusion. | |
| - win: `all_of[enemies_discovered_gte:N, units_lost_lte:0, | |
| within_ticks:T]` (levels raise N, add decoy idle units, tighten T). | |
| - uses `enemies_discovered_gte`,`units_lost_lte`,`within_ticks`; | |
| `move_units`,`observe`. Map `rush-hour-arena`. | |
| - engine status: runnable now. | |
| **P3 — Identify the Production Building (signature ID)** | |
| - perception. RWM: classifying an installation by observable signature | |
| before acting on it. RA: semantic recognition of a target structure | |
| from sensor returns. | |
| - win: `all_of[buildings_discovered_gte:1, within_ticks:T]`; hard adds | |
| decoy buildings + tighter clock. | |
| - uses `buildings_discovered_gte`,`within_ticks`; | |
| `move_units`,`observe`. Map `rush-hour-arena`. | |
| - engine status: runnable now. | |
| ### Reasoning | |
| **R1 — Tech-Tree Critical Path** | |
| - reasoning. RWM: ordering prerequisite-coupled tasks (B needs power | |
| needs ...) to hit a deadline is scheduling, not building. RA: | |
| task-graph scheduling with hard precedence + a makespan deadline. | |
| - win: `all_of[has_building:tent, building_total_gte:4, | |
| power_surplus_gte:0, within_ticks:T]`; levels shorten T, raise depth. | |
| - uses `has_building`,`building_total_gte`,`power_surplus_gte`, | |
| `within_ticks`; `build`,`place_building`,`deploy`. `rush-hour-arena`. | |
| - engine status: runnable now. | |
| **R2 — Power-Budget Brownout Avoidance** | |
| - reasoning. RWM: keeping a power budget non-negative while expanding — | |
| an online resource-constraint problem. RA: a robot keeping net power | |
| ≥ 0 while bringing subsystems online. | |
| - win: `all_of[power_surplus_gte:0, building_total_gte:N, | |
| within_ticks:T]`; hard raises N with scarce power slack. | |
| - uses `power_surplus_gte`,`building_total_gte`,`within_ticks`; | |
| `build`,`place_building`,`power_down`. `rush-hour-arena`. | |
| - engine status: runnable now. | |
| **R3 — Defensive-Direction Commitment** | |
| - reasoning. RWM: committing limited defenses to the *right* approach | |
| under directional uncertainty. RA: sensor/effector placement toward | |
| the most likely intrusion bearing. | |
| - win: `all_of[building_in_region{type:pbox,x:40,y:20,radius:9, | |
| count:2}, within_ticks:T]`; levels move/duplicate the threat axis. | |
| - uses `building_in_region`,`within_ticks`; | |
| `build`,`place_building`. `rush-hour-arena`. | |
| - engine status: runnable now. | |
| **R4 — Economy-Time-Box** (§1) — reasoning — engine status: runnable now. | |
| **R5 — Economy-Investment (a)** (§2a) — reasoning — runnable now; | |
| full (b) **needs S0 + S1**. | |
| ### Action | |
| **A1 — Synchronised Multi-Squad Arrival** | |
| - action. RWM: executing a plan so dispersed agents *converge* | |
| simultaneously — coordination/execution, not planning. RA: | |
| multi-robot rendezvous with a timing constraint. | |
| - win: `all_of[all_units_in_region{x:90,y:20,radius:6}, | |
| units_lost_lte:0, within_ticks:T]`; levels split start positions | |
| wider, tighten T. | |
| - uses `all_units_in_region`,`units_lost_lte`,`within_ticks`; | |
| `move_units`,`attack_move`,`stop`. `rush-hour-arena`. | |
| - engine status: runnable now. | |
| **A2 — Sequenced Build-then-Relocate** | |
| - action. RWM: executing an ordered build→deploy→reposition sequence | |
| without dropping a step under a clock. RA: a fixed manipulation | |
| sequence (assemble → carry → place) executed to deadline. | |
| - win: `all_of[building_in_region{x:60,y:20,radius:10,count:2}, | |
| units_lost_lte:1, within_ticks:T]`. | |
| - uses `building_in_region`,`units_lost_lte`,`within_ticks`; | |
| `move_units`,`deploy`,`build`,`place_building`. `rush-hour-arena`. | |
| - engine status: runnable now. | |
| **A3 — Harvester Re-tasking Under Interruption** | |
| - action. RWM: keeping a foraging loop alive by promptly re-tasking | |
| collectors when the route is disrupted. RA: closed-loop recovery of a | |
| collection cycle after an interruption. | |
| - win: `all_of[harvesters_gte:1, cash_gte:N, within_ticks:T]` — only | |
| meaningful once harvesting earns. | |
| - uses `harvesters_gte`,`cash_gte`,`within_ticks`; | |
| `harvest`,`move_units`,`stop`. `rush-hour-arena`. | |
| - engine status: **needs S0** (no schema-valid ore source today). | |
| ### Economy / Tech / Multi-objective | |
| **E1 — Economy-Time-Box harvest variant** — see §1 — **needs S0** | |
| (+ S1 if silos are in the test). | |
| **E2 — Economy-Investment full** — see §2(b) — **needs S0 + S1**. | |
| **M1 — Earn-and-Survive (multi-objective)** | |
| - reasoning (multi-objective). RWM: holding an income loop *and* a | |
| defensive constraint simultaneously — competing objectives under one | |
| budget. RA: a robot foraging while maintaining a safety constraint. | |
| - win: `all_of[cash_gte:N, units_lost_lte:0, building_total_gte:K, | |
| within_ticks:T]` (the `cash_gte` arm is real income only post-S0; | |
| pre-S0 it degenerates to a spend test like §1). | |
| - uses `cash_gte`,`units_lost_lte`,`building_total_gte`,`within_ticks`; | |
| `harvest`,`build`,`move_units`. `rush-hour-arena`. | |
| - engine status: spend-only form runnable now; income form **needs S0**. | |
| --- | |
| ## Summary | |
| - **Runnable now:** P1, P2, P3, R1, R2, R3, R4 (economy-time-box), | |
| R5/§2a (economy-investment runnable variant), A1, A2, and the | |
| spend-only form of M1 — all expressible in the existing grammar on | |
| `rush-hour-arena`. | |
| - **Engine-prereq S0** (schema-valid ore source — a one-line | |
| whitelist add or a `resource_fields` scenario field; the Rust seed | |
| loop already exists): unblocks any *harvest income* scenario — A3, | |
| E1, the income form of M1, and the §2(b) allocation scoring. | |
| - **Engine-prereq S1** (silo storage: stop hardcoding | |
| `resource_capacity`, route deposits through a capped `resources` | |
| pool in `tick_harvesters`, recompute capacity from owned | |
| `silo`/`proc` buildings, optional `resources_gte` predicate): | |
| required only for the *silo* mechanic in §2(b). | |
| - **Final-economy-value scoring** (cash + asset value at deadline) is a | |
| desirable leaderboard metric, NOT a win predicate — there is no such | |
| leaf and one should not be assumed. | |
| ### Files written | |
| - `openra_bench/scenarios/packs/economy-time-box.yaml` — validator: `OK` | |
| - `openra_bench/scenarios/packs/economy-investment.yaml` — validator: `OK` | |
| - `SCENARIO_BRAINSTORM.md` (this file) | |
| --- | |
| ## Tech-Tree & Production Planning pack (design-doc Family 2) | |
| Pack: `openra_bench/scenarios/packs/tech-production-planning.yaml`. Maps | |
| the Benchmark Design Proposal's **Task Family 2 — Sequential Dependency | |
| Planning** (Scenarios 2A/2B/2C) directly into one bench pack. | |
| **Why a new pack rather than extending existing ones.** Two existing | |
| packs touch this territory but ladder on different axes: | |
| | Pack | Easy axis | Medium axis | Hard axis | Ladder shape | | |
| |---|---|---|---|---| | |
| | `building-and-planning` | 1-hop prereq (powr→tent) + building count | spatial direction (place pbox east) | MCV relocate / new base | building → spatial → mobility (changes axis per level) | | |
| | `strict-production-bom` | exact 3×e1 (no overproduction) | + 2×e3 | + tsla + tight budget | spec-fidelity (BFCL/τ²-bench style; orthogonal to dependency planning) | | |
| | **`tech-production-planning`** (new) | 2-hop chain `weap→fix→3tnk` | parallel branches (barr ‖ weap+fix) + `power_surplus_gte:0` forces 2nd powr | mid-game infantry tech committed → scout fog → pivot to anti-armor | **dependency depth → parallel breadth → adaptive replanning** (same axis at increasing complexity) | | |
| `building-and-planning` is mixed-axis (Family 2-ish at easy, drifts into | |
| Family 3/spatial later). `strict-production-bom` tests a different | |
| capability entirely (instruction-following fidelity, not dependency | |
| planning). The new pack is the first time Family 2 is ladded canonically | |
| on one axis end-to-end. | |
| **Real tech tree (engine ground truth from `openra_env/game_data.py`):** | |
| | Building | Prereqs | Power | | |
| |---|---|---| | |
| | `powr` | — | +100 | | |
| | `proc` (refinery) | `powr` | −30 | | |
| | `weap` (war factory) | `proc` | −30 | | |
| | `fix` (service depot) | `weap` | −30 | | |
| | `barr` (soviet barracks) | `powr` | −20 | | |
| | `3tnk` (Heavy Tank) | `weap` + `fix` | unit | | |
| | `e1` / `e3` (infantry) | `barr|tent` | unit | | |
| → Heavy Tank's real chain is `powr → proc → weap → fix → 3tnk` (4 | |
| prereqs deep). Building all of `proc + weap + fix + barr` drains to −10 | |
| power against a 100-power base — a SECOND powr is mandatory for medium. | |
| This is the real `power_surplus_gte:0` teeth at medium. | |
| **Difficulty calibration (one notch under doc spec).** Design doc 2A | |
| ("Construction Yard + 5000 credits → produce one Heavy Tank from | |
| scratch") is harder than every existing easy in the repo (~3× the | |
| work). Easy here pre-places `fact + powr + proc` so the chain becomes | |
| `weap → fix → 3tnk` (2-hop), aligning with `strict-production-bom` | |
| easy's "1 build + N train" workload. Medium and hard are calibrated | |
| similarly downward. Hard (adaptive transition under fog) is left at the | |
| design-doc difficulty because no existing pack tests online plan | |
| revision — this is the pack's core new contribution. | |
| ### Follow-ups (Design Proposal Families 4 / 5 / 7) — separate PRs | |
| Each likely needs new engine infrastructure that isn't yet wired into | |
| the bench: | |
| - **Family 4 — Adversarial Reasoning** (Beginner/Normal/Hard AI | |
| match). Existing `adversarial-*.yaml` packs are duels/sieges (combat | |
| encounters), not full RTS matches; `bot_type: ''` is empty across | |
| them, so a scripted opponent AI tier doesn't appear to be exposed | |
| yet. Needs `bot_type` integration + a "win full match" win predicate. | |
| - **Family 5 — Reactive Replanning** (Early threat / Multi-raid / | |
| Catastrophic). The `interrupts` field exists (`adversarial-duel` | |
| uses `enemy_unit_spotted` + `own_unit_destroyed`), but | |
| raid-generation hooks (mid-game enemy attack scripts) and an | |
| "infrastructure destroyed" trigger aren't represented in any pack. | |
| - **Family 7 — Holistic Strategic Intelligence** (full RTS match at 3 | |
| difficulties). Needs Family 4's infra (full-match predicate + AI | |
| opponent) plus long-horizon scoring beyond the existing rubric. | |