yxc20098 commited on
Commit
6d71d3b
·
1 Parent(s): 20960c1

Quality drive: schema fix, 5 new/revised packs, 4 engine tests, scenario audit

Browse files

Schema (openra_bench/scenarios/schema.py):
* ScenarioPack gains top-level ore_patches:/water_cells:/water_rect:/
scheduled_events: fields. Without this declaration pydantic
silently DROPPED top-level keys, so easy+medium tiers of
econ-mine-and-grow had empty ore_patches (only hard tier worked
because it re-declared the field in its overrides).
* compile() falls back to pack-level value when the level's
overrides don't restate the field.

Engine-feature test pinning (4 files, all 9 sub-tests pass):
* test_resource_economy.py — ore_patches: materialise + harvest
loop end-to-end (stall LOSS / intended WIN)
* test_engineer_capture.py — Command.capture_actor proc transfer
* test_tanya_c4.py — Command.c4_detonate destroys building
* test_infiltrate.py — spy reveal + thf cash steal
* Field-name corrections (own_units → units_summary,
enemy_buildings → enemy_buildings_summary,
economy.cash → cash) matching the actual RustObsAdapter shape.
* Engineer/Tanya fog-distance fix (proc must be ≤4 cells from
subject so it appears in initial enemy_buildings_summary).

Scenario packs:
* econ-mine-and-grow.yaml — full redesign (auto-spawn
harv + footprint-overlap defect documented; scripted bar holds)
* econ-contested-expansion.yaml — NEW: front-base economy
with patrol harass at contested patch (bar holds × 4 seeds × tiers)
* econ-multi-patch-allocation.yaml — NEW: 4-patch OR allocation
decision (bar holds × tiers)
* econ-second-base-race.yaml — NEW: tempo expansion to
contested centre vs scripted enemy
* econ-harvester-defense-raid.yaml — NEW: harv-preservation
under raid (escort + stance management)
* spec-tanya-c4-strike.yaml — tick budgets recalibrated
(within_ticks 240/360/600, after_ticks +1)
* spec-thief-steal-cash.yaml — starting_cash rebalanced
(1000 floor so per-player cash gives enemy something to steal)
* spec-nuke-strike.yaml — hard tier added, capability
fixed to 'action' (was 'superweapon', not in enum)
* def-bridge-chokepoint.yaml — NEW chokepoint defense
pack using water_cells:; easy tier scripted bar holds, medium/
hard need new BuildingRusher bot (documented in header)

Audit:
* SCENARIO_UNIQUENESS_AUDIT.md — 95.8% packs healthy (181/189
stall LOSS); 3 real defects identified (mid-economy-under-fire,
combat-naval-shore-strike, spec-spy-infiltrate); base-trade
race = 0 packs (biggest capability gap); map mis-bindings on
183/189 packs.

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. SCENARIO_UNIQUENESS_AUDIT.md +496 -0
  2. data/maps/confined-aisle-64x40.oramap +0 -0
  3. data/maps/expansion-aggro-3-base-greedy-arena.oramap +0 -0
  4. data/maps/gen-arena-31f6567b.oramap +0 -0
  5. data/maps/gen-arena-498d46e9.oramap +0 -0
  6. data/maps/lh-defense-tech-second-base-arena.oramap +0 -0
  7. data/maps/mcv-deploy-third-base-arena.oramap +0 -0
  8. data/maps/mfb-base-1-defend-base-2-build-arena.oramap +0 -0
  9. data/maps/mfb-mirror-base-east-west-arena.oramap +0 -0
  10. data/maps/mfb-supply-line-corridor-arena.oramap +0 -0
  11. data/maps/mfb-third-base-against-clock-arena.oramap +0 -0
  12. data/maps/mfb-two-base-simultaneous-arena.oramap +0 -0
  13. data/maps/mid-concede-arena.oramap +0 -0
  14. data/maps/naval-arena-64x40.oramap +0 -0
  15. data/maps/scout-arena.oramap +0 -0
  16. openra_bench/agent.py +43 -1
  17. openra_bench/eval_core.py +96 -0
  18. openra_bench/full_playback.py +334 -0
  19. openra_bench/playback_view.py +117 -6
  20. openra_bench/providers.py +33 -0
  21. openra_bench/run_eval.py +55 -7
  22. openra_bench/scenarios/packs/action-multiunit-coordination.yaml +48 -24
  23. openra_bench/scenarios/packs/action-sequenced-execution.yaml +46 -22
  24. openra_bench/scenarios/packs/adv-asymmetric-weaker-must-win.yaml +58 -35
  25. openra_bench/scenarios/packs/adv-rps-counter-pick.yaml +59 -35
  26. openra_bench/scenarios/packs/adversarial-skirmish.yaml +12 -5
  27. openra_bench/scenarios/packs/artofwar-decoy-sacrifice.yaml +35 -14
  28. openra_bench/scenarios/packs/artofwar-indirect-approach.yaml +39 -18
  29. openra_bench/scenarios/packs/artofwar-lure-the-tiger.yaml +41 -20
  30. openra_bench/scenarios/packs/artofwar-sequenced-citadel.yaml +14 -6
  31. openra_bench/scenarios/packs/build-defensive-skirt-corners.yaml +58 -33
  32. openra_bench/scenarios/packs/build-defensive-tower-cluster.yaml +54 -30
  33. openra_bench/scenarios/packs/build-defensive-tower-line.yaml +49 -26
  34. openra_bench/scenarios/packs/build-engineer-rebuild-after-loss.yaml +59 -36
  35. openra_bench/scenarios/packs/build-power-down-defensive.yaml +49 -25
  36. openra_bench/scenarios/packs/build-power-online-first.yaml +44 -24
  37. openra_bench/scenarios/packs/build-production-throughput-multibuilding.yaml +48 -28
  38. openra_bench/scenarios/packs/build-rally-point-management.yaml +62 -39
  39. openra_bench/scenarios/packs/build-repair-priority-under-fire.yaml +59 -36
  40. openra_bench/scenarios/packs/build-sell-and-rebuild-elsewhere.yaml +61 -38
  41. openra_bench/scenarios/packs/build-sequence-tech-cheapest.yaml +47 -26
  42. openra_bench/scenarios/packs/build-sequence-tech-fastest.yaml +39 -19
  43. openra_bench/scenarios/packs/build-sequence-tech-most-resilient.yaml +70 -47
  44. openra_bench/scenarios/packs/build-tech-skip-decision.yaml +47 -26
  45. openra_bench/scenarios/packs/building-and-planning.yaml +39 -18
  46. openra_bench/scenarios/packs/combat-attack-from-behind-fog.yaml +73 -48
  47. openra_bench/scenarios/packs/combat-bait-counter-attack.yaml +58 -37
  48. openra_bench/scenarios/packs/combat-divide-and-conquer.yaml +59 -36
  49. openra_bench/scenarios/packs/combat-flanking-attack.yaml +65 -44
  50. openra_bench/scenarios/packs/def-bridge-chokepoint.yaml +2046 -0
SCENARIO_UNIQUENESS_AUDIT.md ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Scenario Uniqueness Audit (Phase 2)
2
+
3
+ Read-only audit of `openra_bench/scenarios/packs/*.yaml`. Scope: 212 YAML
4
+ files (excluding `TEMPLATE.yaml`); 23 in-flight `econ-*` packs were
5
+ deliberately skipped (other agents own them), leaving **189 packs probed**.
6
+
7
+ The probe was a stall policy — `Command.observe()` only — run on the
8
+ hard tier, seed=1, of every pack, through the same code path
9
+ (`openra_bench.eval_core.run_level`) the bench evaluator uses. Each
10
+ result is the engine-evaluated outcome (`win` / `loss` / `draw`),
11
+ final game tick, and the static profile read straight from the YAML.
12
+
13
+ Headline numbers
14
+ - 181 / 189 packs (95.8%) — stall LOSSES (healthy: no-cheat bar holds).
15
+ - 3 packs — stall WINS (the no-cheat bar is broken).
16
+ - 3 packs — stall DRAWS (no real LOSS reachable from a stall play).
17
+ - 2 packs — Rust engine panic at `reset(seed=1)` (both
18
+ `adversarial-siege` / `adversarial-skirmish` — already
19
+ `status: quarantine` in-pack so they are NOT in the default eval set,
20
+ but they remain on disk and panic when explicitly invoked).
21
+
22
+ Quarantined packs are still YAML-discoverable and were probed. They are
23
+ flagged `[Q]` in the lists below.
24
+
25
+ ---
26
+
27
+ ## Section 1 — Defect packs
28
+
29
+ A "defect" here means stall WINS (the lazy bar fell) or stall DRAWS
30
+ (no real reachable LOSS — the timeout collapses to draw degeneracy
31
+ because the fail predicate never trips). Both are explicit violations
32
+ of the "no defect, no cheat" bar in `CLAUDE.md`.
33
+
34
+ ### 1A. Stall WINS (3 — bar fell)
35
+
36
+ | Pack | Outcome | Turn | Tick | Diagnosis |
37
+ |---|---|---|---|---|
38
+ | `mid-economy-under-fire` | **WIN** | 11 | 993 | Stall achieves the win predicate without any agent action. Hard win-clause is `economy_value_gte:4000 AND harv≥2 AND units_lost_lte:2`. With the current engine, the 3 starter harvesters auto-harvest (no `harvest` command needed — they begin in `harvest` mode), the perimeter 1tnks auto-fire and kill the lone raider 1tnk, and EV trips 4000 by tick ~993 with zero losses. The pack header says "Stall (only observe)… harvs never harvest → EV stays at 0 → LOSS." That is no longer true under the current engine — harvs in `harvest` mode work without re-issued orders. **Fix**: either remove the harvesters from the starting placement (require the model to issue `Command.harvest`) or replace the `1tnk` raider with a stronger raider wave that out-attritions an idle defense ring. |
39
+ | `combat-naval-shore-strike` | **WIN** | 3 | 198 | Stall wins inside 3 turns. The hard tier places two destroyers in the water channel on `stance:2` (Defend), and the entire shore garrison on `stance:0` (HoldFire). The destroyers auto-fire on every garrison unit in range and clear all 5 enemies in <200 ticks; the agent never sends a command. **Fix**: set the destroyers to `stance:0` so the agent must issue an `attack_unit` order; or place the shore garrison just outside the destroyer's auto-target range so the agent must manually fire. The whole capability (target an across-shore target with a ranged unit) collapses because the engine fires automatically. |
40
+ | `def-with-ambush` | (intended) **WIN** | 24 | 1233 | **Not a defect — exempt by design**. CLAUDE.md `§Triage coverage`: "1 pack (`def-with-ambush`) is exempt by design (positional-discipline scenario where do-nothing IS the intended policy)." The capability under test is *hold the ambush position*; the four `stance:2` flanker tanks auto-engage the rusher band when it enters weapon range. Listed here only for completeness — no action required. |
41
+
42
+ ### 1B. Stall DRAWS (3 — no LOSS reachable)
43
+
44
+ A draw means the win predicate was never met AND the fail predicate
45
+ never tripped, so the level resolves to `draw` (outcome score 0.5).
46
+ This is a defect because it makes the lazy play indistinguishable
47
+ from a partial / borderline play, and inflates the lazy score by
48
+ treating it as half a win.
49
+
50
+ | Pack | Outcome | Turn | Tick | Diagnosis |
51
+ |---|---|---|---|---|
52
+ | `economy-time-box` [Q] | **DRAW** | 80 | 7203 | Already quarantined in-file (reason: "redundant with economy-force-buildup"). The hard tier has **no `fail_condition` at all** (`fail_condition: None`), so a 12000-tick budget that never produces 6 units / 5 buildings simply times out as draw. This is one of the canonical CLAUDE.md defect classes ("no `fail_condition`, or only triggers on full force-wipe; a stall / preserve / partial outcome silently draws"). Since the pack is quarantined the lazy-bar drift is harmless to the default eval; if the pack is ever un-quarantined, add `fail_condition: {after_ticks: 12001}` plus `{not: own_units_gte: 1}`. |
53
+ | `spec-spy-infiltrate` | **DRAW** | 3 | 273 | Hard tier `fail_condition: {after_ticks: 4001}` — i.e. the only failure mode is the deadline. But the agent's only combat-relevant units are 2 spies (`spy`) and the scenario also places one defender `e1`. With stall, the spies are passive while the engine auto-`done`s at turn 3 (likely because the defender `e1` falls to auto-fire or because all agent combat units are destroyed). The outcome lands as DRAW because the spy-only force is wiped before the within_ticks ever bites. **Fix**: add `{not: own_units_gte: 1}` (or `unit_type_count_gte:{spy, 1}`) to the `fail_condition.any_of` so a spy-wipe is a LOSS, not a draw. Same fix likely needed for `easy` / `medium` (uninspected; consider auditing). |
54
+ | `def-bridge-chokepoint` | (mixed: usually **LOSS** at tick 1623, occasionally **DRAW** in one fresh process) | 18 | 1623 | The first probe (subprocess #1) reported DRAW; three subsequent re-runs from a fresh process consistently reported LOSS (17 losses, fail-clause `not has_building:fact` trips when the procs/fact fall under the rifle assault). This is borderline — the pack is *probably* healthy, but the one-off DRAW observation suggests a possible determinism-edge case (race between the engine auto-`done` and the predicate evaluator). Recommend a 4-seed re-probe (1–4) over a clean Python process to confirm. If the LOSS is stable, no fix required; if the DRAW resurfaces, add `{not: own_units_gte: 1}` to the fail clause. |
55
+
56
+ ### 1C. Engine panics at reset (2)
57
+
58
+ | Pack | Status | Error |
59
+ |---|---|---|
60
+ | `adversarial-siege` | `quarantine` (consolidated into `adversarial-duel`) | `pathfinder.rs:175 index out of bounds: len 5120, index 5382` at `env.reset(seed=1)`. The hard tier places actors outside the playable bounds (CLAUDE.md footgun #6) for the chosen spawn. Pack is excluded from default eval; no eval-time impact, but flag the file is still selectable via explicit `--packs`. |
61
+ | `adversarial-skirmish` | `quarantine` (consolidated into `adversarial-duel`) | Same panic at `env.reset`. Same situation as above. |
62
+
63
+ Recommend either deleting these two YAMLs (the consolidation is the
64
+ documented future state) or moving them to `packs/_archived/` so
65
+ `discover_packs` skips them (it already skips `_*` and `TEMPLATE`).
66
+
67
+ ---
68
+
69
+ ## Section 2 — Duplicate / near-duplicate clusters
70
+
71
+ Methodology: each pack was fingerprinted by
72
+ `(capability, tools, actor_types_sorted, win_predicate_keys_sorted)`.
73
+ Five strict (= all four fields equal) clusters surfaced; a second pass
74
+ relaxed `tools` and `actor_types` to surface near-duplicates with the
75
+ same capability + same win-key set, which surfaced a further ~10
76
+ clusters. Each cluster is judged on the per-pack `real_world_meaning`
77
+ and the `level_description` of the hard tier — if these tell the same
78
+ story, the cluster is genuinely redundant; if not, the predicate-key
79
+ match is coincidental and the packs probe distinct skills.
80
+
81
+ ### 2A. Strict duplicates (action required)
82
+
83
+ 1. **`adversarial-siege` + `adversarial-skirmish`** — both already
84
+ quarantined as "consolidated into `adversarial-duel`". **Keep:**
85
+ `adversarial-duel`. **Action:** physical removal (delete or
86
+ `_archive`) — they are the only two packs that crash the engine at
87
+ load, so removing them eliminates the crash class entirely.
88
+
89
+ 2. **`artofwar-decoy-sacrifice` + `artofwar-lure-the-tiger`** — both
90
+ `capability: reasoning`, both use bot `guard`, identical hard-tier
91
+ actor list `[2tnk,e1,e3,fact,jeep]`, identical win-keys
92
+ `{units_in_region_gte, units_lost_lte, within_ticks}`, both
93
+ described as "main-force reaches an objective by diverting a
94
+ leashed defender." **Keep:** `artofwar-lure-the-tiger` (the more
95
+ doctrinally complete framing — leash mechanic; "the strong
96
+ defender" is the load-bearing pull). **Merge / delete:**
97
+ `artofwar-decoy-sacrifice` — its "spend a decoy unit" angle is
98
+ covered by the `units_lost_lte` clause in the lure pack, which
99
+ already permits a small attrition budget the model can spend on
100
+ the bait sub-force.
101
+
102
+ 3. **`scout-cycle-keep-info-fresh` + `scout-track-enemy-movement`** —
103
+ both `capability: perception`, identical hard-tier actor list, both
104
+ use the `then`-chained `units_killed_gte` win predicate, both
105
+ probe "scout multiple times because the world changes between
106
+ observations." Both use the `scheduled_events:` hook (cycle =
107
+ reinforcement waves; track = enemy march legs). **These are
108
+ genuinely distinct skills** — cycle is "re-observe a stale region
109
+ for new arrivals", track is "follow a moving target across the
110
+ map." **Keep both**, but verify the briefing of each clearly
111
+ distinguishes the cause of staleness so the model is not asked to
112
+ solve cycle-by-track-strategy or vice-versa. (No action required if
113
+ the briefings are unambiguous, which they appear to be on
114
+ inspection.)
115
+
116
+ 4. **`build-defensive-skirt-corners` + `build-defensive-tower-line`**
117
+ (strict 4-field match) **AND** the wider quartet
118
+ `build-defensive-tower-cluster` + `def-in-depth-vs-single`
119
+ (same capability + win-key set, near-identical actor list, all use
120
+ `rusher` bot). These four packs all probe "where do you place a
121
+ finite pillbox budget to survive an incoming rush?" with topology
122
+ variants: skirt (one in each map-relative corner of the building),
123
+ cluster (tight wrap), line (across the choke), depth (two thinner
124
+ bands). **Each topology is genuinely distinct in the optimal
125
+ answer**, so all four packs probe a real choice axis. **Keep all
126
+ four** but consider promoting them into a single named "Defense
127
+ Topology Suite" in the eval-cell catalog so the four cells score
128
+ together (one "topology-IQ" metric) rather than as four unrelated
129
+ reasoning packs. (No file action required; this is a catalog /
130
+ metric grouping recommendation.)
131
+
132
+ ### 2B. Near-duplicates by win-predicate keys (informational — same
133
+ predicate idiom is used across distinct scenarios; verify each is
134
+ load-bearing)
135
+
136
+ These groups all share an identical `(capability, win_predicate_keys)`
137
+ fingerprint. Each group below is **not necessarily a duplicate**, but
138
+ flags that all members rely on the same predicate idiom and the
139
+ scenarios should diverge on actor composition / pressure / bot to
140
+ remain discriminative.
141
+
142
+ - `(reasoning, then+within_ticks)` — N=10:
143
+ `build-power-online-first`, `build-sequence-tech-cheapest`,
144
+ `build-sequence-tech-fastest`, `build-sequence-tech-most-resilient`
145
+ (not in cluster — was filtered earlier), `lh-econ-army-victory`,
146
+ `lh-opening-to-defense-to-counter`, `lh-opening-to-tech-to-army`,
147
+ `lh-progression-stage-locked`, `rob-objective-change-midway`,
148
+ `tech-balanced-econ-then-tech`,
149
+ `lh-build-army-coordinate-multifront-attack`. These are all
150
+ ordered-multi-phase scenarios. Distinct intents (build-order vs
151
+ long-horizon vs objective-shift); the `then` predicate is the
152
+ common engine machinery, not a duplication signal.
153
+
154
+ - `(reasoning, building_count_gte+building_in_region+within_ticks)` —
155
+ N=7: `build-sell-and-rebuild-elsewhere`, `def-in-depth`,
156
+ `def-tower-line-vs-cluster`, `expansion-aggro-3-base-greedy`,
157
+ `mcv-deploy-second-base`, `mcv-deploy-third-base`,
158
+ `mfb-mirror-base-east-west`. All probe "build / re-build N
159
+ buildings in a target region." Genuinely distinct intents
160
+ (expansion-greed, mirror-base symmetry, MCV-redeploy under
161
+ pressure); keep all.
162
+
163
+ - `(reasoning, building_count_gte+units_killed_gte+within_ticks)` —
164
+ N=6: `adv-rps-counter-pick`, `build-tech-skip-decision`,
165
+ `def-counter-battery`, `def-walls-vs-towers`, `def-while-building`,
166
+ `lh-tech-rush-vs-army-rush`. All "kill K enemies and have N
167
+ buildings standing." Reads as honest differentiated tests.
168
+
169
+ - `(action, own_units_gte+units_killed_gte+within_ticks)` — N=5:
170
+ `combat-flanking-attack`, `combat-harass-aggro-commit`,
171
+ `combat-kite-and-pull`, `combat-kite-jeep-vs-tank`,
172
+ `combat-tanya-vs-rush`. Five combat micro packs — kite, flank,
173
+ harass. Each probes a different micro idiom; keep all but verify
174
+ that the win bar (`units_killed_gte`) is tuned per pack so the
175
+ intended micro is load-bearing, not just "auto-fire wins."
176
+ Two are explicitly kite (`combat-kite-and-pull`,
177
+ `combat-kite-jeep-vs-tank`) — **possible duplicate**: kite-and-pull
178
+ uses generic 2tnk/3tnk vs jeep-vs-tank's unit-class asymmetry.
179
+ Keep both only if `combat-kite-jeep-vs-tank` actually requires the
180
+ jeep speed advantage that `combat-kite-and-pull` doesn't (read the
181
+ briefings; they look distinct).
182
+
183
+ - `(action, reach_region+units_lost_lte+within_ticks)` — N=4:
184
+ `proc-no-attack-passive-only`, `proc-tool-use-multi-distractor`,
185
+ `proc-tool-use-with-distractor`,
186
+ `strict-toolban-fidelity-under-pressure`. All
187
+ procedural-compliance packs — "reach the goal without using a
188
+ forbidden tool / without attacking." The `proc-tool-use-*` pair is
189
+ a possible duplicate: "with-distractor" (one distractor tool) vs
190
+ "multi-distractor" (multiple). Both are the same skill at different
191
+ scales — recommend collapsing into a single pack with three tiers
192
+ (no distractor / one / many) rather than two top-level packs.
193
+
194
+ - `(perception, buildings_discovered_gte+units_lost_lte+within_ticks)`
195
+ — N=4: `perception-target-vs-fog`, `scout-detect-enemy-tech`,
196
+ `scout-discover-hidden-base`, `scout-multiple-fog-areas`. All
197
+ "discover K enemy buildings under fog." Each probes a distinct
198
+ discrimination (target-vs-fog = ignore decoys; detect-enemy-tech =
199
+ read the tech-tree; hidden-base = single hidden compound;
200
+ multiple-fog-areas = K disjoint regions). Keep all.
201
+
202
+ ### 2C. Overlapping prefixes worth a catalog review (not duplicates,
203
+ but the area is dense)
204
+
205
+ - `combat-*` (25 packs), `def-*` (18), `scout-*` (14), `build-*` (14),
206
+ `lh-*` (13), `proc-*` (10), `mfb-*` (8), `rob-*` (8), `tp-*` (7),
207
+ `coord-*` (7), `mcv-*` (6), `spec-*` (5), `economy-*` (5),
208
+ `tech-*` (4), `strategy-*` (4), `perception-*` (4), `artofwar-*` (4),
209
+ `tempo-*` (2), `maint-*` (2), `mid-*` (2), `expansion-*` (3),
210
+ `coordination-*` (2), `harass-*` (1), `risk-*` (1), `power-*` (1),
211
+ `navigation-*` (1), `defense-*` (1), `longhorizon-*` (1),
212
+ `custom-*` (1), `building-*` (1), `rush-*` (1), `reasoning-*` (2),
213
+ `action-*` (2), `adv-*` (2), `adversarial-*` (3), `strict-*` (3).
214
+ The combat / def / scout / build axes carry ~70 packs combined; a
215
+ follow-up audit explicitly checking pairwise overlap inside each
216
+ prefix (briefing-level read) would surface ~5–10 more candidate
217
+ collapses.
218
+
219
+ ---
220
+
221
+ ## Section 3 — Capability coverage matrix
222
+
223
+ Cross-referenced against the **phase × decision-type** matrix sketched
224
+ in `PAPER_PLAN.md §12.2` and `CLAUDE.md`. Each cell lists the packs
225
+ that primarily measure it (a pack may legitimately serve more than one
226
+ cell — only the dominant assignment is listed; cross-cutting packs are
227
+ flagged).
228
+
229
+ ### Opening
230
+
231
+ | Decision | Packs | Density |
232
+ |---|---|---|
233
+ | MCV deploy site (where to plant) | `mcv-deploy-and-build`, `mcv-deploy-defensible-site`, `mcv-deploy-near-resource`, `mcv-deploy-relocate-under-pressure`, `mcv-deploy-second-base`, `mcv-deploy-third-base` | **6 packs** (dense; good) |
234
+ | Build-order commit | `build-sequence-tech-cheapest`, `build-sequence-tech-fastest`, `build-sequence-tech-most-resilient`, `build-tech-skip-decision`, `tech-balanced-econ-then-tech`, `tech-aggro-all-in`, `tech-turtle-defensive-tech`, `tech-production-planning`, `build-power-online-first`, `power-budget-online`, `building-and-planning` | **11 packs** (dense; good) |
235
+ | Defense-direction commit (which side to anticipate) | `def-position-expected-direction`, `def-position-revealed-direction`, `def-multi-direction`, `def-surprise-flank-react`, `def-pre-position-mobile-reserve` | **5 packs** (good) |
236
+ | Rush-defense | `defense-rush-survive`, `rush-hour`, `build-defensive-tower-cluster`, `build-defensive-tower-line`, `build-defensive-skirt-corners`, `def-in-depth`, `def-in-depth-vs-single`, `def-walls-vs-towers`, `def-tower-line-vs-cluster`, `def-while-building` | **10 packs** (dense) |
237
+
238
+ ### Early-mid
239
+
240
+ | Decision | Packs | Density |
241
+ |---|---|---|
242
+ | Harass / harass-preserve | `harass-response-preserve`, `combat-harass-aggro-commit`, `combat-harass-balanced-hit-and-run`, `combat-skirmish-then-disengage`, `combat-bait-counter-attack`, `combat-kite-and-pull`, `combat-kite-jeep-vs-tank`, `combat-retreat-after-engagement`, `combat-suicide-charge-mission` | **9 packs** (dense) |
243
+ | Exact-count perception | `perception-count-the-threat`, `perception-count-the-threat-small-k`, `scout-count-defenders`, `scout-detect-incoming-army` | **4 packs** (adequate) |
244
+ | Scout-direction commit | `scout-detect-base-direction`, `scout-far-frontier`, `scout-frontier-reading` (=`perception-frontier-reading`), `scout-multiple-fog-areas`, `scout-and-report`, `scout-and-survive`, `scout-discover-hidden-base`, `scout-detect-enemy-tech`, `scout-map-reveal-percent-target`, `reasoning-frontier-commit` | **10 packs** (dense; possible over-coverage) |
245
+
246
+ ### Mid
247
+
248
+ | Decision | Packs | Density |
249
+ |---|---|---|
250
+ | Live-economy defense | `mid-economy-under-fire` (**DEFECT — stall wins**), `econ-harvester-defense-raid` [skipped — econ-*], `econ-protect-harvester-route` [skipped] | **~3 packs, 1 defective** — the only non-econ exemplar is broken. **Gap when the in-flight econ-* land — verify they cover.** |
251
+ | Tech-switch on scout | `mid-tech-switch-on-scout`, `lh-scout-react-counter`, `adv-rps-counter-pick` | **3 packs** (adequate) |
252
+ | Second front | `mfb-base-1-defend-base-2-build`, `mfb-supply-line-link-between-bases`, `mfb-mirror-base-east-west`, `mfb-two-base-simultaneous`, `mfb-third-base-against-clock`, `expansion-balanced-2-base-defended`, `expansion-aggro-3-base-greedy`, `coord-diversionary-attack` | **8 packs** (dense) |
253
+ | Replan after loss | `rob-unit-loss-recovery`, `rob-partial-base-loss-continue`, `lh-recovery-after-mid-game-loss`, `build-engineer-rebuild-after-loss`, `def-retreat-and-rebuild`, `build-sell-and-rebuild-elsewhere` | **6 packs** (good) |
254
+
255
+ ### Mid-late
256
+
257
+ | Decision | Packs | Density |
258
+ |---|---|---|
259
+ | Concede vs hold | `mid-concede-vs-hold` | **1 pack** (THIN — single exemplar) |
260
+ | Isolate vs split | `combat-divide-and-conquer`, `combat-pincer-coordination`, `combat-prevent-retreat`, `combat-formation-tank-wedge` | **4 packs** (adequate) |
261
+ | Tempo double-window | `tempo-double-window`, `tempo-strike-window`, `coordination-staggered-window`, `tp-survive-and-strike-at-window` | **4 packs** (adequate) |
262
+ | Decoy / lure / feint | `artofwar-decoy-sacrifice`, `artofwar-lure-the-tiger`, `artofwar-indirect-approach`, `artofwar-sequenced-citadel`, `combat-bait-counter-attack`, `coord-diversionary-attack`, `def-with-ambush` | **7 packs** (good) |
263
+ | Multi-front | `mfb-rotating-production-pressure`, `mfb-redundant-tech-buildings`, `mfb-tech-base-vs-economy-base`, `rob-multiple-simultaneous-pressures` | **4 packs** (adequate) |
264
+
265
+ ### Late
266
+
267
+ | Decision | Packs | Density |
268
+ |---|---|---|
269
+ | Sustained multi-front | `lh-build-army-coordinate-multifront-attack`, `mfb-rotating-production-pressure` | **2 packs** (thin) |
270
+ | Base-trade race | None directly | **0 packs (GAP)** |
271
+ | Counter-strategy read | `adv-rps-counter-pick`, `combat-attack-from-behind-fog`, `lh-tech-rush-vs-army-rush` | **3 packs** (adequate) |
272
+ | Superweapon timing | `spec-nuke-strike`, `spec-tanya-c4-strike`, `spec-engineer-capture`, `spec-spy-infiltrate` (**DEFECT — stall draws**), `spec-thief-steal-cash` | **5 packs, 1 defective** (good once spec-spy is fixed) |
273
+ | Credit-only final phase | `lh-credit-only-final-phase` | **1 pack** (thin) |
274
+
275
+ ### Cross-cutting
276
+
277
+ | Capability | Packs | Density |
278
+ |---|---|---|
279
+ | Procedural compliance under pressure | `proc-checklist-no-deviation`, `proc-conditional-branch-action`, `proc-instruction-following-edge-case`, `proc-no-attack-passive-only`, `proc-only-build-no-combat`, `proc-only-defend-no-attack`, `proc-ordered-action-strict`, `proc-strict-toolban-fidelity`, `proc-tool-use-multi-distractor`, `proc-tool-use-with-distractor`, `strict-production-bom`, `strict-sequence`, `strict-toolban-fidelity-under-pressure`, `tp-pressure-procedural` | **14 packs** (very dense) |
280
+ | Long-horizon multi-phase | `lh-*` (13 packs), `longhorizon-opening-to-assault`, `lh-100-turn-marathon-survival` | **14 packs** (dense) |
281
+ | Coordination across squads | `coord-converge-on-target`, `coord-cover-and-move`, `coord-diversionary-attack`, `coord-mutual-support`, `coord-relay-attack`, `coord-relay-vision-chain`, `coord-squad-handoff`, `coordination-ordered-rendezvous`, `coordination-staggered-window`, `action-multiunit-coordination`, `action-sequenced-execution`, `combat-pincer-coordination`, `combat-heli-flank` | **13 packs** (dense) |
282
+ | Adversarial 1v1 (full macro) | `adversarial-duel`. The full 1v1 battleground lives in `one_v_one.py` (`openra_bench/`), not as a `meta.capability: adversarial` pack. | **1 pack** (the catalog says this is by design — full macro is the live ladder) |
283
+
284
+ ---
285
+
286
+ ## Section 4 — Capability gaps (uncovered or thin cells)
287
+
288
+ Cells worth new packs, in priority order:
289
+
290
+ 1. **Base-trade race (mid-late / late)** — *no* current pack tests
291
+ "your base falls while you race the enemy's; whoever finishes first
292
+ wins." Sketch: hard tier places both players' bases halfway to
293
+ killable, no defenders left; the agent has one army worth attacking
294
+ with, the enemy is doing the same to the agent's base; win by
295
+ destroying the enemy's `fact` before they destroy yours. Win
296
+ `building_count_gte:{type:fact,n:0,owner:enemy}` paired with
297
+ `has_building:fact` (own) and an aggressive `within_ticks`. Pack id:
298
+ `combat-base-trade-race`.
299
+
300
+ 2. **Concede-vs-hold (mid-late)** — only `mid-concede-vs-hold`
301
+ currently. Sketch a second pack: `mid-concede-vs-rebuild` where a
302
+ forward outpost is lost-cost (committing reinforcements throws
303
+ good money after bad), and the right call is to abandon it and
304
+ rebuild on a held line. Win condition: keep the rear-line `fact`
305
+ alive AND have ≥4 `tent`/`weap` on the rear line at deadline;
306
+ throwing reinforcements at the forward outpost fails the resource
307
+ budget. Pack id: `mid-concede-forward-rebuild-rear`.
308
+
309
+ 3. **Live economy defense (mid)** — the only non-econ pack
310
+ (`mid-economy-under-fire`) is currently a stall-WIN defect. After
311
+ fixing it, add a sibling pack `mid-economy-rebuild-harvester-line`
312
+ where the agent must re-issue `harvest` commands to a freshly
313
+ produced harvester after a raider kills the starter ones (puts
314
+ the load-bearing capability on the `harvest` order, not on the
315
+ passive auto-harvest).
316
+
317
+ 4. **Long-horizon credit-only final phase** — `lh-credit-only-final-phase`
318
+ is the only exemplar. Sketch sibling: `lh-credit-only-bait-window` —
319
+ the agent must spend the last credits on a single decisive strike
320
+ window rather than on attrition. Different decision (one shot vs
321
+ accumulation).
322
+
323
+ 5. **Sustained multi-front (late)** — thin (2 packs). Sketch:
324
+ `mfb-three-front-rotation` — three simultaneously-attacked bases
325
+ with the agent's army too small to defend all three at once; the
326
+ decision is which two to hold and which one to let fall while the
327
+ army cycles. Cross-pack with `coord-relay-attack`.
328
+
329
+ 6. **MCV deploy under timer + crossfire** — current MCV packs cover
330
+ site selection but not "deploy NOW or the MCV is destroyed by an
331
+ incoming wave that hits in 4 turns." Sketch:
332
+ `mcv-deploy-emergency-relocation` — the starting MCV stands in the
333
+ path of an incoming squad; the agent must deploy + re-build OR
334
+ move + re-deploy further west before the squad arrives.
335
+
336
+ 7. **Information freshness across modalities** — `scout-cycle-keep-info-fresh`
337
+ and `scout-track-enemy-movement` cover this for the structured
338
+ channel, but the perception ablation grid (channel × fog) doesn't
339
+ currently have an information-freshness pack that is explicitly
340
+ easier to solve with the labelled image (the `image` channel is
341
+ advantaged when the model can spot newly-spawned units
342
+ inter-tick). Sketch: `perception-freshness-image-advantage`.
343
+
344
+ 8. **Single-pack `meta.capability: adversarial`** — only
345
+ `adversarial-duel` carries this tag. PAPER_PLAN.md §12.2 notes the
346
+ imbalance. The full 1v1 lives in `one_v_one.py` (correct), but a
347
+ second adversarial-tagged pack that tests "reactive opponent
348
+ selects a counter from a small menu mid-game" (different from RPS
349
+ pre-game commit) would put real teeth on the tag.
350
+
351
+ 9. **Engineer / Tanya / spy under fire** (specialist packs) — only one
352
+ pack each (`spec-engineer-capture`, `spec-tanya-c4-strike`,
353
+ `spec-spy-infiltrate`, `spec-thief-steal-cash`, `combat-tanya-vs-rush`).
354
+ Each specialist has one happy-path scenario. Sketch a "stealth +
355
+ target priority" pack per specialist that requires the model to
356
+ pick *which* of N enemy assets to hit with the one-shot specialist.
357
+
358
+ 10. **Naval / amphibious** — `combat-naval-shore-strike` is the only
359
+ naval pack (and it's currently a stall-WIN defect). Once fixed,
360
+ add `combat-naval-amphibious-landing` (a transport ship lands
361
+ infantry on a contested shore) and `combat-naval-anti-air-vs-bomber`
362
+ pending the air-unit engine work. These are documented as
363
+ out-of-scope in `PAPER_PLAN.md §11.1` for the air variant but the
364
+ naval ones are now feasible thanks to `water_rect:`.
365
+
366
+ ---
367
+
368
+ ## Section 5 — Map mis-bindings (rush-hour-arena used where intent
369
+ calls for a custom map)
370
+
371
+ Of 189 probed packs, **183** use `rush-hour-arena`; **3** use a custom
372
+ map (`navigation-confined-hard-only`, `custom-map-no-enemy`,
373
+ `strategy-dilemma`/`-gauntlet`/`-twobody`); **1** uses a generator
374
+ (`combat-naval-shore-strike`); **2** are quarantined-and-crashing.
375
+
376
+ Packs whose stated intent calls for a non-rush-hour geometry:
377
+
378
+ | Pack | Stated intent | Current map | Recommendation |
379
+ |---|---|---|---|
380
+ | `combat-heli-flank` | Helicopter assault from a flank; the brief explicitly references aircraft flight over terrain. | rush-hour-arena (no terrain features to flank around) | Author / use a custom map with a forested ridge that channels ground units one way and aircraft another. (Engine air-unit support is out-of-scope per PAPER_PLAN §11.1 — pack is currently a ground-only proxy.) |
381
+ | `def-bridge-chokepoint` | "A water band cuts the map east-to-west with bridges." | rush-hour-arena + `water_rect` overlay | The `water_rect` overlay correctly synthesizes the bridge geometry on top of rush-hour, but the chokepoint-narrowing aspect ("attackers must pass through 3 narrow openings") works against an open-arena map. Consider a custom `bridges-arena` map authored once — the visual / minimap reading test is more honest when the bridges are real terrain rather than a YAML overlay on an unrelated map. |
382
+ | `combat-naval-shore-strike` | Naval ship in a water channel. | uses `naval-arena` generator (correct) — but the generator spec lives in the pack file rather than a named `.oramap`. | When a second naval pack is authored (per gap #10), promote `naval-arena` to a real `data/maps/naval-arena.oramap` so the bench has a canonical naval geometry. |
383
+ | `def-with-ambush` | "Concealed flanking defenders catch the band in an L-ambush down a lane toward the construction yard." Doctrinal answer requires real linear terrain. | rush-hour-arena (open) | The pack synthesizes the lane purely with actor placement (a single `e1` fixing defender at x=15, flankers at x=40). Visually the geometry is invisible on the minimap — a custom `corridor-arena` map with a real lane would make the spatial decision legible from the image. (Capability is sound; this is a perception-channel polish issue.) |
384
+ | `mfb-supply-line-link-between-bases` | "Supply line corridor between two bases." | rush-hour-arena | Similar polish issue — a custom multi-base map with two separated valleys would make the supply-line decision visible on the minimap rather than implicit in the actor coordinates. |
385
+ | `combat-hold-chokepoint` | "Defend a narrow chokepoint corridor." | rush-hour-arena (no narrow corridor) | Same; either author a `chokepoint-arena` map or accept that the chokepoint is implied by enemy spawn geometry alone. |
386
+ | `mcv-deploy-second-base`, `mcv-deploy-third-base` | "Plant a second / third base at a defensible site." | rush-hour-arena (uniform; no defensible sites) | The map is symmetric and uniform — every cell is roughly as defensible as every other. The capability becomes "pick a cell that satisfies the `building_in_region` predicate," not "pick a *defensible* cell." Add a custom map with terrain features (cliff edges, narrow passes) so the defensibility geometry is real. |
387
+ | `expansion-aggro-3-base-greedy`, `expansion-balanced-2-base-defended`, `expansion-turtle-1-base-fortified` | Expansion decisions over distinct base sites. | rush-hour-arena (only ~3 sensible base sites given map size) | Authoring a wider arena with 5+ distinguishable expansion sites would put real teeth on the trilemma. |
388
+
389
+ **Summary**: roughly 8–10 packs would benefit from a custom map, but
390
+ all are currently *functional* (the no-cheat bar holds). The map
391
+ mis-binding is a perception-channel polish issue, not a correctness
392
+ issue. **Priority**: author 2 custom maps that unlock several packs
393
+ each:
394
+ 1. `bridges-arena` — would replace `water_rect` overlay on
395
+ `def-bridge-chokepoint` and could host `combat-naval-amphibious-landing`.
396
+ 2. `chokepoint-arena` — narrow corridor for `combat-hold-chokepoint`,
397
+ `def-with-ambush`, `mfb-supply-line-link-between-bases`.
398
+
399
+ ---
400
+
401
+ ## Section 6 — Recommended actions (prioritized)
402
+
403
+ ### P0 — Hard defects (gates the no-cheat headline number)
404
+
405
+ 1. **Fix `mid-economy-under-fire`** — currently the only stall-WIN
406
+ defect among non-exempt packs. Either:
407
+ - (a) Remove the 3 starter harvesters; require the model to issue
408
+ `Command.harvest(...)` on a fresh harvester (the load-bearing
409
+ capability), OR
410
+ - (b) Upgrade the raider to a force that out-attritions an idle
411
+ defense ring (e.g. 3× `1tnk` + 2× `e3` over 60 turns), so a
412
+ stall play loses harvesters and the `harv,2` clause fails.
413
+
414
+ 2. **Fix `combat-naval-shore-strike`** — set the destroyer `stance` to
415
+ `0` (HoldFire) so the agent must issue an explicit `attack_unit`
416
+ order. Verify the intended capability (manual cross-shore attack)
417
+ still wins; stall now loses by deadline.
418
+
419
+ 3. **Fix `spec-spy-infiltrate`** — add `{not: own_units_gte: 1}` (or
420
+ `{not: unit_type_count_gte: {type: spy, n: 1}}`) to the
421
+ `fail_condition.any_of` so a spy-wipe is a real LOSS, not a
422
+ DRAW. Audit the easy / medium tiers for the same defect.
423
+
424
+ 4. **Delete or `_archive/` `adversarial-siege` and `adversarial-skirmish`** —
425
+ both already quarantined; deletion removes the only two
426
+ load-time-crashing packs.
427
+
428
+ 5. **Re-probe `def-bridge-chokepoint` over seeds 1–4 in clean processes**
429
+ — confirm the one-off DRAW observation was a transient (subsequent
430
+ re-runs gave LOSS consistently); if the DRAW recurs, add
431
+ `{not: own_units_gte: 1}` to its `fail_condition.any_of`.
432
+
433
+ ### P1 — Coverage gaps (new packs to author)
434
+
435
+ In priority order from §4: `combat-base-trade-race`,
436
+ `mid-concede-forward-rebuild-rear`, `mid-economy-rebuild-harvester-line`,
437
+ `mfb-three-front-rotation`, `mcv-deploy-emergency-relocation`,
438
+ `lh-credit-only-bait-window`, `perception-freshness-image-advantage`,
439
+ `combat-naval-amphibious-landing`, and per-specialist target-priority
440
+ packs (`spec-engineer-priority-targets`, `spec-tanya-priority-targets`).
441
+
442
+ ### P2 — Duplicate consolidation
443
+
444
+ 1. Merge / delete `artofwar-decoy-sacrifice` into
445
+ `artofwar-lure-the-tiger`.
446
+ 2. Merge `proc-tool-use-with-distractor` + `proc-tool-use-multi-distractor`
447
+ into a single pack with two tiers.
448
+ 3. Re-read `combat-kite-and-pull` vs `combat-kite-jeep-vs-tank` and
449
+ either merge or sharpen the asymmetry-of-units distinction in the
450
+ second pack's briefing.
451
+
452
+ ### P3 — Map / catalog polish
453
+
454
+ 1. Author `data/maps/bridges-arena.oramap` and re-bind
455
+ `def-bridge-chokepoint` + (future) `combat-naval-amphibious-landing`.
456
+ 2. Author `data/maps/chokepoint-arena.oramap` and re-bind
457
+ `def-with-ambush`, `combat-hold-chokepoint`,
458
+ `mfb-supply-line-link-between-bases`.
459
+ 3. Promote the four "defensive topology" packs
460
+ (`build-defensive-tower-cluster`, `build-defensive-tower-line`,
461
+ `build-defensive-skirt-corners`, `def-in-depth-vs-single`) into a
462
+ single grouped eval-cell suite so they score one "topology-IQ"
463
+ metric.
464
+
465
+ ---
466
+
467
+ ## Appendix — Probe methodology
468
+
469
+ Each pack was probed by:
470
+
471
+ ```python
472
+ import openra_train # builds the engine; required for env.reset
473
+ from openra_bench.scenarios.loader import compile_level, load_pack
474
+ from openra_bench.eval_core import run_level
475
+
476
+ pack = load_pack(pack_yaml_path)
477
+ c = compile_level(pack, "hard")
478
+ ep = run_level(c, lambda rs, Command: [Command.observe()], seed=1)
479
+ # ep.outcome ∈ {"win", "loss", "draw"}; ep.signals.game_tick = final tick.
480
+ ```
481
+
482
+ The driver (`/tmp/stall_probe_driver.py`) invoked one subprocess per
483
+ pack so a Rust engine panic in any one pack did not abort the run.
484
+ 189 packs probed in 48 seconds wall-time (Apple M-series).
485
+
486
+ Hard tier seed=1 only. A full audit would extend to seeds 1–4 (the
487
+ documented "hard seed" range in CLAUDE.md) and to easy/medium tiers,
488
+ but seed-1 hard is the highest-pressure cell per pack — defects
489
+ visible on any tier are visible here.
490
+
491
+ Static profile was extracted from the compiled `CompiledLevel`
492
+ (authoritative — the engine sees the merged scenario, not the raw
493
+ YAML).
494
+
495
+ Raw probe results JSON: `/tmp/stall_probe_results.json` (regeneratable
496
+ by re-running `/tmp/stall_probe_driver.py`).
data/maps/confined-aisle-64x40.oramap CHANGED
Binary files a/data/maps/confined-aisle-64x40.oramap and b/data/maps/confined-aisle-64x40.oramap differ
 
data/maps/expansion-aggro-3-base-greedy-arena.oramap CHANGED
Binary files a/data/maps/expansion-aggro-3-base-greedy-arena.oramap and b/data/maps/expansion-aggro-3-base-greedy-arena.oramap differ
 
data/maps/gen-arena-31f6567b.oramap CHANGED
Binary files a/data/maps/gen-arena-31f6567b.oramap and b/data/maps/gen-arena-31f6567b.oramap differ
 
data/maps/gen-arena-498d46e9.oramap CHANGED
Binary files a/data/maps/gen-arena-498d46e9.oramap and b/data/maps/gen-arena-498d46e9.oramap differ
 
data/maps/lh-defense-tech-second-base-arena.oramap CHANGED
Binary files a/data/maps/lh-defense-tech-second-base-arena.oramap and b/data/maps/lh-defense-tech-second-base-arena.oramap differ
 
data/maps/mcv-deploy-third-base-arena.oramap CHANGED
Binary files a/data/maps/mcv-deploy-third-base-arena.oramap and b/data/maps/mcv-deploy-third-base-arena.oramap differ
 
data/maps/mfb-base-1-defend-base-2-build-arena.oramap CHANGED
Binary files a/data/maps/mfb-base-1-defend-base-2-build-arena.oramap and b/data/maps/mfb-base-1-defend-base-2-build-arena.oramap differ
 
data/maps/mfb-mirror-base-east-west-arena.oramap CHANGED
Binary files a/data/maps/mfb-mirror-base-east-west-arena.oramap and b/data/maps/mfb-mirror-base-east-west-arena.oramap differ
 
data/maps/mfb-supply-line-corridor-arena.oramap CHANGED
Binary files a/data/maps/mfb-supply-line-corridor-arena.oramap and b/data/maps/mfb-supply-line-corridor-arena.oramap differ
 
data/maps/mfb-third-base-against-clock-arena.oramap CHANGED
Binary files a/data/maps/mfb-third-base-against-clock-arena.oramap and b/data/maps/mfb-third-base-against-clock-arena.oramap differ
 
data/maps/mfb-two-base-simultaneous-arena.oramap CHANGED
Binary files a/data/maps/mfb-two-base-simultaneous-arena.oramap and b/data/maps/mfb-two-base-simultaneous-arena.oramap differ
 
data/maps/mid-concede-arena.oramap CHANGED
Binary files a/data/maps/mid-concede-arena.oramap and b/data/maps/mid-concede-arena.oramap differ
 
data/maps/naval-arena-64x40.oramap CHANGED
Binary files a/data/maps/naval-arena-64x40.oramap and b/data/maps/naval-arena-64x40.oramap differ
 
data/maps/scout-arena.oramap CHANGED
Binary files a/data/maps/scout-arena.oramap and b/data/maps/scout-arena.oramap differ
 
openra_bench/agent.py CHANGED
@@ -616,6 +616,16 @@ class ModelAgent:
616
  )
617
  self.history: list[dict] = [{"role": "system", "content": sys_content}]
618
  self.stats = {"turns": 0, "tool_calls": 0, "empty_replies": 0}
 
 
 
 
 
 
 
 
 
 
619
  # Controller contract (openra_bench/controller.py): a ModelAgent
620
  # IS a Controller — it exposes `name`, `reset`, `act` so the
621
  # eval loop, the 1v1 harness, and the human-labeling harness can
@@ -768,12 +778,44 @@ class ModelAgent:
768
 
769
  def agent_fn(self, render_state: dict, Command: Any) -> list:
770
  self.stats["turns"] += 1
771
- self.history.append(self._user_message(render_state))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  self._strip_old_images(self.history)
773
  wire = self._window(
774
  self.history, getattr(self.cfg, "max_history_turns", 16)
775
  )
776
  reply = self.provider.complete(wire, self.tools)
 
 
 
 
 
 
 
 
 
 
 
 
 
777
  self.history.append(
778
  {
779
  "role": "assistant",
 
616
  )
617
  self.history: list[dict] = [{"role": "system", "content": sys_content}]
618
  self.stats = {"turns": 0, "tool_calls": 0, "empty_replies": 0}
619
+ # Audit-format capture (FullPlayback). When `audit_capture` is
620
+ # True the agent stores the per-turn briefing, the provider's
621
+ # literal request/response, and exposes the system prompt so
622
+ # the audit JSONL line for the turn carries everything.
623
+ # Default off — zero overhead for normal runs.
624
+ self.audit_capture: bool = False
625
+ self.last_briefing: str = ""
626
+ self.last_request: dict | None = None
627
+ self.last_response: dict | None = None
628
+ self.system_prompt: str = sys_content
629
  # Controller contract (openra_bench/controller.py): a ModelAgent
630
  # IS a Controller — it exposes `name`, `reset`, `act` so the
631
  # eval loop, the 1v1 harness, and the human-labeling harness can
 
778
 
779
  def agent_fn(self, render_state: dict, Command: Any) -> list:
780
  self.stats["turns"] += 1
781
+ user_msg = self._user_message(render_state)
782
+ if self.audit_capture:
783
+ # Plain-text briefing capture (image-primary turns carry a
784
+ # list `content`; the text part is the briefing). FullPlayback
785
+ # writes this as the human-readable `briefing` field so the
786
+ # audit JSONL records exactly what the model read.
787
+ c = user_msg.get("content")
788
+ if isinstance(c, str):
789
+ self.last_briefing = c
790
+ elif isinstance(c, list):
791
+ self.last_briefing = "\n".join(
792
+ p.get("text", "") for p in c
793
+ if isinstance(p, dict) and p.get("type") == "text"
794
+ )
795
+ # Enable provider-side audit hook for this turn (drain on
796
+ # return). Lazily install the list — providers without the
797
+ # `request_log` attr (e.g. Bedrock stub) silently skip.
798
+ if hasattr(self.provider, "request_log"):
799
+ self.provider.request_log = []
800
+ self.history.append(user_msg)
801
  self._strip_old_images(self.history)
802
  wire = self._window(
803
  self.history, getattr(self.cfg, "max_history_turns", 16)
804
  )
805
  reply = self.provider.complete(wire, self.tools)
806
+ if self.audit_capture and hasattr(self.provider, "request_log"):
807
+ log = self.provider.request_log or []
808
+ # One model call per turn (no internal retries here — those
809
+ # are surfaced as a single call with the eventual response);
810
+ # take the LAST entry to be safe.
811
+ if log:
812
+ self.last_request = log[-1].get("request")
813
+ self.last_response = log[-1].get("response")
814
+ else:
815
+ self.last_request = None
816
+ self.last_response = None
817
+ # Drain so the next turn starts clean.
818
+ self.provider.request_log = []
819
  self.history.append(
820
  {
821
  "role": "assistant",
openra_bench/eval_core.py CHANGED
@@ -230,6 +230,7 @@ def run_level(
230
  agent_fn: "AgentFn | Controller" = scripted_explore_agent,
231
  seed: int = 0,
232
  playback=None,
 
233
  ) -> EpisodeResult:
234
  """Run one scenario-pack level, scoring against its declarative
235
  win/fail conditions (checked every turn). Outcome maps to the
@@ -272,6 +273,15 @@ def run_level(
272
  # model actually saw (same vendored _minimap_v2, accumulating).
273
  _pb_explored: set = set()
274
  _pb_terrain = None
 
 
 
 
 
 
 
 
 
275
  # Interrupt-driven mode (step 4): if the scenario enabled any
276
  # interrupt signals, advance with step_until_event so the agent
277
  # is re-prompted (debriefed) the moment an event fires
@@ -356,6 +366,57 @@ def run_level(
356
  interrupt=interrupt,
357
  goal=turn_goal(compiled.win_condition, ctx),
358
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  trace.append(
360
  {
361
  "turn": turns,
@@ -454,8 +515,43 @@ def run_level(
454
  },
455
  }
456
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
457
  return result
458
  finally:
 
 
 
 
 
 
 
 
 
 
459
  pool.release(env)
460
  pool.shutdown()
461
  Path(tmp_path).unlink(missing_ok=True)
 
230
  agent_fn: "AgentFn | Controller" = scripted_explore_agent,
231
  seed: int = 0,
232
  playback=None,
233
+ full_playback=None,
234
  ) -> EpisodeResult:
235
  """Run one scenario-pack level, scoring against its declarative
236
  win/fail conditions (checked every turn). Outcome maps to the
 
273
  # model actually saw (same vendored _minimap_v2, accumulating).
274
  _pb_explored: set = set()
275
  _pb_terrain = None
276
+ # Audit-capture wiring: when a FullPlayback is attached, surface
277
+ # the underlying ModelAgent (if any) and flip on `audit_capture`
278
+ # so per-turn briefing / wire request+response are stashed for
279
+ # the audit JSONL.
280
+ _audit_agent = (
281
+ introspection_source(controller) if full_playback is not None else None
282
+ )
283
+ if _audit_agent is not None and hasattr(_audit_agent, "audit_capture"):
284
+ _audit_agent.audit_capture = True
285
  # Interrupt-driven mode (step 4): if the scenario enabled any
286
  # interrupt signals, advance with step_until_event so the agent
287
  # is re-prompted (debriefed) the moment an event fires
 
366
  interrupt=interrupt,
367
  goal=turn_goal(compiled.win_condition, ctx),
368
  )
369
+ if full_playback is not None:
370
+ # Mirror the same PNG (when the legacy playback rendered
371
+ # one). Otherwise render on-demand for the audit format.
372
+ _fp_png = locals().get("_png") if playback is not None else None
373
+ if _fp_png is None:
374
+ try:
375
+ from .minimap import terrain_png_for
376
+ if _pb_terrain is None:
377
+ _pb_terrain = terrain_png_for(
378
+ compiled.scenario.base_map
379
+ )
380
+ from .prompt_v2 import minimap_b64 as _v2_mm
381
+ _fp_png = _v2_mm(
382
+ rs, _pb_terrain, _pb_explored,
383
+ constant_colors=compiled.level in ("easy", "medium"),
384
+ )
385
+ if _fp_png is None:
386
+ from .agent import _render_minimap_b64
387
+ _fp_png = _render_minimap_b64(rs, _pb_terrain)
388
+ except Exception: # noqa: BLE001 — audit never breaks a run
389
+ _fp_png = None
390
+ try:
391
+ full_playback.record_turn(
392
+ turn=turns,
393
+ tick=adapter.signals.game_tick,
394
+ obs=rs,
395
+ briefing=getattr(_audit_agent, "last_briefing", "")
396
+ if _audit_agent is not None
397
+ else "",
398
+ system_prompt=getattr(_audit_agent, "system_prompt", "")
399
+ if _audit_agent is not None
400
+ else "",
401
+ model_request=getattr(_audit_agent, "last_request", None)
402
+ if _audit_agent is not None
403
+ else None,
404
+ model_response=getattr(_audit_agent, "last_response", None)
405
+ if _audit_agent is not None
406
+ else None,
407
+ commands_issued=cmds,
408
+ engine_warnings=(
409
+ info.get("warnings", [])
410
+ if isinstance(info, dict)
411
+ else []
412
+ ),
413
+ signals=adapter.signals,
414
+ minimap_png_b64=_fp_png,
415
+ done=bool(done),
416
+ interrupt=interrupt,
417
+ )
418
+ except Exception: # noqa: BLE001
419
+ pass
420
  trace.append(
421
  {
422
  "turn": turns,
 
515
  },
516
  }
517
  )
518
+ if full_playback is not None:
519
+ try:
520
+ full_playback.finalize(
521
+ outcome=outcome,
522
+ final_obs=final_rs,
523
+ manifest_extra={
524
+ "scenario": result.scenario,
525
+ "pack_id": compiled.pack_id,
526
+ "level": compiled.level,
527
+ "capability": compiled.meta.capability,
528
+ "seed": seed,
529
+ "outcome": outcome,
530
+ "turns": turns,
531
+ "max_turns": compiled.max_turns,
532
+ "actions_issued": issued,
533
+ "actions_warned": warned,
534
+ "agent_stats": getattr(
535
+ introspection_source(controller), "stats", None
536
+ ),
537
+ "objective_progress": result.objective_progress,
538
+ "reward_vector": result.reward_vector,
539
+ },
540
+ )
541
+ except Exception: # noqa: BLE001 — never break a run on I/O
542
+ pass
543
  return result
544
  finally:
545
+ # Abort the audit recorder if the loop crashed before finalize —
546
+ # leaves a `.partial` on disk for forensics; the resume scanner
547
+ # correctly sees no `.jsonl` and retries the cell.
548
+ try:
549
+ if full_playback is not None and Path(
550
+ full_playback.jsonl_path
551
+ ).exists() is False:
552
+ full_playback.abort()
553
+ except Exception: # noqa: BLE001
554
+ pass
555
  pool.release(env)
556
  pool.shutdown()
557
  Path(tmp_path).unlink(missing_ok=True)
openra_bench/full_playback.py ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Audit-ready per-cell data capture (Phase 4 paper-collection format).
2
+
3
+ This is the **audit format** used by `scripts/collect_eval_data.py`. It
4
+ is additive over `openra_bench.playback.Playback`: that one stays the
5
+ inspect-by-human format (legible per-turn record, terse signals); this
6
+ one captures EVERYTHING needed to forensically replay or re-score a
7
+ run after the fact — full obs (including `_raw` and `spatial`), the
8
+ exact briefing the model saw, the system prompt, the literal HTTP
9
+ request body sent to the provider, the literal response (content +
10
+ tool_calls + finish_reason + usage), engine warnings, and a `terminal`
11
+ block on the final turn (outcome, final_obs, wall-clock, tokens).
12
+
13
+ Layout: one JSONL per (model, pack, level, seed, fog_mode) cell at
14
+
15
+ <root>/<pack>__<level>__seed<N>__<fog_mode>.jsonl
16
+
17
+ PNG minimaps go alongside in a sibling dir of the same stem:
18
+
19
+ <root>/<pack>__<level>__seed<N>__<fog_mode>/turn_<N>.png
20
+
21
+ The JSONL line for a turn refs the PNG by relative path (relative to
22
+ the JSONL file's parent). A `terminal:` field on the final line marks
23
+ the episode complete — `scripts/collect_eval_data.py --resume` uses
24
+ that marker to skip cells that already finished cleanly.
25
+
26
+ Why a separate writer (vs extending Playback): the legacy Playback
27
+ format is what `scripts/view_playback.py` reads and what `run_eval.py`
28
+ emits today; rewriting it would invalidate every existing playback
29
+ dir and break the viewer for ~1000 historical episodes. FullPlayback
30
+ runs ALONGSIDE Playback when both are configured; either can be
31
+ disabled independently.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ import base64
37
+ import json
38
+ import time
39
+ from dataclasses import asdict, is_dataclass
40
+ from pathlib import Path
41
+ from typing import Any
42
+
43
+
44
+ def _jsonable(o: Any) -> Any:
45
+ """Recursive serializer matching playback._jsonable but tolerant of
46
+ bytes (base64'd) and tuples (preserved as lists). Bytes are wrapped
47
+ as `{"__b64__": <base64-string>}` so a round-trip recovers them."""
48
+ if isinstance(o, bytes):
49
+ return {"__b64__": base64.b64encode(o).decode("ascii")}
50
+ if is_dataclass(o) and not isinstance(o, type):
51
+ return _jsonable(asdict(o))
52
+ if isinstance(o, dict):
53
+ return {str(k): _jsonable(v) for k, v in o.items()}
54
+ if isinstance(o, (list, tuple)):
55
+ return [_jsonable(v) for v in o]
56
+ if isinstance(o, set):
57
+ return sorted(_jsonable(v) for v in o)
58
+ if isinstance(o, (str, int, float, bool)) or o is None:
59
+ return o
60
+ return repr(o)
61
+
62
+
63
+ def _safe(s: str) -> str:
64
+ """Slug a model id / pack id for path use: keep alnum, `.`, `_`, `-`;
65
+ everything else (especially `/`) becomes `_`."""
66
+ out = []
67
+ for ch in s:
68
+ out.append(ch if (ch.isalnum() or ch in "._-") else "_")
69
+ return "".join(out)
70
+
71
+
72
+ def cell_stem(pack_id: str, level: str, seed: int, fog_mode: str) -> str:
73
+ return f"{_safe(pack_id)}__{_safe(level)}__seed{int(seed)}__{_safe(fog_mode)}"
74
+
75
+
76
+ class FullPlayback:
77
+ """Per-cell audit-ready JSONL + PNG writer.
78
+
79
+ Construct one per (pack, level, seed, fog_mode) cell. Call
80
+ `record_turn(...)` once per model turn (mirroring how the legacy
81
+ Playback is driven from `run_level`); call `finalize(...)` on
82
+ episode end (it emits the `terminal:` field merged into the last
83
+ turn line, NOT a new line — so the file is one-line-per-turn and
84
+ the terminal marker is unambiguous).
85
+
86
+ Concurrency: one cell == one subprocess in the collector, so no
87
+ cross-cell locking is needed. Within a process, this class is NOT
88
+ thread-safe (the eval loop is single-threaded per episode).
89
+ """
90
+
91
+ def __init__(
92
+ self,
93
+ root: str | Path,
94
+ pack_id: str,
95
+ level: str,
96
+ seed: int,
97
+ fog_mode: str,
98
+ ):
99
+ self.root = Path(root)
100
+ self.root.mkdir(parents=True, exist_ok=True)
101
+ self.stem = cell_stem(pack_id, level, seed, fog_mode)
102
+ self.jsonl_path = self.root / f"{self.stem}.jsonl"
103
+ self.png_dir = self.root / self.stem
104
+ self.png_dir.mkdir(parents=True, exist_ok=True)
105
+ # Use a sidecar tmp until finalize, then move atomically over the
106
+ # final path so a `--resume` scan only sees complete files.
107
+ self._tmp_path = self.root / f"{self.stem}.jsonl.partial"
108
+ self._fh = open(self._tmp_path, "w")
109
+ self.pack_id = pack_id
110
+ self.level = level
111
+ self.seed = seed
112
+ self.fog_mode = fog_mode
113
+ self._t0 = time.time()
114
+ # Buffer the last turn line so finalize() can merge `terminal:`
115
+ # into it instead of writing a fresh trailing line (one line ==
116
+ # one turn; the terminal block is a field on the last line).
117
+ self._last_rec: dict | None = None
118
+ # Token accounting across the whole episode (provider-reported
119
+ # usage from each model call); the totals land in `terminal:`.
120
+ self._tokens_in = 0
121
+ self._tokens_out = 0
122
+ # First turn carries the system_prompt; subsequent turns repeat
123
+ # it as `null` to keep the per-turn record uniform but small.
124
+ self._sysp_written = False
125
+
126
+ # ── per-turn ──────────────────────────────────────────────────────
127
+
128
+ def record_turn(
129
+ self,
130
+ *,
131
+ turn: int,
132
+ tick: int | None,
133
+ obs: dict,
134
+ briefing: str,
135
+ system_prompt: str,
136
+ model_request: dict | None,
137
+ model_response: dict | None,
138
+ commands_issued: list,
139
+ engine_warnings: list[str],
140
+ signals: Any,
141
+ minimap_png_b64: str | None = None,
142
+ done: bool = False,
143
+ interrupt: str | None = None,
144
+ extra: dict | None = None,
145
+ ) -> None:
146
+ # Flush the previously-buffered line now that we know we're past
147
+ # it (a new turn started, so the prior turn was NOT terminal).
148
+ if self._last_rec is not None:
149
+ self._fh.write(json.dumps(_jsonable(self._last_rec)) + "\n")
150
+ self._fh.flush()
151
+ self._last_rec = None
152
+
153
+ # Track per-call tokens (provider returns them in response.usage)
154
+ u = (model_response or {}).get("usage") or {}
155
+ self._tokens_in += int(u.get("prompt_tokens", 0) or 0)
156
+ self._tokens_out += int(u.get("completion_tokens", 0) or 0)
157
+
158
+ png_rel: str | None = None
159
+ if minimap_png_b64:
160
+ try:
161
+ png_path = self.png_dir / f"turn_{int(turn):03d}.png"
162
+ png_path.write_bytes(base64.b64decode(minimap_png_b64))
163
+ # Relative to the JSONL's parent dir so the file is
164
+ # portable (you can move the run dir without breaking
165
+ # refs).
166
+ png_rel = f"{self.stem}/turn_{int(turn):03d}.png"
167
+ except Exception: # noqa: BLE001 — never break a run on I/O
168
+ png_rel = None
169
+
170
+ rec: dict = {
171
+ "turn": int(turn),
172
+ "tick": tick,
173
+ "interrupt": interrupt,
174
+ "obs": _jsonable(obs),
175
+ "briefing": briefing,
176
+ "system_prompt": system_prompt if not self._sysp_written else None,
177
+ "model_request": _jsonable(model_request) if model_request else None,
178
+ "model_response": _jsonable(model_response) if model_response else None,
179
+ "commands_issued": [repr(c) for c in commands_issued],
180
+ "engine_warnings": list(engine_warnings or []),
181
+ "signals": _jsonable(_signal_snapshot(signals)),
182
+ "minimap_png": png_rel,
183
+ "done": bool(done),
184
+ }
185
+ if extra:
186
+ rec["extra"] = _jsonable(extra)
187
+ self._sysp_written = True
188
+ # Buffer; finalize() merges `terminal:` into this line.
189
+ self._last_rec = rec
190
+
191
+ def finalize(
192
+ self,
193
+ *,
194
+ outcome: str,
195
+ final_obs: dict | None,
196
+ manifest_extra: dict | None = None,
197
+ ) -> None:
198
+ """Stamp the buffered last line with the `terminal:` block,
199
+ flush, and atomically move the partial file over the final
200
+ path so `--resume` sees a fully-complete cell."""
201
+ wall = round(time.time() - self._t0, 3)
202
+ terminal = {
203
+ "outcome": outcome,
204
+ "final_obs": _jsonable(final_obs) if final_obs is not None else None,
205
+ "wall_clock_seconds": wall,
206
+ "total_tokens_in": int(self._tokens_in),
207
+ "total_tokens_out": int(self._tokens_out),
208
+ }
209
+ if manifest_extra:
210
+ terminal["manifest"] = _jsonable(manifest_extra)
211
+
212
+ if self._last_rec is None:
213
+ # Episode produced zero turns (engine crashed on reset, say).
214
+ # Emit a synthetic terminal-only record so `--resume` can
215
+ # still detect "this cell was attempted and completed".
216
+ self._last_rec = {
217
+ "turn": 0,
218
+ "tick": None,
219
+ "interrupt": None,
220
+ "obs": None,
221
+ "briefing": "",
222
+ "system_prompt": None,
223
+ "model_request": None,
224
+ "model_response": None,
225
+ "commands_issued": [],
226
+ "engine_warnings": [],
227
+ "signals": {},
228
+ "minimap_png": None,
229
+ "done": True,
230
+ }
231
+ self._last_rec["terminal"] = terminal
232
+ self._fh.write(json.dumps(_jsonable(self._last_rec)) + "\n")
233
+ self._fh.flush()
234
+ try:
235
+ self._fh.close()
236
+ except Exception: # noqa: BLE001
237
+ pass
238
+ try:
239
+ self._tmp_path.replace(self.jsonl_path)
240
+ except Exception: # noqa: BLE001 — last-ditch fallback
241
+ try:
242
+ self.jsonl_path.write_text(self._tmp_path.read_text())
243
+ except Exception: # noqa: BLE001
244
+ pass
245
+
246
+ def abort(self) -> None:
247
+ """Close without finalizing. The .partial file stays on disk so
248
+ a post-hoc diagnostic can inspect what was captured before the
249
+ crash; the final .jsonl is NOT created, so `--resume` will
250
+ correctly retry this cell on the next invocation."""
251
+ try:
252
+ if self._last_rec is not None:
253
+ self._fh.write(json.dumps(_jsonable(self._last_rec)) + "\n")
254
+ self._fh.flush()
255
+ self._fh.close()
256
+ except Exception: # noqa: BLE001
257
+ pass
258
+
259
+
260
+ def _signal_snapshot(signals: Any) -> dict:
261
+ """Pull every primitive scalar / list off an EpisodeSignals (or
262
+ duck-typed shim) into a JSON-safe dict. Defensive: a missing attr
263
+ is just absent. Mirrors the existing playback shape so downstream
264
+ tools recognise the same field names, but adds a few signals
265
+ (resources, harvesters, tool_violations) that the existing playback
266
+ omits."""
267
+ fields = (
268
+ "game_tick",
269
+ "cash",
270
+ "resources",
271
+ "resource_capacity",
272
+ "power_provided",
273
+ "power_drained",
274
+ "harvesters",
275
+ "explored_percent",
276
+ "units_killed",
277
+ "units_lost",
278
+ "enemies_seen_ids",
279
+ "enemy_buildings_seen_ids",
280
+ "production_items",
281
+ "tool_violations",
282
+ "outcome",
283
+ )
284
+ out: dict[str, Any] = {}
285
+ for f in fields:
286
+ if not hasattr(signals, f):
287
+ continue
288
+ v = getattr(signals, f)
289
+ if isinstance(v, (set, frozenset)):
290
+ v = sorted(_jsonable(x) for x in v)
291
+ out[f] = _jsonable(v)
292
+ # Convenience: counts + computed signals downstream uses heavily.
293
+ if "enemies_seen_ids" in out and isinstance(out["enemies_seen_ids"], list):
294
+ out["enemies_seen_count"] = len(out["enemies_seen_ids"])
295
+ if "enemy_buildings_seen_ids" in out and isinstance(
296
+ out["enemy_buildings_seen_ids"], list
297
+ ):
298
+ out["enemy_buildings_seen_count"] = len(out["enemy_buildings_seen_ids"])
299
+ if "cash" in out and "resources" in out:
300
+ try:
301
+ out["economy_value"] = int(out["cash"]) + int(out["resources"])
302
+ except (TypeError, ValueError):
303
+ pass
304
+ return out
305
+
306
+
307
+ def is_complete_cell(jsonl_path: str | Path) -> bool:
308
+ """True iff `jsonl_path` exists, is non-empty, and the LAST line
309
+ carries a `terminal:` field. The resume scanner uses this so a
310
+ crash mid-cell (partial .jsonl with no terminal) is correctly
311
+ retried, while a cleanly finished cell is correctly skipped.
312
+
313
+ Reads only the file tail (~64KB) — safe to call on thousands of
314
+ cells in a scan."""
315
+ p = Path(jsonl_path)
316
+ if not p.exists() or p.stat().st_size == 0:
317
+ return False
318
+ try:
319
+ # Tail read: open, seek near end, find the last newline-bounded
320
+ # line. JSONL turn records are small (~few KB at most), so 64KB
321
+ # is plenty for the last line of even a long episode.
322
+ with open(p, "rb") as fh:
323
+ fh.seek(0, 2)
324
+ size = fh.tell()
325
+ tail_n = min(size, 65536)
326
+ fh.seek(size - tail_n)
327
+ tail = fh.read().decode("utf-8", errors="replace")
328
+ last = tail.strip().splitlines()[-1] if tail.strip() else ""
329
+ if not last:
330
+ return False
331
+ rec = json.loads(last)
332
+ except (OSError, ValueError):
333
+ return False
334
+ return isinstance(rec, dict) and "terminal" in rec
openra_bench/playback_view.py CHANGED
@@ -21,8 +21,14 @@ from pathlib import Path
21
 
22
  def load_episode(ep_dir: str | Path) -> dict:
23
  """Reassemble one ``seed<N>`` episode folder. Tolerant of a still-
24
- running episode (missing files become empty)."""
25
- d = Path(ep_dir)
 
 
 
 
 
 
26
  manifest = _read_json(d / "manifest.json", {})
27
  messages = _read_json(d / "messages.json", [])
28
  turns = []
@@ -40,10 +46,115 @@ def load_episode(ep_dir: str | Path) -> dict:
40
 
41
 
42
  def find_episodes(root: str | Path) -> list[Path]:
43
- """All ``.../seed<N>`` episode dirs under a playback root."""
44
- return sorted(
45
- p.parent for p in Path(root).glob("**/manifest.json")
46
- ) or sorted(Path(root).glob("**/seed*"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
 
49
  def _read_json(p: Path, default):
 
21
 
22
  def load_episode(ep_dir: str | Path) -> dict:
23
  """Reassemble one ``seed<N>`` episode folder. Tolerant of a still-
24
+ running episode (missing files become empty).
25
+
26
+ Accepts EITHER a legacy seed dir OR a FullPlayback audit JSONL file;
27
+ a path ending in `.jsonl` dispatches to `load_audit_jsonl`."""
28
+ p = Path(ep_dir)
29
+ if p.is_file() and p.suffix == ".jsonl":
30
+ return load_audit_jsonl(p)
31
+ d = p
32
  manifest = _read_json(d / "manifest.json", {})
33
  messages = _read_json(d / "messages.json", [])
34
  turns = []
 
46
 
47
 
48
  def find_episodes(root: str | Path) -> list[Path]:
49
+ """All episode targets under a playback root.
50
+
51
+ Returns a mix of two shapes the viewer transparently handles:
52
+ * Legacy `.../seed<N>/` dirs (manifest.json + turns.jsonl + …)
53
+ * Audit-format `.../<stem>.jsonl` files written by FullPlayback —
54
+ the loader detects a `.jsonl` path and translates it on the fly.
55
+ """
56
+ root = Path(root)
57
+ legacy = sorted(
58
+ p.parent for p in root.glob("**/manifest.json")
59
+ ) or sorted(root.glob("**/seed*"))
60
+ # Audit-format cells: `<root>/<run_dir>/<pack>__<level>__seedN__fog.jsonl`.
61
+ # Skip the `.partial` half-runs and any sidecar files (start with `_`).
62
+ audit = sorted(
63
+ p for p in root.glob("**/*.jsonl")
64
+ if not p.name.startswith("_")
65
+ and "__seed" in p.name
66
+ and not p.name.endswith(".partial")
67
+ )
68
+ return legacy + audit
69
+
70
+
71
+ def load_audit_jsonl(jsonl_path: str | Path) -> dict:
72
+ """Translate a FullPlayback audit JSONL into the same `{dir, manifest,
73
+ turns, messages}` dict shape `load_episode` returns, so the same
74
+ viewer code (`render_streamlit` / downstream loaders) can consume
75
+ either format transparently.
76
+
77
+ - `manifest` is reconstructed from the terminal record's
78
+ `terminal.manifest` block (+ a few top-level fields), preserving
79
+ compatibility with viewers that expect `outcome`, `model`, etc.
80
+ - `turns` mirror the legacy per-turn shape (`turn`, `tick`,
81
+ `commands`, `signals`, `goal`, `minimap_png` path).
82
+ - `messages` is synthesized from the system_prompt + each turn's
83
+ briefing/response, so the viewer's transcript pane still works.
84
+ """
85
+ p = Path(jsonl_path)
86
+ recs: list[dict] = []
87
+ for line in p.read_text().splitlines():
88
+ line = line.strip()
89
+ if not line:
90
+ continue
91
+ try:
92
+ recs.append(json.loads(line))
93
+ except json.JSONDecodeError:
94
+ continue
95
+ if not recs:
96
+ return {"dir": str(p.parent), "manifest": {}, "turns": [], "messages": []}
97
+ term_block = (recs[-1].get("terminal") or {}) if recs else {}
98
+ manifest: dict = dict(term_block.get("manifest") or {})
99
+ # Surface the audit-only totals at the manifest level so the viewer's
100
+ # "outcome / wall / tokens" header line has them.
101
+ manifest.setdefault("outcome", term_block.get("outcome", "?"))
102
+ manifest.setdefault("wall_clock_seconds", term_block.get("wall_clock_seconds"))
103
+ manifest["total_tokens_in"] = term_block.get("total_tokens_in", 0)
104
+ manifest["total_tokens_out"] = term_block.get("total_tokens_out", 0)
105
+ turns = []
106
+ messages: list[dict] = []
107
+ # System prompt lives on the first turn only.
108
+ sysp = recs[0].get("system_prompt")
109
+ if sysp:
110
+ messages.append({"role": "system", "content": sysp})
111
+ for r in recs:
112
+ # Turn record — translate to legacy keys the viewer recognises.
113
+ png_rel = r.get("minimap_png")
114
+ png_abs = str(p.parent / png_rel) if png_rel else None
115
+ sig = r.get("signals") or {}
116
+ turns.append(
117
+ {
118
+ "turn": r.get("turn"),
119
+ "tick": r.get("tick"),
120
+ "interrupt": r.get("interrupt"),
121
+ "commands": r.get("commands_issued") or [],
122
+ "ascii_minimap": (r.get("obs") or {}).get("minimap", ""),
123
+ "signals": sig,
124
+ "units": (r.get("obs") or {}).get("units_summary", []),
125
+ "enemies": (r.get("obs") or {}).get("enemy_summary", []),
126
+ "goal": {}, # not captured by FullPlayback
127
+ "minimap_png": png_abs,
128
+ }
129
+ )
130
+ if r.get("briefing"):
131
+ messages.append({"role": "user", "content": r["briefing"]})
132
+ resp = r.get("model_response") or {}
133
+ if resp.get("text") or resp.get("tool_calls"):
134
+ messages.append(
135
+ {
136
+ "role": "assistant",
137
+ "content": resp.get("text") or "",
138
+ "tool_calls": [
139
+ {
140
+ "id": f"c{i}",
141
+ "type": "function",
142
+ "function": {
143
+ "name": (tc or {}).get("name", ""),
144
+ "arguments": (tc or {}).get("arguments", {}),
145
+ },
146
+ }
147
+ for i, tc in enumerate(resp.get("tool_calls") or [])
148
+ ],
149
+ "reasoning": resp.get("reasoning", ""),
150
+ }
151
+ )
152
+ return {
153
+ "dir": str(p.parent),
154
+ "manifest": manifest,
155
+ "turns": turns,
156
+ "messages": messages,
157
+ }
158
 
159
 
160
  def _read_json(p: Path, default):
openra_bench/providers.py CHANGED
@@ -145,6 +145,11 @@ class OpenAICompatibleProvider(ChatProvider):
145
  base=cfg.retry_base_s,
146
  cap=cfg.retry_cap_s,
147
  )
 
 
 
 
 
148
 
149
  @property
150
  def cost_meter(self):
@@ -222,6 +227,34 @@ class OpenAICompatibleProvider(ChatProvider):
222
  u = reply.usage or {}
223
  self._cost.add(u.get("prompt_tokens", 0), u.get("completion_tokens", 0))
224
  self._cost.check() # raises BudgetExceeded → evaluate finalizes
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  return reply
226
 
227
  def _stream_once(self, url, headers, body) -> ChatReply:
 
145
  base=cfg.retry_base_s,
146
  cap=cfg.retry_cap_s,
147
  )
148
+ # Audit hook: when set (a list), every successful complete()
149
+ # appends a dict {"request": <body>, "response": <raw>} so the
150
+ # FullPlayback recorder can capture the literal wire payloads.
151
+ # Drained by the caller after each turn. None disables capture.
152
+ self.request_log: list[dict] | None = None
153
 
154
  @property
155
  def cost_meter(self):
 
227
  u = reply.usage or {}
228
  self._cost.add(u.get("prompt_tokens", 0), u.get("completion_tokens", 0))
229
  self._cost.check() # raises BudgetExceeded → evaluate finalizes
230
+ if self.request_log is not None:
231
+ # Audit capture: redact the bearer header (the body is the
232
+ # interesting part) and store the literal request + raw
233
+ # response side-by-side. FullPlayback drains after each turn.
234
+ try:
235
+ self.request_log.append(
236
+ {
237
+ "request": {
238
+ "url": url,
239
+ "body": body,
240
+ },
241
+ "response": {
242
+ "raw": reply.raw,
243
+ "text": reply.text,
244
+ "tool_calls": reply.tool_calls,
245
+ "reasoning": reply.reasoning,
246
+ "usage": dict(reply.usage or {}),
247
+ "finish_reason": (
248
+ (reply.raw.get("choices") or [{}])[0]
249
+ .get("finish_reason")
250
+ if isinstance(reply.raw, dict)
251
+ else None
252
+ ),
253
+ },
254
+ }
255
+ )
256
+ except Exception: # noqa: BLE001 — audit must never break a run
257
+ pass
258
  return reply
259
 
260
  def _stream_once(self, url, headers, body) -> ChatReply:
openra_bench/run_eval.py CHANGED
@@ -166,6 +166,7 @@ def evaluate(
166
  handoff_k: int = 3,
167
  handoff_bank: str | Path | None = None,
168
  repeats: int = 1,
 
169
  ) -> dict:
170
  """Run packs×levels×seeds. If `held_out_seeds` is given, those are
171
  run too and tagged split='held_out'; the report adds
@@ -305,10 +306,18 @@ def evaluate(
305
  for c in pack.configs
306
  ]
307
  else:
308
- unit_iter = [
309
- (compile_level(pack, lv), f"{pack.meta.id}:{lv}")
310
- for lv in levels
311
- ]
 
 
 
 
 
 
 
 
312
  for compiled, cell in unit_iter:
313
  if not compiled.map_supported:
314
  skipped.append(f"{cell} (map not Rust-loadable)")
@@ -333,6 +342,30 @@ def evaluate(
333
  seed,
334
  )
335
  pb.run_id, pb.model = run_id, model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  ctrl = factory(compiled)
337
  if handoff_sweep and ":handoff-" in cell:
338
  ctrl, _hnote = _handoff_wrap(
@@ -340,7 +373,7 @@ def evaluate(
340
  )
341
  else:
342
  _hnote = ""
343
- res = run_level(compiled, ctrl, seed=seed, playback=pb)
344
  hstats = getattr(ctrl, "handoff_stats", None)
345
  if hstats is not None:
346
  hstats = dict(hstats)
@@ -685,8 +718,22 @@ def main(argv: list[str]) -> int:
685
  "'wandb/bf16' (no fallback) — premium routing off the free pool",
686
  )
687
  ap.add_argument("--fog-mode", default="vision",
688
- choices=["vision", "structured"],
689
- help="spatial channel: PNG minimap vs text fog")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
690
  ap.add_argument("--perception-sweep", action="store_true",
691
  help="run the 2x2 perception ablation: every "
692
  "pack:level expanded into vision/structured x "
@@ -757,6 +804,7 @@ def main(argv: list[str]) -> int:
757
  handoff_k=a.handoff_k,
758
  handoff_bank=a.handoff_bank,
759
  repeats=a.repeats,
 
760
  progress=lambda d, n, rec, c: print(
761
  f"[{d}/{n}] {rec['cell']}:{rec['split']}#{rec['seed']} "
762
  f"{rec['outcome']} comp={rec['composite']} "
 
166
  handoff_k: int = 3,
167
  handoff_bank: str | Path | None = None,
168
  repeats: int = 1,
169
+ full_playback_root: str | Path | None = None,
170
  ) -> dict:
171
  """Run packs×levels×seeds. If `held_out_seeds` is given, those are
172
  run too and tagged split='held_out'; the report adds
 
306
  for c in pack.configs
307
  ]
308
  else:
309
+ # Apply the global fog_mode (from ProviderConfig / CLI) so a
310
+ # single-fog run can audit cells in the `image`/`structured`/
311
+ # `-clear` channels (compiled.fog_mode defaults to vision
312
+ # without this lift, which would silently downgrade every
313
+ # cell to the canonical vision-fogged modality).
314
+ _fog = getattr(provider_cfg, "fog_mode", None) if provider_cfg else None
315
+ unit_iter = []
316
+ for lv in levels:
317
+ cl = compile_level(pack, lv)
318
+ if _fog:
319
+ cl.fog_mode = _fog
320
+ unit_iter.append((cl, f"{pack.meta.id}:{lv}"))
321
  for compiled, cell in unit_iter:
322
  if not compiled.map_supported:
323
  skipped.append(f"{cell} (map not Rust-loadable)")
 
342
  seed,
343
  )
344
  pb.run_id, pb.model = run_id, model
345
+ # Audit-format playback (FullPlayback): one JSONL per cell at the
346
+ # canonical `<pack>__<level>__seed<N>__<fog>.jsonl` path the
347
+ # paper-collection script consumes. Same first-repeat gating as
348
+ # the legacy Playback.
349
+ fpb = None
350
+ if full_playback_root is not None and rep == 0:
351
+ from .full_playback import FullPlayback
352
+
353
+ # Derive (pack_id, level, fog_mode) from the cell. For
354
+ # perception-sweep cells, the cell is `pack:level:mode`; for
355
+ # legacy/configured cells, fall back to compiled fields.
356
+ parts = cell.split(":")
357
+ _pack_id = compiled.pack_id
358
+ _level = compiled.level
359
+ _fog = getattr(compiled, "fog_mode", "vision") or "vision"
360
+ if len(parts) >= 3:
361
+ _fog = parts[-1]
362
+ fpb = FullPlayback(
363
+ Path(full_playback_root) / f"{run_id}__{_safe_model}",
364
+ pack_id=_pack_id,
365
+ level=_level,
366
+ seed=seed,
367
+ fog_mode=_fog,
368
+ )
369
  ctrl = factory(compiled)
370
  if handoff_sweep and ":handoff-" in cell:
371
  ctrl, _hnote = _handoff_wrap(
 
373
  )
374
  else:
375
  _hnote = ""
376
+ res = run_level(compiled, ctrl, seed=seed, playback=pb, full_playback=fpb)
377
  hstats = getattr(ctrl, "handoff_stats", None)
378
  if hstats is not None:
379
  hstats = dict(hstats)
 
718
  "'wandb/bf16' (no fallback) — premium routing off the free pool",
719
  )
720
  ap.add_argument("--fog-mode", default="vision",
721
+ choices=[
722
+ "vision", "vision-clear",
723
+ "structured", "structured-clear",
724
+ "image", "image-clear",
725
+ ],
726
+ help="spatial channel: PNG minimap (vision), text fog "
727
+ "(structured), or image-primary (image). `-clear` "
728
+ "variants run with no fog of war.")
729
+ ap.add_argument(
730
+ "--full-playback",
731
+ default=None,
732
+ help="audit-format playback dir: one JSONL per cell at "
733
+ "<dir>/<pack>__<level>__seed<N>__<fog>.jsonl with full obs / "
734
+ "request / response / engine warnings. Used by "
735
+ "scripts/collect_eval_data.py for paper-grade data capture.",
736
+ )
737
  ap.add_argument("--perception-sweep", action="store_true",
738
  help="run the 2x2 perception ablation: every "
739
  "pack:level expanded into vision/structured x "
 
804
  handoff_k=a.handoff_k,
805
  handoff_bank=a.handoff_bank,
806
  repeats=a.repeats,
807
+ full_playback_root=a.full_playback,
808
  progress=lambda d, n, rec, c: print(
809
  f"[{d}/{n}] {rec['cell']}:{rec['split']}#{rec['seed']} "
810
  f"{rec['outcome']} comp={rec['composite']} "
openra_bench/scenarios/packs/action-multiunit-coordination.yaml CHANGED
@@ -55,11 +55,19 @@ base:
55
 
56
  levels:
57
  easy:
 
 
 
 
 
 
 
 
58
  description: >
59
- Two regions, 6 units. Put >=2 units in EACH region (NE ~110,6 and
60
- SE ~110,33) before tick 2400. One direct parallel dispatch makes
61
- it (~17-20 turns); touring both with one column does not. Lose if
62
- the deadline passes or the force is wiped.
63
  overrides: {}
64
  win_condition:
65
  all_of:
@@ -78,14 +86,22 @@ levels:
78
  max_turns: 30
79
 
80
  medium:
 
 
 
 
 
 
 
 
 
 
 
81
  description: >
82
- Three DISPERSED regions, 8 units, attrition cap. Put >=2 units in
83
- EACH of: NE corner (115,6), bottom-left (20,36), SE corner
84
- (115,34) before tick 2800, losing <=2. The regions are in three
85
- different directions a single eastward column cannot reach the
86
- bottom-left one — so this needs genuine multi-vector dispatch,
87
- not one fanned-out march. Enemy infantry contest the two eastern
88
- lanes; the bottom-left route is clear.
89
  overrides:
90
  actors:
91
  # `stance: 0` (HoldFire) on every agent unit — see the base
@@ -128,20 +144,28 @@ levels:
128
  # feedback loop that makes coordinate-blind grounding solvable. A
129
  # hard failure attributes to spatial grounding, not extra combat.
130
  objective_coords: relative
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  description: >
132
- Three dispersed regions, 8 units, attrition cap — you are NOT
133
- given coordinates. Each target region is marked by a specific
134
- enemy building, hidden in fog until you scout it. Put >=2 units
135
- next to EACH of: the enemy Construction Yard in the NORTH-EAST,
136
- the enemy Power Plant in the SOUTH-WEST (bottom-left), and the
137
- enemy Ore Refinery in the SOUTH-EAST, before tick 2800, losing
138
- <=2. Push a group toward each direction and KEEP ADVANCING — do
139
- not stop at a guessed spot. The instant a unit spots a target
140
- building you will be interrupted and shown its exact location;
141
- then converge >=2 units on it. The three lie in three different
142
- directions; a single eastward column cannot reach the south-west
143
- one, so this needs genuine multi-vector dispatch. Enemy infantry
144
- contest the two eastern lanes; the south-west route is clear.
145
  overrides:
146
  interrupts:
147
  # Re-prompt the moment a unit sights a marker building — gives
 
55
 
56
  levels:
57
  easy:
58
+ # Original (pre-verbosity-sweep) description preserved for
59
+ # contributors. The trimmed version below removes scripted-policy
60
+ # spoilers and cell-coord dumps; load-bearing intent kept.
61
+ #
62
+ # Two regions, 6 units. Put >=2 units in EACH region (NE ~110,6 and
63
+ # SE ~110,33) before tick 2400. One direct parallel dispatch makes
64
+ # it (~17-20 turns); touring both with one column does not. Lose if
65
+ # the deadline passes or the force is wiped.
66
  description: >
67
+ Split your six tanks into two columns and put at least two units
68
+ into EACH of the north-east and south-east enemy regions within
69
+ about 27 turns. Losing the whole force, or missing the deadline,
70
+ is a loss.
71
  overrides: {}
72
  win_condition:
73
  all_of:
 
86
  max_turns: 30
87
 
88
  medium:
89
+ # Original (pre-verbosity-sweep) description preserved for
90
+ # contributors. The trimmed version below removes scripted-policy
91
+ # spoilers and cell-coord dumps; load-bearing intent kept.
92
+ #
93
+ # Three DISPERSED regions, 8 units, attrition cap. Put >=2 units in
94
+ # EACH of: NE corner (115,6), bottom-left (20,36), SE corner
95
+ # (115,34) before tick 2800, losing <=2. The regions are in three
96
+ # different directions — a single eastward column cannot reach the
97
+ # bottom-left one — so this needs genuine multi-vector dispatch,
98
+ # not one fanned-out march. Enemy infantry contest the two eastern
99
+ # lanes; the bottom-left route is clear.
100
  description: >
101
+ Three regions in three different directions (north-east, south-east,
102
+ bottom-left), 8 units. Put at least two units into EACH region
103
+ within about 31 turns, losing no more than two. Enemy infantry
104
+ contest the eastern lanes.
 
 
 
105
  overrides:
106
  actors:
107
  # `stance: 0` (HoldFire) on every agent unit — see the base
 
144
  # feedback loop that makes coordinate-blind grounding solvable. A
145
  # hard failure attributes to spatial grounding, not extra combat.
146
  objective_coords: relative
147
+ # Original (pre-verbosity-sweep) description preserved for
148
+ # contributors. The trimmed version below removes scripted-policy
149
+ # spoilers and cell-coord dumps; load-bearing intent kept.
150
+ #
151
+ # Three dispersed regions, 8 units, attrition cap — you are NOT
152
+ # given coordinates. Each target region is marked by a specific
153
+ # enemy building, hidden in fog until you scout it. Put >=2 units
154
+ # next to EACH of: the enemy Construction Yard in the NORTH-EAST,
155
+ # the enemy Power Plant in the SOUTH-WEST (bottom-left), and the
156
+ # enemy Ore Refinery in the SOUTH-EAST, before tick 2800, losing
157
+ # <=2. Push a group toward each direction and KEEP ADVANCING — do
158
+ # not stop at a guessed spot. The instant a unit spots a target
159
+ # building you will be interrupted and shown its exact location;
160
+ # then converge >=2 units on it. The three lie in three different
161
+ # directions; a single eastward column cannot reach the south-west
162
+ # one, so this needs genuine multi-vector dispatch. Enemy infantry
163
+ # contest the two eastern lanes; the south-west route is clear.
164
  description: >
165
+ No coordinates given. Push columns toward the north-east,
166
+ south-east, and south-west to scout for the enemy Construction
167
+ Yard, Ore Refinery, and Power Plant. Put two units beside each
168
+ within about 31 turns, losing no more than two.
 
 
 
 
 
 
 
 
 
169
  overrides:
170
  interrupts:
171
  # Re-prompt the moment a unit sights a marker building — gives
openra_bench/scenarios/packs/action-sequenced-execution.yaml CHANGED
@@ -46,12 +46,20 @@ levels:
46
  # ── EASY ── one column, ONE ordered 3-waypoint route, clear lanes,
47
  # generous budget. Tests pure "go in the given order, don't idle."
48
  easy:
 
 
 
 
 
 
 
 
 
49
  description: >
50
- Execute ONE given route IN ORDER: waypoint W1 (mid-south, marked
51
- by an enemy Power Plant) → W2 (north, enemy Ore Refinery) W3 /
52
- final (far SE, enemy Construction Yard). You must reach them in
53
- that order rushing straight to the final does NOT count. Keep
54
- the column moving: arrive within the time budget or you lose.
55
  overrides:
56
  actors:
57
  - {type: 2tnk, owner: agent, stance: 0, position: [5, 8], count: 2}
@@ -80,15 +88,23 @@ levels:
80
  # execution (finish route N, then route S) overruns the budget —
81
  # only a genuine two-column parallel split clears it. Attrition cap.
82
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
83
  description: >
84
- TWO given routes that must BOTH be completed, each IN ORDER.
85
- Route N: N1 (W, enemy Power Plant) N2 (mid-SOUTH, enemy Ore
86
- Refinery) N3 (far-E NORTH, enemy Construction Yard). Route S:
87
- S1 (W-south, enemy Radar Dome) S2 (mid-NORTH, enemy Power
88
- Plant) → S3 (far-E SOUTH, enemy Ore Refinery). The two routes
89
- cross in the middle: split into two columns and run them in
90
- parallel — doing one fully then the other overruns the clock.
91
- Lose ≤1 unit; arrive within budget or lose.
92
  overrides:
93
  actors:
94
  # Northern staging cluster
@@ -141,16 +157,24 @@ levels:
141
  # Seed picks the staging corner. Strict budget + attrition.
142
  hard:
143
  objective_coords: relative
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  description: >
145
- TWO given routes across a large arena, each four ordered
146
- waypoints, described ONLY by direction you are NOT given
147
- coordinates. Each waypoint is an enemy building hidden in fog:
148
- scout toward the stated direction and you will be interrupted the
149
- instant a unit sights the marker, revealing it; then move on to
150
- it and continue the route IN ORDER. Both routes must be completed
151
- (split into two columns and run them together). Keep advancing —
152
- idling, skipping, or doing one route then the other overruns the
153
- clock. Lose ≤1 unit.
154
  overrides:
155
  # Larger arena for real fog scouting across dispersed enemy bases
156
  # (rush-hour-arena 128x40 is too cramped). Generated on demand via
 
46
  # ── EASY ── one column, ONE ordered 3-waypoint route, clear lanes,
47
  # generous budget. Tests pure "go in the given order, don't idle."
48
  easy:
49
+ # Original (pre-verbosity-sweep) description preserved for
50
+ # contributors. The trimmed version below removes scripted-policy
51
+ # spoilers and cell-coord dumps; load-bearing intent kept.
52
+ #
53
+ # Execute ONE given route IN ORDER: waypoint W1 (mid-south, marked
54
+ # by an enemy Power Plant) → W2 (north, enemy Ore Refinery) → W3 /
55
+ # final (far SE, enemy Construction Yard). You must reach them in
56
+ # that order — rushing straight to the final does NOT count. Keep
57
+ # the column moving: arrive within the time budget or you lose.
58
  description: >
59
+ Visit three waypoints IN ORDER: enemy Power Plant (mid-south), then
60
+ Ore Refinery (north), then Construction Yard (far south-east).
61
+ Skipping or reordering does not count. Arrive within about 27
62
+ turns or you lose.
 
63
  overrides:
64
  actors:
65
  - {type: 2tnk, owner: agent, stance: 0, position: [5, 8], count: 2}
 
88
  # execution (finish route N, then route S) overruns the budget —
89
  # only a genuine two-column parallel split clears it. Attrition cap.
90
  medium:
91
+ # Original (pre-verbosity-sweep) description preserved for
92
+ # contributors. The trimmed version below removes scripted-policy
93
+ # spoilers and cell-coord dumps; load-bearing intent kept.
94
+ #
95
+ # TWO given routes that must BOTH be completed, each IN ORDER.
96
+ # Route N: N1 (W, enemy Power Plant) → N2 (mid-SOUTH, enemy Ore
97
+ # Refinery) → N3 (far-E NORTH, enemy Construction Yard). Route S:
98
+ # S1 (W-south, enemy Radar Dome) → S2 (mid-NORTH, enemy Power
99
+ # Plant) → S3 (far-E SOUTH, enemy Ore Refinery). The two routes
100
+ # cross in the middle: split into two columns and run them in
101
+ # parallel — doing one fully then the other overruns the clock.
102
+ # Lose ≤1 unit; arrive within budget or lose.
103
  description: >
104
+ Run TWO three-waypoint routes IN ORDER, both required. The routes
105
+ cross at mid-map, so split into two columns and run them in
106
+ parallel. Each route is labelled by enemy buildings. Arrive within
107
+ about 34 turns and lose at most one unit.
 
 
 
 
108
  overrides:
109
  actors:
110
  # Northern staging cluster
 
157
  # Seed picks the staging corner. Strict budget + attrition.
158
  hard:
159
  objective_coords: relative
160
+ # Original (pre-verbosity-sweep) description preserved for
161
+ # contributors. The trimmed version below removes scripted-policy
162
+ # spoilers and cell-coord dumps; load-bearing intent kept.
163
+ #
164
+ # TWO given routes across a large arena, each four ordered
165
+ # waypoints, described ONLY by direction — you are NOT given
166
+ # coordinates. Each waypoint is an enemy building hidden in fog:
167
+ # scout toward the stated direction and you will be interrupted the
168
+ # instant a unit sights the marker, revealing it; then move on to
169
+ # it and continue the route IN ORDER. Both routes must be completed
170
+ # (split into two columns and run them together). Keep advancing —
171
+ # idling, skipping, or doing one route then the other overruns the
172
+ # clock. Lose ≤1 unit.
173
  description: >
174
+ TWO four-waypoint routes across a large fogged arena, given only by
175
+ compass direction. Each waypoint is a hidden enemy building. Split
176
+ into two columns and run both routes IN ORDER in parallel. Arrive
177
+ within about 67 turns and lose at most one unit.
 
 
 
 
 
178
  overrides:
179
  # Larger arena for real fog scouting across dispersed enemy bases
180
  # (rush-hour-arena 128x40 is too cramped). Generated on demand via
openra_bench/scenarios/packs/adv-asymmetric-weaker-must-win.yaml CHANGED
@@ -139,14 +139,21 @@ levels:
139
  # is the point of the easy tier: bare commit-and-kill, no
140
  # asymmetry pressure yet.
141
  easy:
 
 
 
 
 
 
 
 
 
 
 
142
  description: >
143
- Two of your medium tanks (2tnk) at the west of the map (x≈8,
144
- y=20) must kill TWO enemy rifle infantry (e1) holding post at
145
- the east-centre cluster (around x=78, y=20). The infantry
146
- can't damage your tanks meaningfully — driving east and
147
- finishing them works. You MUST keep at least one tank alive
148
- AND retain your construction yard (`fact` at 4,20). Before
149
- tick 5400. Stalling loses on the clock.
150
  overrides:
151
  actors:
152
  # AGENT MEDIUMS — both on the y=20 lane; head-on works on
@@ -188,22 +195,30 @@ levels:
188
  # sqrt(16²+6²) ≈ 17 > 16), so the e1s die one at a time without
189
  # ever drawing the heavy off post.
190
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  description: >
192
- Two medium tanks (2tnk) at the west of the map must kill FOUR
193
- enemy rifle infantry (e1) that form a wall on the west face of
194
- the east-centre cluster (around x=74, y=18..22), behind which
195
- a HEAVY enemy tank (3tnk) holds post on the east face (90,20).
196
- The heavy's cannon out-DPSs your medium pair at close range —
197
- a head-on push along y=20 fixes you against the infantry wall
198
- long enough for the heavy to lunge into range and finish you.
199
- The only winning play is to stage OFF-AXIS (e.g. on the north
200
- corridor y=10), approach the infantry from a flank cell that
201
- is within your tank's gun range of an infantryman but OUTSIDE
202
- the heavy's aggro radius, pick off the infantry one at a time,
203
- and retreat past the heavy's leash any time it lunges. Keep
204
- at least one tank alive AND retain your construction yard
205
- (`fact` at 4,20). Before tick 5400. Stall, head-on charge,
206
- and brute attack_move east all LOSE.
207
  overrides:
208
  actors:
209
  # AGENT MEDIUMS — stage OFF-AXIS on the north corridor
@@ -249,20 +264,28 @@ levels:
249
  # — a sluggish kite cadence runs the clock; the asymmetric play
250
  # has to be efficient.
251
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  description: >
253
- Two medium tanks (2tnk) stage at one of two west-of-map
254
- corridors (NORTH y=12 OR SOUTH y=28, chosen by seed
255
- anti-memorisation). The garrison is identical to medium —
256
- rifle infantry (e1) walling the west face of the cluster
257
- (around x=74) and 1× HEAVY tank (3tnk) at the east face
258
- (90,20). Whichever corridor you stage on, the working flank
259
- attack is on YOUR side of the cluster (north flank from a
260
- north spawn; south flank from a south spawn). Head-on along
261
- y=20 still LOSES. Stage off-axis, approach the infantry on
262
- the flank that keeps you outside the heavy's aggro envelope,
263
- pick the infantry off, retreat past the leash any time the
264
- heavy lunges. Keep at least one tank alive AND retain your
265
- construction yard (`fact` at 4,20). Before tick 4500.
266
  overrides:
267
  actors:
268
  # spawn_point 0 — NORTH staging corridor (y=12). Natural
 
139
  # is the point of the easy tier: bare commit-and-kill, no
140
  # asymmetry pressure yet.
141
  easy:
142
+ # Original (pre-verbosity-sweep) description preserved for
143
+ # contributors. The trimmed version below removes scripted-policy
144
+ # spoilers and cell-coord dumps; load-bearing intent kept.
145
+ #
146
+ # Two of your medium tanks (2tnk) at the west of the map (x≈8,
147
+ # y=20) must kill TWO enemy rifle infantry (e1) holding post at
148
+ # the east-centre cluster (around x=78, y=20). The infantry
149
+ # can't damage your tanks meaningfully — driving east and
150
+ # finishing them works. You MUST keep at least one tank alive
151
+ # AND retain your construction yard (`fact` at 4,20). Before
152
+ # tick 5400. Stalling loses on the clock.
153
  description: >
154
+ Your two medium tanks must kill two enemy rifle infantry at the
155
+ east-centre cluster within about 60 turns. Keep at least one tank
156
+ alive and keep your construction yard standing.
 
 
 
 
157
  overrides:
158
  actors:
159
  # AGENT MEDIUMS — both on the y=20 lane; head-on works on
 
195
  # sqrt(16²+6²) ≈ 17 > 16), so the e1s die one at a time without
196
  # ever drawing the heavy off post.
197
  medium:
198
+ # Original (pre-verbosity-sweep) description preserved for
199
+ # contributors. The trimmed version below removes scripted-policy
200
+ # spoilers and cell-coord dumps; load-bearing intent kept.
201
+ #
202
+ # Two medium tanks (2tnk) at the west of the map must kill FOUR
203
+ # enemy rifle infantry (e1) that form a wall on the west face of
204
+ # the east-centre cluster (around x=74, y=18..22), behind which
205
+ # a HEAVY enemy tank (3tnk) holds post on the east face (90,20).
206
+ # The heavy's cannon out-DPSs your medium pair at close range —
207
+ # a head-on push along y=20 fixes you against the infantry wall
208
+ # long enough for the heavy to lunge into range and finish you.
209
+ # The only winning play is to stage OFF-AXIS (e.g. on the north
210
+ # corridor y=10), approach the infantry from a flank cell that
211
+ # is within your tank's gun range of an infantryman but OUTSIDE
212
+ # the heavy's aggro radius, pick off the infantry one at a time,
213
+ # and retreat past the heavy's leash any time it lunges. Keep
214
+ # at least one tank alive AND retain your construction yard
215
+ # (`fact` at 4,20). Before tick 5400. Stall, head-on charge,
216
+ # and brute attack_move east all LOSE.
217
  description: >
218
+ Two medium tanks must kill four enemy rifle infantry walling the
219
+ east-centre cluster while a heavy enemy tank holds post just
220
+ behind them. The heavy out-trades you head-on. Keep one tank
221
+ alive, keep your construction yard, finish within about 60 turns.
 
 
 
 
 
 
 
 
 
 
 
222
  overrides:
223
  actors:
224
  # AGENT MEDIUMS — stage OFF-AXIS on the north corridor
 
264
  # — a sluggish kite cadence runs the clock; the asymmetric play
265
  # has to be efficient.
266
  hard:
267
+ # Original (pre-verbosity-sweep) description preserved for
268
+ # contributors. The trimmed version below removes scripted-policy
269
+ # spoilers and cell-coord dumps; load-bearing intent kept.
270
+ #
271
+ # Two medium tanks (2tnk) stage at one of two west-of-map
272
+ # corridors (NORTH y=12 OR SOUTH y=28, chosen by seed —
273
+ # anti-memorisation). The garrison is identical to medium —
274
+ # 4× rifle infantry (e1) walling the west face of the cluster
275
+ # (around x=74) and 1× HEAVY tank (3tnk) at the east face
276
+ # (90,20). Whichever corridor you stage on, the working flank
277
+ # attack is on YOUR side of the cluster (north flank from a
278
+ # north spawn; south flank from a south spawn). Head-on along
279
+ # y=20 still LOSES. Stage off-axis, approach the infantry on
280
+ # the flank that keeps you outside the heavy's aggro envelope,
281
+ # pick the infantry off, retreat past the leash any time the
282
+ # heavy lunges. Keep at least one tank alive AND retain your
283
+ # construction yard (`fact` at 4,20). Before tick 4500.
284
  description: >
285
+ Same composition as medium but a tighter deadline (about 50
286
+ turns), and your two tanks stage in either the north or south
287
+ corridor (seed picks). Kill four rifle infantry without losing
288
+ both tanks or your construction yard.
 
 
 
 
 
 
 
 
 
289
  overrides:
290
  actors:
291
  # spawn_point 0 — NORTH staging corridor (y=12). Natural
openra_bench/scenarios/packs/adv-rps-counter-pick.yaml CHANGED
@@ -166,18 +166,25 @@ levels:
166
  # Wrong-counter (8× e3) cost-per-effect wastes on soft infantry.
167
  # Stall: kill bar (6) never met → after_ticks LOSS.
168
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  description: >
170
- Cash $2550. The enemy is a small rifle-infantry swarm (8× e1)
171
- entrenched at the centre of the map. You start with a full
172
- base (construction yard, refinery, power, barracks, war
173
- factory, service depot, 2 scout jeeps) — all production
174
- queues are online from turn 1. Scout the enemy with a jeep,
175
- read the composition, then commit the whole $2550 to the
176
- matching counter (3× medium tanks 2tnk = $2550 walks through
177
- a rifle swarm; mass rockets are anti-armour ordnance and
178
- waste cost-per-effect on soft targets). Win when 6 enemy
179
- units are killed AND your construction yard still stands,
180
- before tick 5400. Stalling and wrong-counter both LOSE.
181
  overrides:
182
  actors:
183
  - {type: fact, owner: agent, stance: 0, position: [10, 20]}
@@ -220,18 +227,26 @@ levels:
220
  # ground-truth, and a play that scouts must DO so before
221
  # committing.
222
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  description: >
224
- Cash $2550. The enemy holds a centre garrison (~12 rifle
225
- infantry) on the central lane; their composition is FOGGED
226
- scout with one of the starter jeeps to verify the threat
227
- profile BEFORE committing the budget. Your construction yard,
228
- refinery, power, barracks, war factory, and service depot are
229
- all online from turn 1. Once you've read the enemy, commit
230
- the WHOLE $2550 to the matching counter (3× 2tnk medium tanks
231
- walks through a rifle swarm; mass rockets are anti-armour and
232
- waste cost-per-effect; mass own rifles is a 1:1 trade with no
233
- positional advantage). Win when 8 enemy units are killed AND
234
- your construction yard still stands, before tick 5400.
235
  overrides:
236
  actors:
237
  - {type: fact, owner: agent, stance: 0, position: [10, 20]}
@@ -295,20 +310,29 @@ levels:
295
  # escorts = 5, the e3 cluster has 5 rocket soldiers) while a
296
  # wrong-counter play stalls below it.
297
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  description: >
299
- Cash $2550. Your base is fixed at the west (construction
300
- yard, refinery, power, barracks, war factory, service depot,
301
- 2 scout jeeps) full production from turn 1. The enemy
302
- garrison at the centre-east of the map rotates ARCHETYPE
303
- across seeds: an entrenched rifle swarm (e1), a heavy tank
304
- column (3tnk), or an anti-armour rocket cluster (e3). Each
305
- archetype demands a DIFFERENT counter: tanks (2tnk) shred
306
- rifles; rockets (e3) shred heavy armour; mass rifles (e1)
307
- shred rockets (e3 are anti-armour, weak vs soft mass).
308
- Scout with a jeep to identify the composition BEFORE
309
- committing the budget — a pre-committed single build LOSES
310
- on at least one seed. Win when 5 enemy units are killed AND
311
- your construction yard still stands, before tick 5400.
312
  overrides:
313
  actors:
314
  # ── AGENT BASE (no spawn_point — identical every seed) ──
 
166
  # Wrong-counter (8× e3) cost-per-effect wastes on soft infantry.
167
  # Stall: kill bar (6) never met → after_ticks LOSS.
168
  easy:
169
+ # Original (pre-verbosity-sweep) description preserved for
170
+ # contributors. The trimmed version below removes scripted-policy
171
+ # spoilers and cell-coord dumps; load-bearing intent kept.
172
+ #
173
+ # Cash $2550. The enemy is a small rifle-infantry swarm (8× e1)
174
+ # entrenched at the centre of the map. You start with a full
175
+ # base (construction yard, refinery, power, barracks, war
176
+ # factory, service depot, 2 scout jeeps) — all production
177
+ # queues are online from turn 1. Scout the enemy with a jeep,
178
+ # read the composition, then commit the whole $2550 to the
179
+ # matching counter (3× medium tanks 2tnk = $2550 walks through
180
+ # a rifle swarm; mass rockets are anti-armour ordnance and
181
+ # waste cost-per-effect on soft targets). Win when 6 enemy
182
+ # units are killed AND your construction yard still stands,
183
+ # before tick 5400. Stalling and wrong-counter both LOSE.
184
  description: >
185
+ Cash $2550, full base online. Enemy is a rifle-infantry swarm at
186
+ centre-east. Pick the matching counter and kill six enemy units
187
+ within about 60 turns, keeping your construction yard.
 
 
 
 
 
 
 
 
188
  overrides:
189
  actors:
190
  - {type: fact, owner: agent, stance: 0, position: [10, 20]}
 
227
  # ground-truth, and a play that scouts must DO so before
228
  # committing.
229
  medium:
230
+ # Original (pre-verbosity-sweep) description preserved for
231
+ # contributors. The trimmed version below removes scripted-policy
232
+ # spoilers and cell-coord dumps; load-bearing intent kept.
233
+ #
234
+ # Cash $2550. The enemy holds a centre garrison (~12 rifle
235
+ # infantry) on the central lane; their composition is FOGGED
236
+ # — scout with one of the starter jeeps to verify the threat
237
+ # profile BEFORE committing the budget. Your construction yard,
238
+ # refinery, power, barracks, war factory, and service depot are
239
+ # all online from turn 1. Once you've read the enemy, commit
240
+ # the WHOLE $2550 to the matching counter (3× 2tnk medium tanks
241
+ # walks through a rifle swarm; mass rockets are anti-armour and
242
+ # waste cost-per-effect; mass own rifles is a 1:1 trade with no
243
+ # positional advantage). Win when 8 enemy units are killed AND
244
+ # your construction yard still stands, before tick 5400.
245
  description: >
246
+ Cash $2550, full base online. A centre garrison sits in fog —
247
+ scout with a jeep first, then commit the budget to the matching
248
+ counter. Kill eight enemy units within about 60 turns, keeping
249
+ your construction yard.
 
 
 
 
 
 
 
250
  overrides:
251
  actors:
252
  - {type: fact, owner: agent, stance: 0, position: [10, 20]}
 
310
  # escorts = 5, the e3 cluster has 5 rocket soldiers) while a
311
  # wrong-counter play stalls below it.
312
  hard:
313
+ # Original (pre-verbosity-sweep) description preserved for
314
+ # contributors. The trimmed version below removes scripted-policy
315
+ # spoilers and cell-coord dumps; load-bearing intent kept.
316
+ #
317
+ # Cash $2550. Your base is fixed at the west (construction
318
+ # yard, refinery, power, barracks, war factory, service depot,
319
+ # 2 scout jeeps) — full production from turn 1. The enemy
320
+ # garrison at the centre-east of the map rotates ARCHETYPE
321
+ # across seeds: an entrenched rifle swarm (e1), a heavy tank
322
+ # column (3tnk), or an anti-armour rocket cluster (e3). Each
323
+ # archetype demands a DIFFERENT counter: tanks (2tnk) shred
324
+ # rifles; rockets (e3) shred heavy armour; mass rifles (e1)
325
+ # shred rockets (e3 are anti-armour, weak vs soft mass).
326
+ # Scout with a jeep to identify the composition BEFORE
327
+ # committing the budget — a pre-committed single build LOSES
328
+ # on at least one seed. Win when 5 enemy units are killed AND
329
+ # your construction yard still stands, before tick 5400.
330
  description: >
331
+ Cash $2550, full base online. The centre-east enemy archetype
332
+ rotates per seed (rifle swarm, heavy tank column, or rocket
333
+ cluster). Scout first, then commit the budget to the matching
334
+ counter. Kill five enemy units within about 60 turns, keeping
335
+ your construction yard.
 
 
 
 
 
 
 
 
336
  overrides:
337
  actors:
338
  # ── AGENT BASE (no spawn_point — identical every seed) ──
openra_bench/scenarios/packs/adversarial-skirmish.yaml CHANGED
@@ -89,11 +89,18 @@ levels:
89
  own_units_gte: 1
90
  max_turns: 70
91
  hard:
92
- description: 'Rung 3 — 3 tanks vs 8, loss-capped, and the outnumbered force starts from a seed-chosen
93
- corner (two spawn_point groups start axis varies by seed, so a memorised kite line can''t generalise).
94
- Enemy is mid-map beyond initial sight: engage selection under fog.
95
-
96
- '
 
 
 
 
 
 
 
97
  overrides:
98
  actors:
99
  - type: 2tnk
 
89
  own_units_gte: 1
90
  max_turns: 70
91
  hard:
92
+ # Original (pre-verbosity-sweep) description preserved for
93
+ # contributors. The trimmed version below removes scripted-policy
94
+ # spoilers and cell-coord dumps; load-bearing intent kept.
95
+ #
96
+ # Rung 3 — 3 tanks vs 8, loss-capped, and the outnumbered force
97
+ # starts from a seed-chosen corner (two spawn_point groups → start
98
+ # axis varies by seed, so a memorised kite line can't generalise).
99
+ # Enemy is mid-map beyond initial sight: engage selection under fog.
100
+ description: >
101
+ Outnumbered three tanks vs eight mixed enemies under fog. Start
102
+ corner varies by seed. Kill eight, losing at most two, within
103
+ about 89 turns.
104
  overrides:
105
  actors:
106
  - type: 2tnk
openra_bench/scenarios/packs/artofwar-decoy-sacrifice.yaml CHANGED
@@ -62,13 +62,21 @@ levels:
62
  # leading the hunters off with the jeeps clears the way. Generous
63
  # clock.
64
  easy:
 
 
 
 
 
 
 
 
 
 
65
  description: >
66
- A hunting picket (rocket infantry + riflemen) blocks the lane to
67
- the objective and chases your nearest unit. Get THREE tanks to
68
- the objective (far east). Driving the column straight in gets it
69
- shot apart before three arrive; send the fast jeeps in as bait on
70
- a separate vector to pull the hunters off, then run the tanks
71
- through. Idling or never committing loses on the clock.
72
  overrides: {}
73
  win_condition:
74
  all_of:
@@ -83,10 +91,16 @@ levels:
83
  # ── MEDIUM ── +1 controlled variable: a heavier anti-armor picket
84
  # (more e3) so a head-on push is decisively fatal; tighter clock.
85
  medium:
 
 
 
 
 
 
 
86
  description: >
87
- Heavier rocket-infantry picket a head-on push is now decisively
88
- fatal. Same task: divert the hunters with the bait, land THREE
89
- tanks on the objective before the (tighter) deadline.
90
  overrides:
91
  actors:
92
  - {type: 2tnk, owner: agent, position: [6, 20], stance: 1, count: 3}
@@ -115,12 +129,19 @@ levels:
115
  # be memorised; strongest picket; strict attrition cap on top (the
116
  # main body must arrive nearly whole — only the bait may be spent).
117
  hard:
 
 
 
 
 
 
 
 
 
118
  description: >
119
- Strongest hunting picket; the force stages from a seed-chosen
120
- latitude (the decoy line that works varies by seed, so it can't
121
- be memorised). Land THREE tanks on the objective AND lose ≤2
122
- units (only the two bait jeeps may be spent — burning armour
123
- fails) before the tight deadline.
124
  overrides:
125
  actors:
126
  # spawn_point 0 — northern staging
 
62
  # leading the hunters off with the jeeps clears the way. Generous
63
  # clock.
64
  easy:
65
+ # Original (pre-verbosity-sweep) description preserved for
66
+ # contributors. The trimmed version below removes scripted-policy
67
+ # spoilers and cell-coord dumps; load-bearing intent kept.
68
+ #
69
+ # A hunting picket (rocket infantry + riflemen) blocks the lane to
70
+ # the objective and chases your nearest unit. Get THREE tanks to
71
+ # the objective (far east). Driving the column straight in gets it
72
+ # shot apart before three arrive; send the fast jeeps in as bait on
73
+ # a separate vector to pull the hunters off, then run the tanks
74
+ # through. Idling or never committing loses on the clock.
75
  description: >
76
+ A hunting rocket-and-rifle picket blocks the corridor to the far
77
+ east objective. Get three tanks to the objective within about 65
78
+ turns. The picket chases the nearest unit, so an unsupported
79
+ head-on push is fatal.
 
 
80
  overrides: {}
81
  win_condition:
82
  all_of:
 
91
  # ── MEDIUM ── +1 controlled variable: a heavier anti-armor picket
92
  # (more e3) so a head-on push is decisively fatal; tighter clock.
93
  medium:
94
+ # Original (pre-verbosity-sweep) description preserved for
95
+ # contributors. The trimmed version below removes scripted-policy
96
+ # spoilers and cell-coord dumps; load-bearing intent kept.
97
+ #
98
+ # Heavier rocket-infantry picket — a head-on push is now decisively
99
+ # fatal. Same task: divert the hunters with the bait, land THREE
100
+ # tanks on the objective before the (tighter) deadline.
101
  description: >
102
+ Heavier rocket-infantry picket. Land three tanks on the far east
103
+ objective within about 75 turns.
 
104
  overrides:
105
  actors:
106
  - {type: 2tnk, owner: agent, position: [6, 20], stance: 1, count: 3}
 
129
  # be memorised; strongest picket; strict attrition cap on top (the
130
  # main body must arrive nearly whole — only the bait may be spent).
131
  hard:
132
+ # Original (pre-verbosity-sweep) description preserved for
133
+ # contributors. The trimmed version below removes scripted-policy
134
+ # spoilers and cell-coord dumps; load-bearing intent kept.
135
+ #
136
+ # Strongest hunting picket; the force stages from a seed-chosen
137
+ # latitude (the decoy line that works varies by seed, so it can't
138
+ # be memorised). Land THREE tanks on the objective AND lose ≤2
139
+ # units (only the two bait jeeps may be spent — burning armour
140
+ # fails) before the tight deadline.
141
  description: >
142
+ Strongest hunting picket. The force stages from a seed-chosen
143
+ latitude. Land three tanks on the far east objective, losing at
144
+ most two units, within about 82 turns.
 
 
145
  overrides:
146
  actors:
147
  # spawn_point 0 — northern staging
openra_bench/scenarios/packs/artofwar-indirect-approach.yaml CHANGED
@@ -67,14 +67,21 @@ levels:
67
  # flank route around the wall's east end lands the force. Generous
68
  # clock. Loss-cap of 1 (a single clipped tank is forgivable here).
69
  easy:
 
 
 
 
 
 
 
 
 
 
 
70
  description: >
71
- A leashed rocket-infantry wall blocks the direct lane east to the
72
- objective and shreds anything that tries to cross it. Get THREE
73
- tanks to the objective (far east, marked by the enemy structure).
74
- Driving the column straight in loses it in a turn; go the long way
75
- — climb to the open flank, run east PAST the end of the wall, then
76
- turn down to the objective. Idling never commits and loses on the
77
- clock.
78
  overrides: {}
79
  win_condition:
80
  all_of:
@@ -92,12 +99,18 @@ levels:
92
  # east, x=26..86) so the survivable flank route is longer and the
93
  # detour costs more turns before it pays off; tighter clock; zero-loss.
94
  medium:
 
 
 
 
 
 
 
 
 
95
  description: >
96
- The guard wall now extends much farther east — the long way round
97
- is longer and costlier, and a single clipped tank now fails. Same
98
- task: ignore the lethal short lane entirely, take the flank route
99
- around the (longer) wall's end, land THREE tanks on the objective
100
- before the tighter deadline.
101
  overrides:
102
  actors:
103
  - {type: 1tnk, owner: agent, position: [6, 20], stance: 1, count: 3}
@@ -138,13 +151,21 @@ levels:
138
  # can't be memorised. Strongest/longest wall; strict zero-loss; every
139
  # surviving unit must arrive (all_units_in_region) and ≥3 must.
140
  hard:
 
 
 
 
 
 
 
 
 
 
141
  description: >
142
- Strongest, longest wall; the force stages from a seed-chosen
143
- latitude (north or south of the wall), so the length and shape of
144
- the long way round varies by seed and can't be memorised. The
145
- short lane is lethal from either start. Get the WHOLE force (every
146
- surviving tank, ≥3) onto the objective with ZERO losses, via the
147
- long way around the wall's end, before the tight deadline.
148
  overrides:
149
  actors:
150
  # spawn_point 0 — northern staging
 
67
  # flank route around the wall's east end lands the force. Generous
68
  # clock. Loss-cap of 1 (a single clipped tank is forgivable here).
69
  easy:
70
+ # Original (pre-verbosity-sweep) description preserved for
71
+ # contributors. The trimmed version below removes scripted-policy
72
+ # spoilers and cell-coord dumps; load-bearing intent kept.
73
+ #
74
+ # A leashed rocket-infantry wall blocks the direct lane east to the
75
+ # objective and shreds anything that tries to cross it. Get THREE
76
+ # tanks to the objective (far east, marked by the enemy structure).
77
+ # Driving the column straight in loses it in a turn; go the long way
78
+ # — climb to the open flank, run east PAST the end of the wall, then
79
+ # turn down to the objective. Idling never commits and loses on the
80
+ # clock.
81
  description: >
82
+ A rocket-infantry wall blocks the direct lane east. Get three
83
+ tanks to the far east objective within about 59 turns, losing at
84
+ most one. Crossing the wall head-on is fatal.
 
 
 
 
85
  overrides: {}
86
  win_condition:
87
  all_of:
 
99
  # east, x=26..86) so the survivable flank route is longer and the
100
  # detour costs more turns before it pays off; tighter clock; zero-loss.
101
  medium:
102
+ # Original (pre-verbosity-sweep) description preserved for
103
+ # contributors. The trimmed version below removes scripted-policy
104
+ # spoilers and cell-coord dumps; load-bearing intent kept.
105
+ #
106
+ # The guard wall now extends much farther east — the long way round
107
+ # is longer and costlier, and a single clipped tank now fails. Same
108
+ # task: ignore the lethal short lane entirely, take the flank route
109
+ # around the (longer) wall's end, land THREE tanks on the objective
110
+ # before the tighter deadline.
111
  description: >
112
+ Longer rocket-infantry wall. Get three tanks to the far east
113
+ objective within about 65 turns with ZERO losses.
 
 
 
114
  overrides:
115
  actors:
116
  - {type: 1tnk, owner: agent, position: [6, 20], stance: 1, count: 3}
 
151
  # can't be memorised. Strongest/longest wall; strict zero-loss; every
152
  # surviving unit must arrive (all_units_in_region) and ≥3 must.
153
  hard:
154
+ # Original (pre-verbosity-sweep) description preserved for
155
+ # contributors. The trimmed version below removes scripted-policy
156
+ # spoilers and cell-coord dumps; load-bearing intent kept.
157
+ #
158
+ # Strongest, longest wall; the force stages from a seed-chosen
159
+ # latitude (north or south of the wall), so the length and shape of
160
+ # the long way round varies by seed and can't be memorised. The
161
+ # short lane is lethal from either start. Get the WHOLE force (every
162
+ # surviving tank, ≥3) onto the objective with ZERO losses, via the
163
+ # long way around the wall's end, before the tight deadline.
164
  description: >
165
+ Strongest wall. Stage from a seed-chosen latitude (north or south
166
+ of the wall). Land the WHOLE surviving force (at least three
167
+ tanks) on the far east objective with ZERO losses, within about
168
+ 69 turns.
 
 
169
  overrides:
170
  actors:
171
  # spawn_point 0 — northern staging
openra_bench/scenarios/packs/artofwar-lure-the-tiger.yaml CHANGED
@@ -85,16 +85,24 @@ levels:
85
  # 2; burning a tank fails). Idling never commits and loses on the
86
  # clock.
87
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  description: >
89
- A strong rocket-infantry guard wall blocks the one lane to the
90
- objective (far east, marked by the enemy structure) and snaps
91
- straight back to it the instant nothing threatens it. Get THREE
92
- tanks to the objective. Driving the column straight in runs it
93
- into the wall and it is destroyed; send the fast jeeps in close
94
- on a separate vector so a segment of the wall lunges off post
95
- after them, then run the tanks through the open slot before it
96
- snaps back. Only the two bait jeeps may be spent — burning a tank
97
- (≥3 lost) fails. Idling never commits and loses on the clock.
98
  overrides: {}
99
  win_condition:
100
  all_of:
@@ -113,12 +121,18 @@ levels:
113
  # segment off; tighter clock. Same loss cap of 2 (only the two bait
114
  # jeeps may be spent — burning armour fails).
115
  medium:
 
 
 
 
 
 
 
 
 
116
  description: >
117
- Denser rocket-infantry wall a head-on push is now decisively
118
- fatal and the bait must pull a wider segment off the lane. Same
119
- task: lure a segment off post with the jeeps on a divergent
120
- vector, run THREE tanks through the open slot to the objective
121
- before the (tighter) deadline, losing only the two bait jeeps.
122
  overrides:
123
  actors:
124
  - {type: 2tnk, owner: agent, position: [6, 20], stance: 1, count: 3}
@@ -151,13 +165,20 @@ levels:
151
  # the lure line can't be memorised. Densest wall; strict loss cap of
152
  # 2 (only the two bait jeeps may be spent — burning armour fails).
153
  hard:
 
 
 
 
 
 
 
 
 
 
154
  description: >
155
- Densest guard wall; the force stages from a seed-chosen latitude
156
- (north or south), so the bait vector and the slot the main body
157
- runs through vary by seed and can't be memorised. Lure a segment
158
- off post with the jeeps, then run THREE tanks through the open
159
- slot to the objective AND lose ≤2 units (only the two bait jeeps
160
- may be spent — burning armour fails) before the tight deadline.
161
  overrides:
162
  actors:
163
  # spawn_point 0 — northern staging (in-bounds, y=10/14)
 
85
  # 2; burning a tank fails). Idling never commits and loses on the
86
  # clock.
87
  easy:
88
+ # Original (pre-verbosity-sweep) description preserved for
89
+ # contributors. The trimmed version below removes scripted-policy
90
+ # spoilers and cell-coord dumps; load-bearing intent kept.
91
+ #
92
+ # A strong rocket-infantry guard wall blocks the one lane to the
93
+ # objective (far east, marked by the enemy structure) and snaps
94
+ # straight back to it the instant nothing threatens it. Get THREE
95
+ # tanks to the objective. Driving the column straight in runs it
96
+ # into the wall and it is destroyed; send the fast jeeps in close
97
+ # on a separate vector so a segment of the wall lunges off post
98
+ # after them, then run the tanks through the open slot before it
99
+ # snaps back. Only the two bait jeeps may be spent — burning a tank
100
+ # (≥3 lost) fails. Idling never commits and loses on the clock.
101
  description: >
102
+ A leashed rocket-infantry wall blocks the lane to the far east
103
+ objective; each guard lunges at nearby foes then snaps back. Get
104
+ three tanks to the objective within about 65 turns, losing at
105
+ most two units.
 
 
 
 
 
106
  overrides: {}
107
  win_condition:
108
  all_of:
 
121
  # segment off; tighter clock. Same loss cap of 2 (only the two bait
122
  # jeeps may be spent — burning armour fails).
123
  medium:
124
+ # Original (pre-verbosity-sweep) description preserved for
125
+ # contributors. The trimmed version below removes scripted-policy
126
+ # spoilers and cell-coord dumps; load-bearing intent kept.
127
+ #
128
+ # Denser rocket-infantry wall — a head-on push is now decisively
129
+ # fatal and the bait must pull a wider segment off the lane. Same
130
+ # task: lure a segment off post with the jeeps on a divergent
131
+ # vector, run THREE tanks through the open slot to the objective
132
+ # before the (tighter) deadline, losing only the two bait jeeps.
133
  description: >
134
+ Denser rocket-infantry wall. Get three tanks to the far east
135
+ objective within about 75 turns, losing at most two units.
 
 
 
136
  overrides:
137
  actors:
138
  - {type: 2tnk, owner: agent, position: [6, 20], stance: 1, count: 3}
 
165
  # the lure line can't be memorised. Densest wall; strict loss cap of
166
  # 2 (only the two bait jeeps may be spent — burning armour fails).
167
  hard:
168
+ # Original (pre-verbosity-sweep) description preserved for
169
+ # contributors. The trimmed version below removes scripted-policy
170
+ # spoilers and cell-coord dumps; load-bearing intent kept.
171
+ #
172
+ # Densest guard wall; the force stages from a seed-chosen latitude
173
+ # (north or south), so the bait vector and the slot the main body
174
+ # runs through vary by seed and can't be memorised. Lure a segment
175
+ # off post with the jeeps, then run THREE tanks through the open
176
+ # slot to the objective AND lose ≤2 units (only the two bait jeeps
177
+ # may be spent — burning armour fails) before the tight deadline.
178
  description: >
179
+ Densest guard wall. Force stages from a seed-chosen latitude
180
+ (north or south). Land three tanks on the far east objective,
181
+ losing at most two units, within about 82 turns.
 
 
 
182
  overrides:
183
  actors:
184
  # spawn_point 0 — northern staging (in-bounds, y=10/14)
openra_bench/scenarios/packs/artofwar-sequenced-citadel.yaml CHANGED
@@ -41,13 +41,21 @@ levels:
41
  # modest hold before the citadel counts. Generous clock. A beeline
42
  # straight to C never satisfies the ordered latch → timeout LOSS.
43
  easy:
 
 
 
 
 
 
 
 
 
 
44
  description: >
45
- Strict ordered chain: first reach staging point A (mid-west),
46
- then transit point B (north-centre), then seize the citadel C
47
- (far east) IN THAT ORDER. Reaching C without having passed A
48
- then B counts for nothing. The strike is timed: C only counts
49
- after the hold. A greedy beeline to C, or idling, loses on the
50
- clock.
51
  overrides: {}
52
  win_condition:
53
  all_of:
 
41
  # modest hold before the citadel counts. Generous clock. A beeline
42
  # straight to C never satisfies the ordered latch → timeout LOSS.
43
  easy:
44
+ # Original (pre-verbosity-sweep) description preserved for
45
+ # contributors. The trimmed version below removes scripted-policy
46
+ # spoilers and cell-coord dumps; load-bearing intent kept.
47
+ #
48
+ # Strict ordered chain: first reach staging point A (mid-west),
49
+ # then transit point B (north-centre), then seize the citadel C
50
+ # (far east) — IN THAT ORDER. Reaching C without having passed A
51
+ # then B counts for nothing. The strike is timed: C only counts
52
+ # after the hold. A greedy beeline to C, or idling, loses on the
53
+ # clock.
54
  description: >
55
+ Visit staging point A (mid-west), transit point B (north-centre),
56
+ then seize the citadel at C (far east) IN ORDER. C only counts
57
+ after turn 13 and before turn 26. Light pickets contest the
58
+ route.
 
 
59
  overrides: {}
60
  win_condition:
61
  all_of:
openra_bench/scenarios/packs/build-defensive-skirt-corners.yaml CHANGED
@@ -161,17 +161,25 @@ levels:
161
  # (count). max_turns 60 ⇒ reachable tick 93+90·59 = 5403;
162
  # deadline 5400.
163
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  description: >
165
- Four rusher bands of rifle infantry will charge your construction
166
- yard (fact, at map centre (64,20)) CONCURRENTLY from the four
167
- diagonal corners. Build 4 pillboxes (pbox 600cr each, budget
168
- exactly 2400) AND place ONE of them inside the radius-4 disc of
169
- EACH corner region: NE (82,9), NW (46,9), SE (82,31), SW (46,31).
170
- Each band spawns at one corner — a pbox planted there shreds it.
171
- Massing all four pillboxes on a single corner holds that corner
172
- but lets the other three waves reach the fact. Stall, pure-army,
173
- and concentrate all LOSE. The fact must survive; ≥9 enemy units
174
- must die before tick 5400.
175
  starting_cash: 2400
176
  overrides:
177
  actors:
@@ -225,17 +233,25 @@ levels:
225
  # both fails three region clauses AND lets those heavier waves walk
226
  # into the fact. max_turns 60 ⇒ reachable tick 5403; deadline 5400.
227
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
228
  description: >
229
- Four rusher bands — 4 rifle infantry each (16 total) will
230
- charge your construction yard (fact, at map centre (64,20))
231
- CONCURRENTLY from the four diagonal corners. Build 4 pillboxes
232
- (budget exactly 2400cr = 4 pbox at 600 each) AND place ONE inside
233
- the radius-4 disc of EACH corner region: NE (82,9), NW (46,9),
234
- SE (82,31), SW (46,31). Massing all four pillboxes on a single
235
- corner satisfies only one region clause and lets the three
236
- uncovered waves walk into the fact. Stall, pure-army, and
237
- concentrate all LOSE. The fact must survive; ≥13 enemy units
238
- must die before tick 5400.
239
  starting_cash: 2400
240
  overrides:
241
  actors:
@@ -292,20 +308,29 @@ levels:
292
  # must READ the fact's longitude from observation. Kill bar 13.
293
  # max_turns 60 ⇒ reachable tick 5403; deadline 5400.
294
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  description: >
296
- The agent construction yard (fact) flips between a WEST base
297
- (fact at (50,20)) and an EAST base (fact at (78,20)) by seed; the
298
- four corner regions of the skirt must follow. Build 4 pillboxes
299
- (budget 2400cr = 4 pbox at 600 each) AND place ONE inside the
300
- radius-4 disc of EACH corner of the CURRENT fact. For a WEST fact
301
- (50,20) the corners are NE (68,9) NW (32,9) SE (68,31) SW
302
- (32,31); for an EAST fact (78,20) they are NE (96,9) NW (60,9)
303
- SE (96,31) SW (60,31) — read the fact's longitude from the
304
- observation. Massing all four pillboxes on one corner, or
305
- skirting the wrong (x=64) centre, satisfies at most one region
306
- clause and lets the uncovered waves raze the fact. Stall,
307
- pure-army, and concentrate all LOSE. The fact must survive;
308
- ≥13 enemy units must die before tick 5400.
309
  starting_cash: 2400
310
  overrides:
311
  actors:
 
161
  # (count). max_turns 60 ⇒ reachable tick 93+90·59 = 5403;
162
  # deadline 5400.
163
  easy:
164
+ # Original (pre-verbosity-sweep) description preserved for
165
+ # contributors. The trimmed version below removes scripted-policy
166
+ # spoilers and cell-coord dumps; load-bearing intent kept.
167
+ #
168
+ # Four rusher bands of rifle infantry will charge your construction
169
+ # yard (fact, at map centre (64,20)) CONCURRENTLY from the four
170
+ # diagonal corners. Build 4 pillboxes (pbox — 600cr each, budget
171
+ # exactly 2400) AND place ONE of them inside the radius-4 disc of
172
+ # EACH corner region: NE (82,9), NW (46,9), SE (82,31), SW (46,31).
173
+ # Each band spawns at one corner — a pbox planted there shreds it.
174
+ # Massing all four pillboxes on a single corner holds that corner
175
+ # but lets the other three waves reach the fact. Stall, pure-army,
176
+ # and concentrate all LOSE. The fact must survive; ≥9 enemy units
177
+ # must die before tick 5400.
178
  description: >
179
+ Four rifle bands will rush your central construction yard from all
180
+ four diagonal corners. Budget $2400 build one pillbox in each
181
+ corner approach. Keep the yard alive, kill nine enemies, within
182
+ about 60 turns.
 
 
 
 
 
 
183
  starting_cash: 2400
184
  overrides:
185
  actors:
 
233
  # both fails three region clauses AND lets those heavier waves walk
234
  # into the fact. max_turns 60 ⇒ reachable tick 5403; deadline 5400.
235
  medium:
236
+ # Original (pre-verbosity-sweep) description preserved for
237
+ # contributors. The trimmed version below removes scripted-policy
238
+ # spoilers and cell-coord dumps; load-bearing intent kept.
239
+ #
240
+ # Four rusher bands — 4 rifle infantry each (16 total) — will
241
+ # charge your construction yard (fact, at map centre (64,20))
242
+ # CONCURRENTLY from the four diagonal corners. Build 4 pillboxes
243
+ # (budget exactly 2400cr = 4 pbox at 600 each) AND place ONE inside
244
+ # the radius-4 disc of EACH corner region: NE (82,9), NW (46,9),
245
+ # SE (82,31), SW (46,31). Massing all four pillboxes on a single
246
+ # corner satisfies only one region clause and lets the three
247
+ # uncovered waves walk into the fact. Stall, pure-army, and
248
+ # concentrate all LOSE. The fact must survive; ≥13 enemy units
249
+ # must die before tick 5400.
250
  description: >
251
+ Four heavier rifle bands (sixteen total) will rush your central
252
+ construction yard from all four diagonal corners. Budget $2400 —
253
+ build one pillbox in each corner approach. Keep the yard alive,
254
+ kill thirteen enemies, within about 60 turns.
 
 
 
 
 
 
255
  starting_cash: 2400
256
  overrides:
257
  actors:
 
308
  # must READ the fact's longitude from observation. Kill bar 13.
309
  # max_turns 60 ⇒ reachable tick 5403; deadline 5400.
310
  hard:
311
+ # Original (pre-verbosity-sweep) description preserved for
312
+ # contributors. The trimmed version below removes scripted-policy
313
+ # spoilers and cell-coord dumps; load-bearing intent kept.
314
+ #
315
+ # The agent construction yard (fact) flips between a WEST base
316
+ # (fact at (50,20)) and an EAST base (fact at (78,20)) by seed; the
317
+ # four corner regions of the skirt must follow. Build 4 pillboxes
318
+ # (budget 2400cr = 4 pbox at 600 each) AND place ONE inside the
319
+ # radius-4 disc of EACH corner of the CURRENT fact. For a WEST fact
320
+ # (50,20) the corners are NE (68,9) NW (32,9) SE (68,31) SW
321
+ # (32,31); for an EAST fact (78,20) they are NE (96,9) NW (60,9)
322
+ # SE (96,31) SW (60,31) — read the fact's longitude from the
323
+ # observation. Massing all four pillboxes on one corner, or
324
+ # skirting the wrong (x=64) centre, satisfies at most one region
325
+ # clause and lets the uncovered waves raze the fact. Stall,
326
+ # pure-army, and concentrate all LOSE. The fact must survive;
327
+ # ≥13 enemy units must die before tick 5400.
328
  description: >
329
+ Same four-corner rush, but your construction yard is at the WEST
330
+ or EAST of the map depending on seed the four corner approaches
331
+ shift with it. Budget $2400, build one pillbox in each corner of
332
+ the current yard. Keep it alive, kill thirteen, within about 60
333
+ turns.
 
 
 
 
 
 
 
 
334
  starting_cash: 2400
335
  overrides:
336
  actors:
openra_bench/scenarios/packs/build-defensive-tower-cluster.yaml CHANGED
@@ -133,17 +133,25 @@ levels:
133
  # fails. Stall loses (clock OR fact razed); pure-army loses (count).
134
  # max_turns 60 ⇒ reachable tick 93+90·59 = 5403; deadline 5400.
135
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  description: >
137
- A rusher band of rifle infantry charges your construction yard
138
- (fact, at (10,20)). Build 4 pillboxes (pbox 600cr each, budget
139
- exactly 2400) AND place at least 3 of them INSIDE the radius-4
140
- disc around the fact, so their overlapping fields of fire shred
141
- the rush at the doorstep of the protected asset. A thin pillbox
142
- LINE strung along the attack lane (pboxes at x=20..35) fails the
143
- region clause (none land inside the radius-4 disc around the
144
- fact) and also lets the rifle band slip through. Stall, pure-
145
- army, and pbox-line all LOSE. The fact must survive; ≥3 enemy
146
- units must die before tick 5400.
147
  starting_cash: 2400
148
  overrides:
149
  actors:
@@ -194,16 +202,24 @@ levels:
194
  # 5 to require the cluster to actually fight, not just absorb.
195
  # max_turns 60 ⇒ reachable tick 5403; deadline 5400.
196
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  description: >
198
- The full rush band — 6 rifle infantry + 2 rocket soldiers —
199
- charges your construction yard (fact, at (10,20)). Build 4
200
- pillboxes (budget exactly 2400cr = 4 pbox at 600 each) AND
201
- place at least 3 of them INSIDE the radius-4 disc around the
202
- fact. A pillbox LINE (1 at the fact, 3 along the lane) meets
203
- the count but FAILS the region bar (1 of 4 inside the disc, not
204
- the required 3) AND cannot mass enough firepower to blunt the
205
- heavier band. Stall, pure-army, and pbox-line all LOSE. The
206
- fact must survive; ≥5 enemy units must die before tick 5400.
207
  starting_cash: 2400
208
  overrides:
209
  actors:
@@ -254,18 +270,26 @@ levels:
254
  # grows to 4 (still 2400cr, exact). Kill bar 6.
255
  # max_turns 60 ⇒ reachable tick 5403; deadline 5400.
256
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  description: >
258
- The agent construction yard (fact) flips between NORTH (y=14)
259
- and SOUTH (y=26) by seed; the cluster centre must follow.
260
- Build 4 pillboxes (budget 2400cr = 4 pbox at 600 each) AND
261
- place at least 3 of them INSIDE the radius-4 disc around the
262
- CURRENT fact (either (10,14) or (10,26) — read it from the
263
- observation). A line layout, a cluster around (10,20) (the
264
- old centre that no longer holds the fact), or any plan that
265
- doesn't follow the fact's latitude FAILS the region bar AND
266
- lets the rush reach the fact. Stall, pure-army, pbox-line
267
- all LOSE. The fact must survive; ≥6 enemy units must die
268
- before tick 5400.
269
  starting_cash: 2400
270
  overrides:
271
  actors:
 
133
  # fails. Stall loses (clock OR fact razed); pure-army loses (count).
134
  # max_turns 60 ⇒ reachable tick 93+90·59 = 5403; deadline 5400.
135
  easy:
136
+ # Original (pre-verbosity-sweep) description preserved for
137
+ # contributors. The trimmed version below removes scripted-policy
138
+ # spoilers and cell-coord dumps; load-bearing intent kept.
139
+ #
140
+ # A rusher band of rifle infantry charges your construction yard
141
+ # (fact, at (10,20)). Build 4 pillboxes (pbox — 600cr each, budget
142
+ # exactly 2400) AND place at least 3 of them INSIDE the radius-4
143
+ # disc around the fact, so their overlapping fields of fire shred
144
+ # the rush at the doorstep of the protected asset. A thin pillbox
145
+ # LINE strung along the attack lane (pboxes at x=20..35) fails the
146
+ # region clause (none land inside the radius-4 disc around the
147
+ # fact) and also lets the rifle band slip through. Stall, pure-
148
+ # army, and pbox-line all LOSE. The fact must survive; ≥3 enemy
149
+ # units must die before tick 5400.
150
  description: >
151
+ A rifle band charges your construction yard from the east. Budget
152
+ $2400 build four pillboxes with at least three sitting within
153
+ four cells of the yard. Keep the yard alive, kill three enemies,
154
+ within about 60 turns.
 
 
 
 
 
 
155
  starting_cash: 2400
156
  overrides:
157
  actors:
 
202
  # 5 to require the cluster to actually fight, not just absorb.
203
  # max_turns 60 ⇒ reachable tick 5403; deadline 5400.
204
  medium:
205
+ # Original (pre-verbosity-sweep) description preserved for
206
+ # contributors. The trimmed version below removes scripted-policy
207
+ # spoilers and cell-coord dumps; load-bearing intent kept.
208
+ #
209
+ # The full rush band — 6 rifle infantry + 2 rocket soldiers —
210
+ # charges your construction yard (fact, at (10,20)). Build 4
211
+ # pillboxes (budget exactly 2400cr = 4 pbox at 600 each) AND
212
+ # place at least 3 of them INSIDE the radius-4 disc around the
213
+ # fact. A pillbox LINE (1 at the fact, 3 along the lane) meets
214
+ # the count but FAILS the region bar (1 of 4 inside the disc, not
215
+ # the required 3) AND cannot mass enough firepower to blunt the
216
+ # heavier band. Stall, pure-army, and pbox-line all LOSE. The
217
+ # fact must survive; ≥5 enemy units must die before tick 5400.
218
  description: >
219
+ Heavier rush — 6 rifle infantry plus 2 rocket soldiers — charges
220
+ your construction yard. Budget $2400, build four pillboxes with
221
+ at least three within four cells of the yard. Keep the yard
222
+ alive, kill five enemies, within about 60 turns.
 
 
 
 
 
223
  starting_cash: 2400
224
  overrides:
225
  actors:
 
270
  # grows to 4 (still 2400cr, exact). Kill bar 6.
271
  # max_turns 60 ⇒ reachable tick 5403; deadline 5400.
272
  hard:
273
+ # Original (pre-verbosity-sweep) description preserved for
274
+ # contributors. The trimmed version below removes scripted-policy
275
+ # spoilers and cell-coord dumps; load-bearing intent kept.
276
+ #
277
+ # The agent construction yard (fact) flips between NORTH (y=14)
278
+ # and SOUTH (y=26) by seed; the cluster centre must follow.
279
+ # Build 4 pillboxes (budget 2400cr = 4 pbox at 600 each) AND
280
+ # place at least 3 of them INSIDE the radius-4 disc around the
281
+ # CURRENT fact (either (10,14) or (10,26) — read it from the
282
+ # observation). A line layout, a cluster around (10,20) (the
283
+ # old centre that no longer holds the fact), or any plan that
284
+ # doesn't follow the fact's latitude FAILS the region bar AND
285
+ # lets the rush reach the fact. Stall, pure-army, pbox-line
286
+ # all LOSE. The fact must survive; ≥6 enemy units must die
287
+ # before tick 5400.
288
  description: >
289
+ Same rush, but your construction yard is at the north or south of
290
+ the west edge depending on seed. Budget $2400, build four
291
+ pillboxes with at least three within four cells of the current
292
+ yard. Keep it alive, kill six, within about 60 turns.
 
 
 
 
 
 
 
293
  starting_cash: 2400
294
  overrides:
295
  actors:
openra_bench/scenarios/packs/build-defensive-tower-line.yaml CHANGED
@@ -98,18 +98,26 @@ levels:
98
  # clause AND the fact razed by the rush.
99
  # max_turns 60 ⇒ reachable tick 93+90·59 = 5403; deadline 5400.
100
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  description: >
102
- A rusher band will spawn east of the corridor and must traverse
103
- the narrow corridor at x=60, y=18..22 to reach your base on the
104
- west. Build 4 pillboxes (pbox 600cr each, budget exactly 2400)
105
- AND place ONE on each of the four corridor rungs (at (60,18),
106
- (60,19), (60,21), (60,22)) so the rusher cannot slip past on any
107
- row. A cluster on the middle of the corridor satisfies the count
108
- but FAILS every rung and lets the rush leak through; a random
109
- scatter near the base fails every rung and kills nothing; a
110
- pure-army layout (no pbox) fails the count and lets the rush
111
- raze the fact. Your pillboxes must kill at least 4 of the rush;
112
- your fact must survive.
113
  starting_cash: 2400
114
  overrides:
115
  actors:
@@ -160,14 +168,21 @@ levels:
160
  # heavier wave through to fail the kill bar AND raze the fact.
161
  # max_turns 60 ⇒ reachable tick 5403; deadline 5400.
162
  medium:
 
 
 
 
 
 
 
 
 
 
 
163
  description: >
164
- Same forced rusher corridor at x=60, y=18..22. Build 4 pillboxes
165
- (budget 2400cr = exactly 4 pbox at 600 each) AND place ONE on
166
- each of the four corridor rungs (at (60,18), (60,19), (60,21),
167
- (60,22)). The rush wave is heavier than easy — the complete LINE
168
- must shred it at the corridor. Your pillboxes must kill at least
169
- 7 of the rush; a cluster, a scatter, and a pure-army layout all
170
- lose; the fact must survive.
171
  starting_cash: 2400
172
  overrides:
173
  actors:
@@ -217,15 +232,23 @@ levels:
217
  # y=18..22 regardless of base latitude). max_turns 70 ⇒ reachable
218
  # tick 93+90·69 = 6303; deadline 6300.
219
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
220
  description: >
221
- Agent base latitude flips between NORTH (y=12) and SOUTH (y=28)
222
- by seed. Build 4 pillboxes (budget 2400cr = exactly 4 pbox at
223
- 600 each) AND place ONE on each of the four corridor rungs
224
- (at (60,18), (60,19), (60,21), (60,22)). The corridor at x=60
225
- y=18..22 is a fixed map feature — covering the rows next to your
226
- base instead (y=14..18 for NORTH, y=22..26 for SOUTH) FAILS the
227
- rung clauses and lets the rush leak through. Your pillboxes must
228
- kill at least 7 of the rush; the fact must survive.
229
  starting_cash: 2400
230
  overrides:
231
  actors:
 
98
  # clause AND the fact razed by the rush.
99
  # max_turns 60 ⇒ reachable tick 93+90·59 = 5403; deadline 5400.
100
  easy:
101
+ # Original (pre-verbosity-sweep) description preserved for
102
+ # contributors. The trimmed version below removes scripted-policy
103
+ # spoilers and cell-coord dumps; load-bearing intent kept.
104
+ #
105
+ # A rusher band will spawn east of the corridor and must traverse
106
+ # the narrow corridor at x=60, y=18..22 to reach your base on the
107
+ # west. Build 4 pillboxes (pbox — 600cr each, budget exactly 2400)
108
+ # AND place ONE on each of the four corridor rungs (at (60,18),
109
+ # (60,19), (60,21), (60,22)) so the rusher cannot slip past on any
110
+ # row. A cluster on the middle of the corridor satisfies the count
111
+ # but FAILS every rung and lets the rush leak through; a random
112
+ # scatter near the base fails every rung and kills nothing; a
113
+ # pure-army layout (no pbox) fails the count and lets the rush
114
+ # raze the fact. Your pillboxes must kill at least 4 of the rush;
115
+ # your fact must survive.
116
  description: >
117
+ A rush will funnel through the narrow mid-map corridor at x=60,
118
+ rows y=18 to 22. Budget $2400 — build a pillbox on each of those
119
+ four rows so nothing slips through. Kill four enemies and keep
120
+ your construction yard, within about 60 turns.
 
 
 
 
 
 
 
121
  starting_cash: 2400
122
  overrides:
123
  actors:
 
168
  # heavier wave through to fail the kill bar AND raze the fact.
169
  # max_turns 60 ⇒ reachable tick 5403; deadline 5400.
170
  medium:
171
+ # Original (pre-verbosity-sweep) description preserved for
172
+ # contributors. The trimmed version below removes scripted-policy
173
+ # spoilers and cell-coord dumps; load-bearing intent kept.
174
+ #
175
+ # Same forced rusher corridor at x=60, y=18..22. Build 4 pillboxes
176
+ # (budget 2400cr = exactly 4 pbox at 600 each) AND place ONE on
177
+ # each of the four corridor rungs (at (60,18), (60,19), (60,21),
178
+ # (60,22)). The rush wave is heavier than easy — the complete LINE
179
+ # must shred it at the corridor. Your pillboxes must kill at least
180
+ # 7 of the rush; a cluster, a scatter, and a pure-army layout all
181
+ # lose; the fact must survive.
182
  description: >
183
+ Same corridor at x=60, y=18 to 22, heavier rush wave. Budget
184
+ $2400 build a pillbox on each of those four rows. Kill seven
185
+ enemies and keep your construction yard, within about 60 turns.
 
 
 
 
186
  starting_cash: 2400
187
  overrides:
188
  actors:
 
232
  # y=18..22 regardless of base latitude). max_turns 70 ⇒ reachable
233
  # tick 93+90·69 = 6303; deadline 6300.
234
  hard:
235
+ # Original (pre-verbosity-sweep) description preserved for
236
+ # contributors. The trimmed version below removes scripted-policy
237
+ # spoilers and cell-coord dumps; load-bearing intent kept.
238
+ #
239
+ # Agent base latitude flips between NORTH (y=12) and SOUTH (y=28)
240
+ # by seed. Build 4 pillboxes (budget 2400cr = exactly 4 pbox at
241
+ # 600 each) AND place ONE on each of the four corridor rungs
242
+ # (at (60,18), (60,19), (60,21), (60,22)). The corridor at x=60
243
+ # y=18..22 is a fixed map feature — covering the rows next to your
244
+ # base instead (y=14..18 for NORTH, y=22..26 for SOUTH) FAILS the
245
+ # rung clauses and lets the rush leak through. Your pillboxes must
246
+ # kill at least 7 of the rush; the fact must survive.
247
  description: >
248
+ Same corridor at x=60, y=18 to 22 — its location is a fixed map
249
+ feature even though your base latitude flips between north and
250
+ south by seed. Build a pillbox on each of those four rows. Kill
251
+ seven enemies and keep your yard, within about 70 turns.
 
 
 
 
252
  starting_cash: 2400
253
  overrides:
254
  actors:
openra_bench/scenarios/packs/build-engineer-rebuild-after-loss.yaml CHANGED
@@ -198,19 +198,27 @@ levels:
198
  # follow-on path). Reserve 500 cr funds the powr rebuild with
199
  # $200 slack. max_turns 30 → ceiling 2703 > 2700 ✓.
200
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  description: >
202
- You inherit a working production base a Construction Yard
203
- ('fact'), an Ore Refinery ('proc'), a Power Plant ('powr'),
204
- a War Factory ('weap'), and one Ore Truck ('harv') with an
205
- ore patch — and $500 cash. The Power Plant is at LOW HEALTH
206
- and a single enemy heavy tank ('4tnk') is staged adjacent to
207
- it; the Power Plant will be DESTROYED in the opening turn.
208
- To recover: queue another Power Plant ('build' "powr",
209
- cost 300) and place it adjacent to the Construction Yard so
210
- production stays online. Win = the Power Plant must be
211
- DESTROYED and then REBUILT, AND you must still own a
212
- Refinery, before tick 2700. Stalling, ignoring the rebuild,
213
- or spending the reserve on army units all miss the bar.
214
  starting_cash: 500
215
  overrides:
216
  actors:
@@ -270,20 +278,28 @@ levels:
270
  # placement cell (e.g. adjacent to the fact on the OPPOSITE side
271
  # from the strike lane). Reserve still 500 cr, deadline tightened.
272
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  description: >
274
- Same base as easy (fact, proc, low-HP powr, weap, harv, mine,
275
- one 3tnk defender, $500 reserve) but the enemy strike is now
276
- TWO heavy tanks adjacent to the Power Plant. The second tank
277
- guarantees the kill on the opening turn AND will engage a
278
- fresh Power Plant that you place too close to the strike
279
- lane. To recover: queue another Power Plant and place it
280
- ADJACENT TO THE CONSTRUCTION YARD on the far side from the
281
- strike lane (e.g. west of the fact). Win = the Power Plant
282
- must be DESTROYED and then REBUILT, AND you must still own a
283
- Refinery, before tick 2700. Stalling, ignoring the rebuild,
284
- placing the new powr in the strike lane (where the lingering
285
- 4tnks will destroy it again), or spending the reserve on
286
- army units all miss the bar.
287
  starting_cash: 500
288
  overrides:
289
  actors:
@@ -331,18 +347,25 @@ levels:
331
  # active agent assets. The active-latitude strikers do their
332
  # one-shot powr kill identically to medium.
333
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  description: >
335
- Same exogenous-loss-and-rebuild task as medium (2× 4tnk
336
- strike, low-HP Power Plant, $500 reserve, tick 2700 deadline)
337
- but your base may begin in the NORTH band (y≈14..22) OR the
338
- SOUTH band (y≈22..30) of the map depending on the seed. Read
339
- the Construction Yard's actual position from the observation
340
- and place the replacement Power Plant adjacent to it on the
341
- far side from the strike lane. A memorised "(10,18)" rebuild
342
- cell will mis-place out of build radius on one of the two
343
- spawns. Win = the Power Plant must be DESTROYED and then
344
- REBUILT, AND you must still own a Refinery, before tick
345
- 2700. The same lazy / wrong-spend plays lose as on medium.
346
  starting_cash: 500
347
  overrides:
348
  actors:
 
198
  # follow-on path). Reserve 500 cr funds the powr rebuild with
199
  # $200 slack. max_turns 30 → ceiling 2703 > 2700 ✓.
200
  easy:
201
+ # Original (pre-verbosity-sweep) description preserved for
202
+ # contributors. The trimmed version below removes scripted-policy
203
+ # spoilers and cell-coord dumps; load-bearing intent kept.
204
+ #
205
+ # You inherit a working production base — a Construction Yard
206
+ # ('fact'), an Ore Refinery ('proc'), a Power Plant ('powr'),
207
+ # a War Factory ('weap'), and one Ore Truck ('harv') with an
208
+ # ore patch — and $500 cash. The Power Plant is at LOW HEALTH
209
+ # and a single enemy heavy tank ('4tnk') is staged adjacent to
210
+ # it; the Power Plant will be DESTROYED in the opening turn.
211
+ # To recover: queue another Power Plant ('build' "powr",
212
+ # cost 300) and place it adjacent to the Construction Yard so
213
+ # production stays online. Win = the Power Plant must be
214
+ # DESTROYED and then REBUILT, AND you must still own a
215
+ # Refinery, before tick 2700. Stalling, ignoring the rebuild,
216
+ # or spending the reserve on army units all miss the bar.
217
  description: >
218
+ You inherit a full base and $500 cash. Your low-HP Power Plant
219
+ will be destroyed on the opening turn by an adjacent enemy heavy
220
+ tank. Rebuild the Power Plant and keep your Refinery within about
221
+ 30 turns.
 
 
 
 
 
 
 
 
222
  starting_cash: 500
223
  overrides:
224
  actors:
 
278
  # placement cell (e.g. adjacent to the fact on the OPPOSITE side
279
  # from the strike lane). Reserve still 500 cr, deadline tightened.
280
  medium:
281
+ # Original (pre-verbosity-sweep) description preserved for
282
+ # contributors. The trimmed version below removes scripted-policy
283
+ # spoilers and cell-coord dumps; load-bearing intent kept.
284
+ #
285
+ # Same base as easy (fact, proc, low-HP powr, weap, harv, mine,
286
+ # one 3tnk defender, $500 reserve) but the enemy strike is now
287
+ # TWO heavy tanks adjacent to the Power Plant. The second tank
288
+ # guarantees the kill on the opening turn AND will engage a
289
+ # fresh Power Plant that you place too close to the strike
290
+ # lane. To recover: queue another Power Plant and place it
291
+ # ADJACENT TO THE CONSTRUCTION YARD on the far side from the
292
+ # strike lane (e.g. west of the fact). Win = the Power Plant
293
+ # must be DESTROYED and then REBUILT, AND you must still own a
294
+ # Refinery, before tick 2700. Stalling, ignoring the rebuild,
295
+ # placing the new powr in the strike lane (where the lingering
296
+ # 4tnks will destroy it again), or spending the reserve on
297
+ # army units all miss the bar.
298
  description: >
299
+ Same base and $500 reserve, but TWO heavy tanks now sit beside
300
+ the low-HP Power Plant and will linger after the kill. Rebuild
301
+ the Power Plant somewhere it can survive, and keep your Refinery,
302
+ within about 30 turns.
 
 
 
 
 
 
 
 
 
303
  starting_cash: 500
304
  overrides:
305
  actors:
 
347
  # active agent assets. The active-latitude strikers do their
348
  # one-shot powr kill identically to medium.
349
  hard:
350
+ # Original (pre-verbosity-sweep) description preserved for
351
+ # contributors. The trimmed version below removes scripted-policy
352
+ # spoilers and cell-coord dumps; load-bearing intent kept.
353
+ #
354
+ # Same exogenous-loss-and-rebuild task as medium (2× 4tnk
355
+ # strike, low-HP Power Plant, $500 reserve, tick 2700 deadline)
356
+ # but your base may begin in the NORTH band (y≈14..22) OR the
357
+ # SOUTH band (y≈22..30) of the map depending on the seed. Read
358
+ # the Construction Yard's actual position from the observation
359
+ # and place the replacement Power Plant adjacent to it on the
360
+ # far side from the strike lane. A memorised "(10,18)" rebuild
361
+ # cell will mis-place out of build radius on one of the two
362
+ # spawns. Win = the Power Plant must be DESTROYED and then
363
+ # REBUILT, AND you must still own a Refinery, before tick
364
+ # 2700. The same lazy / wrong-spend plays lose as on medium.
365
  description: >
366
+ Same task as medium, but your base may sit in the NORTH or SOUTH
367
+ band depending on seed. Rebuild the Power Plant adjacent to your
368
+ Construction Yard, keep your Refinery, within about 30 turns.
 
 
 
 
 
 
 
 
369
  starting_cash: 500
370
  overrides:
371
  actors:
openra_bench/scenarios/packs/build-power-down-defensive.yaml CHANGED
@@ -122,16 +122,24 @@ levels:
122
  # leave proc + the load-bearing structures standing. max_turns 40 →
123
  # reachable tick 93 + 90·39 = 3603 > 3600 ✓.
124
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  description: >
126
- Your base is OVERDRAWN: 1 Power Plant (powr, +100) carries five
127
- drainers proc (−30), tent (−20), barr (−20), weap (−30),
128
- dome (−40) = drained 140, surplus −40 (production runs at 50%
129
- speed in low-power). Restore power_surplus_gte:0 by powering
130
- DOWN any combination of NON-ESSENTIAL drainers (tent + barr
131
- sheds 40 exactly; weap alone sheds 30; dome alone sheds 40).
132
- Do NOT sell anything — proc, tent, barr, weap, dome, fact must
133
- remain standing. Do NOT power_down the lone powr — provided
134
- power must stay ≥100. Deadline tick 3600.
135
  starting_cash: 1000
136
  overrides:
137
  actors:
@@ -201,17 +209,25 @@ levels:
201
  # selling, no shedding the powr). max_turns 36 → reachable tick
202
  # 3243 > 3240 ✓.
203
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  description: >
205
- Same overdrawn base, but heavier load drainers total 180
206
- (proc 30 + tent 20 + barr 20 + weap 30 + dome 40 + fix 30 +
207
- hpad 10 = 180). Surplus −80. You must shed ≥80 of load by
208
- powering DOWN non-essential drainers (e.g. weap + dome = 70 is
209
- NOT enough; weap + dome + tent = 90; dome + tent + barr +
210
- hpad = 90; dome + fix = 70 NOT enough; dome + fix + tent =
211
- 90). No single power_down restores surplus — combinations
212
- required. Do NOT sell anything — proc, tent, barr, weap,
213
- dome, fix, hpad, fact must remain standing. Provided must
214
- stay ≥100 (do not power_down the powr). Deadline tick 3240.
215
  starting_cash: 1000
216
  overrides:
217
  actors:
@@ -280,13 +296,21 @@ levels:
280
  # both spawn_point groups. The enemy marker has no spawn_point and
281
  # always places.
282
  hard:
 
 
 
 
 
 
 
 
 
 
283
  description: >
284
- Same overdrawn base as medium (surplus −90, shed ≥90 of load by
285
- powering DOWN), but the base latitude FLIPS between NORTH (y=12)
286
- and SOUTH (y=28) by seed. The building IDs you must target with
287
- `power_down` are at the spawn-specific coords read them from
288
- the observation. Do NOT sell anything; do NOT power_down the
289
- powr. Deadline tick 3240.
290
  starting_cash: 1000
291
  overrides:
292
  actors:
 
122
  # leave proc + the load-bearing structures standing. max_turns 40 →
123
  # reachable tick 93 + 90·39 = 3603 > 3600 ✓.
124
  easy:
125
+ # Original (pre-verbosity-sweep) description preserved for
126
+ # contributors. The trimmed version below removes scripted-policy
127
+ # spoilers and cell-coord dumps; load-bearing intent kept.
128
+ #
129
+ # Your base is OVERDRAWN: 1 Power Plant (powr, +100) carries five
130
+ # drainers — proc (−30), tent (−20), barr (−20), weap (−30),
131
+ # dome (−40) = drained 140, surplus −40 (production runs at 50%
132
+ # speed in low-power). Restore power_surplus_gte:0 by powering
133
+ # DOWN any combination of NON-ESSENTIAL drainers (tent + barr
134
+ # sheds 40 exactly; weap alone sheds 30; dome alone sheds 40).
135
+ # Do NOT sell anything — proc, tent, barr, weap, dome, fact must
136
+ # remain standing. Do NOT power_down the lone powr — provided
137
+ # power must stay ≥100. Deadline tick 3600.
138
  description: >
139
+ Your base is overdrawn by 40 power. Use power_down to shed enough
140
+ non-essential load to restore non-negative surplus within about
141
+ 40 turns. Do NOT sell any building; keep at least 100 provided
142
+ power (do not power down the Power Plant).
 
 
 
 
 
143
  starting_cash: 1000
144
  overrides:
145
  actors:
 
209
  # selling, no shedding the powr). max_turns 36 → reachable tick
210
  # 3243 > 3240 ✓.
211
  medium:
212
+ # Original (pre-verbosity-sweep) description preserved for
213
+ # contributors. The trimmed version below removes scripted-policy
214
+ # spoilers and cell-coord dumps; load-bearing intent kept.
215
+ #
216
+ # Same overdrawn base, but heavier load — drainers total 180
217
+ # (proc 30 + tent 20 + barr 20 + weap 30 + dome 40 + fix 30 +
218
+ # hpad 10 = 180). Surplus −80. You must shed ≥80 of load by
219
+ # powering DOWN non-essential drainers (e.g. weap + dome = 70 is
220
+ # NOT enough; weap + dome + tent = 90; dome + tent + barr +
221
+ # hpad = 90; dome + fix = 70 NOT enough; dome + fix + tent =
222
+ # 90). No single power_down restores surplus — combinations
223
+ # required. Do NOT sell anything — proc, tent, barr, weap,
224
+ # dome, fix, hpad, fact must remain standing. Provided must
225
+ # stay ≥100 (do not power_down the powr). Deadline tick 3240.
226
  description: >
227
+ Heavier overdraw (surplus -80) on a larger base. Power down
228
+ enough non-essential buildings to restore non-negative surplus
229
+ within about 36 turns. No single shed will do it. Sell nothing;
230
+ keep at least 100 provided power.
 
 
 
 
 
 
231
  starting_cash: 1000
232
  overrides:
233
  actors:
 
296
  # both spawn_point groups. The enemy marker has no spawn_point and
297
  # always places.
298
  hard:
299
+ # Original (pre-verbosity-sweep) description preserved for
300
+ # contributors. The trimmed version below removes scripted-policy
301
+ # spoilers and cell-coord dumps; load-bearing intent kept.
302
+ #
303
+ # Same overdrawn base as medium (surplus −90, shed ≥90 of load by
304
+ # powering DOWN), but the base latitude FLIPS between NORTH (y=12)
305
+ # and SOUTH (y=28) by seed. The building IDs you must target with
306
+ # `power_down` are at the spawn-specific coords — read them from
307
+ # the observation. Do NOT sell anything; do NOT power_down the
308
+ # powr. Deadline tick 3240.
309
  description: >
310
+ Same overdraw as medium, but base latitude flips between north
311
+ and south by seed. Read the buildings from the observation, power
312
+ down enough to restore non-negative surplus within about 36
313
+ turns. Sell nothing; keep at least 100 provided power.
 
 
314
  starting_cash: 1000
315
  overrides:
316
  actors:
openra_bench/scenarios/packs/build-power-online-first.yaml CHANGED
@@ -128,15 +128,22 @@ levels:
128
  # the chain; the model cannot also blow the budget on a single 400-
129
  # cost mis-build (tent) and still complete the chain.
130
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
131
  description: >
132
- You start with a Construction Yard ('fact') at (20,20) and
133
- $1900. Build a Power Plant ('powr', cost 300) FIRST, then an
134
- Ore Refinery ('proc', cost 1400). Win = own a 'powr' AND own
135
- a 'proc' IN THAT ORDER (the powr must appear FIRST), before
136
- tick 3600. The Refinery REQUIRES a Power Plant — queuing
137
- 'proc' first is silently rejected by the engine. Stalling,
138
- building infantry/army, or attempting the Refinery before the
139
- Power Plant all miss the clock and LOSE.
140
  starting_cash: 1900
141
  overrides:
142
  actors:
@@ -165,14 +172,20 @@ levels:
165
  # powr that the model rebuilds twice does drain) tightens the
166
  # margin. max_turns 32 → reachable tick 93 + 90·31 = 2883 > 2880 ✓.
167
  medium:
 
 
 
 
 
 
 
 
 
 
 
168
  description: >
169
- Same opening: 'fact' at (20,20), no power, no economy. Build
170
- 'powr' FIRST (cost 300), THEN 'proc' (cost 1400). Tighter
171
- budget ($1750 — covers the chain with $50 to spare) and
172
- tighter clock (tick 2880). The Refinery REQUIRES the Power
173
- Plant — queuing 'proc' first is silently rejected. Stalling,
174
- army-building, or attempting 'proc' before 'powr' all run out
175
- the clock and LOSE.
176
  starting_cash: 1750
177
  overrides:
178
  actors:
@@ -203,16 +216,23 @@ levels:
203
  # read from the actual fact position. max_turns 32 → reachable
204
  # tick 2883 > 2880 ✓.
205
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  description: >
207
- Same opening as medium (powr FIRST then proc, tight $1750
208
- budget, tick 2880 clock) but your Construction Yard may begin
209
- in the NORTH band (y≈12) or the SOUTH band (y≈28) of the map
210
- depending on the seed. Read the fact's actual position from
211
- the observation and place 'powr' and 'proc' adjacent to it.
212
- A memorised "(20,20)" opening will mis-place the buildings
213
- out-of-radius on one of the two spawns. The Refinery still
214
- REQUIRES the Power Plant; out-of-order builds are silently
215
- rejected and the clock runs out as a LOSS.
216
  starting_cash: 1750
217
  overrides:
218
  actors:
 
128
  # the chain; the model cannot also blow the budget on a single 400-
129
  # cost mis-build (tent) and still complete the chain.
130
  easy:
131
+ # Original (pre-verbosity-sweep) description preserved for
132
+ # contributors. The trimmed version below removes scripted-policy
133
+ # spoilers and cell-coord dumps; load-bearing intent kept.
134
+ #
135
+ # You start with a Construction Yard ('fact') at (20,20) and
136
+ # $1900. Build a Power Plant ('powr', cost 300) FIRST, then an
137
+ # Ore Refinery ('proc', cost 1400). Win = own a 'powr' AND own
138
+ # a 'proc' IN THAT ORDER (the powr must appear FIRST), before
139
+ # tick 3600. The Refinery REQUIRES a Power Plant — queuing
140
+ # 'proc' first is silently rejected by the engine. Stalling,
141
+ # building infantry/army, or attempting the Refinery before the
142
+ # Power Plant all miss the clock and LOSE.
143
  description: >
144
+ Start with a Construction Yard and $1900. Build a Power Plant,
145
+ then an Ore Refinery, within about 40 turns. The Refinery
146
+ requires a Power Plant first.
 
 
 
 
 
147
  starting_cash: 1900
148
  overrides:
149
  actors:
 
172
  # powr that the model rebuilds twice does drain) tightens the
173
  # margin. max_turns 32 → reachable tick 93 + 90·31 = 2883 > 2880 ✓.
174
  medium:
175
+ # Original (pre-verbosity-sweep) description preserved for
176
+ # contributors. The trimmed version below removes scripted-policy
177
+ # spoilers and cell-coord dumps; load-bearing intent kept.
178
+ #
179
+ # Same opening: 'fact' at (20,20), no power, no economy. Build
180
+ # 'powr' FIRST (cost 300), THEN 'proc' (cost 1400). Tighter
181
+ # budget ($1750 — covers the chain with $50 to spare) and
182
+ # tighter clock (tick 2880). The Refinery REQUIRES the Power
183
+ # Plant — queuing 'proc' first is silently rejected. Stalling,
184
+ # army-building, or attempting 'proc' before 'powr' all run out
185
+ # the clock and LOSE.
186
  description: >
187
+ Same opening with tighter cash ($1750) and a tighter clock
188
+ (about 32 turns). Build a Power Plant, then an Ore Refinery.
 
 
 
 
 
189
  starting_cash: 1750
190
  overrides:
191
  actors:
 
216
  # read from the actual fact position. max_turns 32 → reachable
217
  # tick 2883 > 2880 ✓.
218
  hard:
219
+ # Original (pre-verbosity-sweep) description preserved for
220
+ # contributors. The trimmed version below removes scripted-policy
221
+ # spoilers and cell-coord dumps; load-bearing intent kept.
222
+ #
223
+ # Same opening as medium (powr FIRST then proc, tight $1750
224
+ # budget, tick 2880 clock) but your Construction Yard may begin
225
+ # in the NORTH band (y≈12) or the SOUTH band (y≈28) of the map
226
+ # depending on the seed. Read the fact's actual position from
227
+ # the observation and place 'powr' and 'proc' adjacent to it.
228
+ # A memorised "(20,20)" opening will mis-place the buildings
229
+ # out-of-radius on one of the two spawns. The Refinery still
230
+ # REQUIRES the Power Plant; out-of-order builds are silently
231
+ # rejected and the clock runs out as a LOSS.
232
  description: >
233
+ Same task as medium ($1750, about 32 turns), but your Construction
234
+ Yard sits in the north or south band depending on seed. Read its
235
+ position from the observation and place buildings adjacent to it.
 
 
 
 
 
 
236
  starting_cash: 1750
237
  overrides:
238
  actors:
openra_bench/scenarios/packs/build-production-throughput-multibuilding.yaml CHANGED
@@ -147,18 +147,25 @@ levels:
147
  # 6th well inside the window. max_turns 35 → reachable tick
148
  # 93 + 90·34 = 3153 > 2701 ✓ (the deadline bites as a real LOSS).
149
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  description: >
151
- You have a full base Construction Yard (fact), Refinery
152
- (proc), Power Plant (powr), Service Depot (fix) and ONE War
153
- Factory (weap) and $9000. Field 6 medium tanks (2tnk, cost
154
- $850 each) before tick 2700. ONE war factory cannot build 6
155
- tanks in time — it is a single serial production line. You have
156
- cash for a SECOND war factory ($2000); two factories produce
157
- vehicles IN PARALLEL and roughly double your output. Build the
158
- second weap, place it, and keep BOTH queues full. Win = own ≥6
159
- medium tanks AND your fact still stands, before tick 2700.
160
- Stalling or spamming tanks from the single factory both miss
161
- the quota and LOSE on the clock.
162
  starting_cash: 9000
163
  overrides:
164
  actors:
@@ -188,14 +195,20 @@ levels:
188
  # by then, comfortable WIN). Same quota, same base. max_turns 33 →
189
  # reachable tick 93 + 90·32 = 2973 > 2614 ✓.
190
  medium:
 
 
 
 
 
 
 
 
 
 
 
191
  description: >
192
- Same base as easy (fact + proc + powr + fix + ONE weap, $9000)
193
- but a tighter deadline: field 6 medium tanks (2tnk) before
194
- tick 2613. A single war factory clears only ~5 tanks by then.
195
- Build a SECOND war factory and feed both queues — two factories
196
- produce vehicles in parallel and double your throughput. Win =
197
- own ≥6 medium tanks AND your fact still stands, before tick
198
- 2613. Stall or single-factory spam both LOSE on the clock.
199
  starting_cash: 9000
200
  overrides:
201
  actors:
@@ -228,17 +241,24 @@ levels:
228
  # the spawn latitude varies. max_turns 33 → reachable tick 2973
229
  # > 2614 ✓.
230
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  description: >
232
- Same throughput call as medium (field 6 medium tanks before
233
- tick 2613 from a base with ONE pre-placed war factory; build a
234
- SECOND for parallel output) but your base may begin in the
235
- NORTH band (y≈14) or the SOUTH band (y≈26) depending on the
236
- seed. Read your War Factory's actual position from the
237
- observation and place the second weap adjacent to it. A single
238
- factory clears only ~5 tanks by the deadline; build the second
239
- and feed both queues. Win = own ≥6 medium tanks AND your fact
240
- still stands, before tick 2613. Stall or single-factory spam
241
- both LOSE on the clock.
242
  starting_cash: 9000
243
  overrides:
244
  actors:
 
147
  # 6th well inside the window. max_turns 35 → reachable tick
148
  # 93 + 90·34 = 3153 > 2701 ✓ (the deadline bites as a real LOSS).
149
  easy:
150
+ # Original (pre-verbosity-sweep) description preserved for
151
+ # contributors. The trimmed version below removes scripted-policy
152
+ # spoilers and cell-coord dumps; load-bearing intent kept.
153
+ #
154
+ # You have a full base — Construction Yard (fact), Refinery
155
+ # (proc), Power Plant (powr), Service Depot (fix) and ONE War
156
+ # Factory (weap) — and $9000. Field 6 medium tanks (2tnk, cost
157
+ # $850 each) before tick 2700. ONE war factory cannot build 6
158
+ # tanks in time — it is a single serial production line. You have
159
+ # cash for a SECOND war factory ($2000); two factories produce
160
+ # vehicles IN PARALLEL and roughly double your output. Build the
161
+ # second weap, place it, and keep BOTH queues full. Win = own ≥6
162
+ # medium tanks AND your fact still stands, before tick 2700.
163
+ # Stalling or spamming tanks from the single factory both miss
164
+ # the quota and LOSE on the clock.
165
  description: >
166
+ Full base with one War Factory and $9000. Field six medium tanks
167
+ (2tnk) within about 30 turns, keeping your construction yard. One
168
+ factory cannot meet the deadline.
 
 
 
 
 
 
 
 
169
  starting_cash: 9000
170
  overrides:
171
  actors:
 
195
  # by then, comfortable WIN). Same quota, same base. max_turns 33 →
196
  # reachable tick 93 + 90·32 = 2973 > 2614 ✓.
197
  medium:
198
+ # Original (pre-verbosity-sweep) description preserved for
199
+ # contributors. The trimmed version below removes scripted-policy
200
+ # spoilers and cell-coord dumps; load-bearing intent kept.
201
+ #
202
+ # Same base as easy (fact + proc + powr + fix + ONE weap, $9000)
203
+ # but a tighter deadline: field 6 medium tanks (2tnk) before
204
+ # tick 2613. A single war factory clears only ~5 tanks by then.
205
+ # Build a SECOND war factory and feed both queues — two factories
206
+ # produce vehicles in parallel and double your throughput. Win =
207
+ # own ≥6 medium tanks AND your fact still stands, before tick
208
+ # 2613. Stall or single-factory spam both LOSE on the clock.
209
  description: >
210
+ Same base as easy with a tighter deadline. Field six medium tanks
211
+ within about 29 turns, keeping your construction yard.
 
 
 
 
 
212
  starting_cash: 9000
213
  overrides:
214
  actors:
 
241
  # the spawn latitude varies. max_turns 33 → reachable tick 2973
242
  # > 2614 ✓.
243
  hard:
244
+ # Original (pre-verbosity-sweep) description preserved for
245
+ # contributors. The trimmed version below removes scripted-policy
246
+ # spoilers and cell-coord dumps; load-bearing intent kept.
247
+ #
248
+ # Same throughput call as medium (field 6 medium tanks before
249
+ # tick 2613 from a base with ONE pre-placed war factory; build a
250
+ # SECOND for parallel output) but your base may begin in the
251
+ # NORTH band (y≈14) or the SOUTH band (y≈26) depending on the
252
+ # seed. Read your War Factory's actual position from the
253
+ # observation and place the second weap adjacent to it. A single
254
+ # factory clears only ~5 tanks by the deadline; build the second
255
+ # and feed both queues. Win = own ≥6 medium tanks AND your fact
256
+ # still stands, before tick 2613. Stall or single-factory spam
257
+ # both LOSE on the clock.
258
  description: >
259
+ Same task as medium, but your base may sit in the north or south
260
+ band depending on seed. Field six medium tanks within about 29
261
+ turns, keeping your construction yard.
 
 
 
 
 
 
 
262
  starting_cash: 9000
263
  overrides:
264
  actors:
openra_bench/scenarios/packs/build-rally-point-management.yaml CHANGED
@@ -135,23 +135,31 @@ levels:
135
  # (23,21)..(26,25), never enter region, never engage barr → LOSS
136
  # on the deadline.
137
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  description: >
139
- Production logistics opening. You start with a Construction
140
- Yard ('fact') at (20,20), a Power Plant ('powr') at (22,22),
141
- and a Barracks ('tent') at (24,22) total cash $1500. An
142
- enemy Barracks ('barr', the kill target) sits at the FORWARD
143
- staging area (62,20) ≈38 cells east of your tent. Win = at
144
- least 3 of your units inside a radius-5 disc around (62,20)
145
- AND ≥1 enemy unit/building killed AND before tick 2700. The
146
- tent's default rally point is right next to the tent —
147
- freshly-built infantry pile at (23–26, 21–25) and never
148
- engage. You must call set_rally_point on the tent with
149
- target (62,20) BEFORE (or right after) you queue the first
150
- Rifle Infantry ('e1', cost 100), so every subsequent unit
151
- walks to the forward zone and chews the enemy barr down.
152
- Stalling, building without setting the rally forward, or
153
- setting the rally to a near-base cell all miss the deadline
154
- and LOSE.
155
  starting_cash: 1500
156
  overrides:
157
  actors:
@@ -185,17 +193,24 @@ levels:
185
  # but a model that stalls for even a few turns before setting the
186
  # rally misses the window. Stall / no-rally still LOSE outright.
187
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  description: >
189
- Same opening — fact (20,20), powr (22,22), tent (24,22),
190
- $1500 but the SLA tightens. Enemy 'barr' kill target at
191
- (62,20); win requires ≥3 of your units inside a radius-5
192
- disc around (62,20) AND ≥1 kill AND before tick 1900 (tight
193
- — a few stall turns before the rally call will miss the
194
- window). Tent default rally piles units at the base; you
195
- must call set_rally_point on the tent with target (62,20)
196
- and queue infantry ('e1', cost 100) IMMEDIATELY. Stall,
197
- no-rally, or rally-to-a-near-base-cell all LOSE on the
198
- deadline.
199
  starting_cash: 1500
200
  overrides:
201
  actors:
@@ -228,20 +243,28 @@ levels:
228
  # the centreline (62,20)). Same tight clock as medium. max_turns
229
  # 22 → reachable tick 1983 > 1900 ✓.
230
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
231
  description: >
232
- Same SLA-tight opening as medium (forward zone (62,20),
233
- enemy 'barr' there, ≥3 units in r=5 disc + ≥1 kill before
234
- tick 1900) but your Construction Yard, Power Plant, and
235
- Barracks may begin in the NORTH band (y≈16) or the SOUTH
236
- band (y≈28) of the map depending on the seed. Read the
237
- tent's actual position from the observation and call
238
- set_rally_point on the tent with target (62,20) — the
239
- centreline forward zone is fixed regardless of which spawn
240
- you got. A memorised "set rally from y=22" opening will
241
- still target (62,20), but your tent id and starting cells
242
- will differ per seed. The Barracks default rally piles
243
- units at the base; without an explicit forward rally call
244
- the units never reach (62,20) and the SLA is missed.
245
  starting_cash: 1500
246
  overrides:
247
  actors:
 
135
  # (23,21)..(26,25), never enter region, never engage barr → LOSS
136
  # on the deadline.
137
  easy:
138
+ # Original (pre-verbosity-sweep) description preserved for
139
+ # contributors. The trimmed version below removes scripted-policy
140
+ # spoilers and cell-coord dumps; load-bearing intent kept.
141
+ #
142
+ # Production logistics opening. You start with a Construction
143
+ # Yard ('fact') at (20,20), a Power Plant ('powr') at (22,22),
144
+ # and a Barracks ('tent') at (24,22) — total cash $1500. An
145
+ # enemy Barracks ('barr', the kill target) sits at the FORWARD
146
+ # staging area (62,20) ≈38 cells east of your tent. Win = at
147
+ # least 3 of your units inside a radius-5 disc around (62,20)
148
+ # AND ≥1 enemy unit/building killed AND before tick 2700. The
149
+ # tent's default rally point is right next to the tent —
150
+ # freshly-built infantry pile at (23–26, 21–25) and never
151
+ # engage. You must call set_rally_point on the tent with
152
+ # target (62,20) BEFORE (or right after) you queue the first
153
+ # Rifle Infantry ('e1', cost 100), so every subsequent unit
154
+ # walks to the forward zone and chews the enemy barr down.
155
+ # Stalling, building without setting the rally forward, or
156
+ # setting the rally to a near-base cell all miss the deadline
157
+ # and LOSE.
158
  description: >
159
+ You have a Construction Yard, Power Plant, and Barracks with
160
+ $1500. An enemy barracks sits at the forward zone (62,20). Get
161
+ at least three of your units into a radius-5 disc around (62,20)
162
+ and kill one enemy, within about 30 turns.
 
 
 
 
 
 
 
 
 
 
 
 
163
  starting_cash: 1500
164
  overrides:
165
  actors:
 
193
  # but a model that stalls for even a few turns before setting the
194
  # rally misses the window. Stall / no-rally still LOSE outright.
195
  medium:
196
+ # Original (pre-verbosity-sweep) description preserved for
197
+ # contributors. The trimmed version below removes scripted-policy
198
+ # spoilers and cell-coord dumps; load-bearing intent kept.
199
+ #
200
+ # Same opening — fact (20,20), powr (22,22), tent (24,22),
201
+ # $1500 — but the SLA tightens. Enemy 'barr' kill target at
202
+ # (62,20); win requires ≥3 of your units inside a radius-5
203
+ # disc around (62,20) AND ≥1 kill AND before tick 1900 (tight
204
+ # — a few stall turns before the rally call will miss the
205
+ # window). Tent default rally piles units at the base; you
206
+ # must call set_rally_point on the tent with target (62,20)
207
+ # and queue infantry ('e1', cost 100) IMMEDIATELY. Stall,
208
+ # no-rally, or rally-to-a-near-base-cell all LOSE on the
209
+ # deadline.
210
  description: >
211
+ Same opening with a tighter clock about 21 turns. Get at least
212
+ three units into a radius-5 disc around the forward zone (62,20)
213
+ and kill one enemy.
 
 
 
 
 
 
 
214
  starting_cash: 1500
215
  overrides:
216
  actors:
 
243
  # the centreline (62,20)). Same tight clock as medium. max_turns
244
  # 22 → reachable tick 1983 > 1900 ✓.
245
  hard:
246
+ # Original (pre-verbosity-sweep) description preserved for
247
+ # contributors. The trimmed version below removes scripted-policy
248
+ # spoilers and cell-coord dumps; load-bearing intent kept.
249
+ #
250
+ # Same SLA-tight opening as medium (forward zone (62,20),
251
+ # enemy 'barr' there, ≥3 units in r=5 disc + ≥1 kill before
252
+ # tick 1900) but your Construction Yard, Power Plant, and
253
+ # Barracks may begin in the NORTH band (y≈16) or the SOUTH
254
+ # band (y≈28) of the map depending on the seed. Read the
255
+ # tent's actual position from the observation and call
256
+ # set_rally_point on the tent with target (62,20) — the
257
+ # centreline forward zone is fixed regardless of which spawn
258
+ # you got. A memorised "set rally from y=22" opening will
259
+ # still target (62,20), but your tent id and starting cells
260
+ # will differ per seed. The Barracks default rally piles
261
+ # units at the base; without an explicit forward rally call
262
+ # the units never reach (62,20) and the SLA is missed.
263
  description: >
264
+ Same task as medium, but your base sits in the north or south
265
+ band depending on seed. The forward zone (62,20) is the same
266
+ either way. Get three units there and kill one enemy within
267
+ about 21 turns.
 
 
 
 
 
 
 
 
 
268
  starting_cash: 1500
269
  overrides:
270
  actors:
openra_bench/scenarios/packs/build-repair-priority-under-fire.yaml CHANGED
@@ -181,20 +181,28 @@ levels:
181
  # 2400]; max_turns 28 → reachable tick 93 + 90·27 = 2523, so the
182
  # after_ticks 2401 fail bites.
183
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  description: >
185
- Three of your structures are under attrition: the refinery
186
- (proc), the war factory (weap), and the pillbox (pbox). The
187
- pillbox is already damaged to about 30% HP and looks the most
188
- damaged but it is low value, heavily armoured, and the
189
- grenadiers barely scratch it; it survives on its own. The
190
- refinery is on a lethal trajectory: it dies within a few turns
191
- unless you toggle REPAIR on it now. Repairing the refinery also
192
- keeps its attackers pinned, so the war factory stays safe. Win
193
- when the refinery and the war factory are both still standing
194
- after tick 1200 and before tick 2400. If the refinery falls, or
195
- the clock runs out with the win unmet, you lose. Stalling and
196
- repairing the most-damaged-looking building (the pillbox) both
197
- lose the refinery.
198
  overrides:
199
  actors:
200
  # proc — HIGH value, LETHAL trajectory. 3 grenadiers kill the
@@ -241,18 +249,26 @@ levels:
241
  # max_turns 31 → reachable tick 93 + 90·30 = 2793, so after_ticks
242
  # 2701 bites.
243
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  description: >
245
- Three structures are under attrition: the refinery (proc), the
246
- war factory (weap), and the pillbox (pbox). The pillbox is
247
- already damaged to about 30% HP and looks the most damaged — but
248
- it is low value, heavily armoured, and survives on its own. The
249
- refinery AND the war factory are both on lethal trajectories:
250
- each dies within a few turns unless you toggle REPAIR on it. Win
251
- when the refinery and the war factory are both still standing
252
- after tick 1700 and before tick 2700. If either falls, or the
253
- clock runs out with the win unmet, you lose. Stalling, repairing
254
- only the pillbox, and repairing only one of the two critical
255
- buildings all lose.
256
  overrides:
257
  actors:
258
  - {type: proc, owner: agent, position: [20, 8]}
@@ -292,19 +308,26 @@ levels:
292
  # agent unit (the base is otherwise building-only). Same composition
293
  # and clock as medium (proc + weap both lethal; pbox decoy).
294
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
295
  description: >
296
- Three structures the refinery (proc), the war factory (weap),
297
- and the pillbox (pbox) are under attrition from three grenadier
298
- bands. Your base stages from a seed-chosen longitude, so a single
299
- memorised opening cannot generalise. The pillbox is pre-damaged
300
- to ~30% HP and looks the most damaged but is low value, heavily
301
- armoured, and survives on its own; the refinery and the war
302
- factory are both on lethal trajectories and each needs REPAIR.
303
- Win when the refinery and the war factory are both still standing
304
- after tick 1700 and before tick 2700. If either falls, or the
305
- clock runs out with the win unmet, you lose. Stalling, repairing
306
- only the pillbox, and repairing only one critical building all
307
- lose.
308
  overrides:
309
  actors:
310
  # WEST base group (spawn_point 0). Full base + bands duplicated
 
181
  # 2400]; max_turns 28 → reachable tick 93 + 90·27 = 2523, so the
182
  # after_ticks 2401 fail bites.
183
  easy:
184
+ # Original (pre-verbosity-sweep) description preserved for
185
+ # contributors. The trimmed version below removes scripted-policy
186
+ # spoilers and cell-coord dumps; load-bearing intent kept.
187
+ #
188
+ # Three of your structures are under attrition: the refinery
189
+ # (proc), the war factory (weap), and the pillbox (pbox). The
190
+ # pillbox is already damaged to about 30% HP and looks the most
191
+ # damaged — but it is low value, heavily armoured, and the
192
+ # grenadiers barely scratch it; it survives on its own. The
193
+ # refinery is on a lethal trajectory: it dies within a few turns
194
+ # unless you toggle REPAIR on it now. Repairing the refinery also
195
+ # keeps its attackers pinned, so the war factory stays safe. Win
196
+ # when the refinery and the war factory are both still standing
197
+ # after tick 1200 and before tick 2400. If the refinery falls, or
198
+ # the clock runs out with the win unmet, you lose. Stalling and
199
+ # repairing the most-damaged-looking building (the pillbox) both
200
+ # lose the refinery.
201
  description: >
202
+ Three of your buildings refinery, war factory, and pillbox —
203
+ are taking fire. The pillbox starts at 30% HP. Use repair to
204
+ keep the refinery and war factory both alive from turn 14 to
205
+ about turn 27.
 
 
 
 
 
 
 
 
 
206
  overrides:
207
  actors:
208
  # proc — HIGH value, LETHAL trajectory. 3 grenadiers kill the
 
249
  # max_turns 31 → reachable tick 93 + 90·30 = 2793, so after_ticks
250
  # 2701 bites.
251
  medium:
252
+ # Original (pre-verbosity-sweep) description preserved for
253
+ # contributors. The trimmed version below removes scripted-policy
254
+ # spoilers and cell-coord dumps; load-bearing intent kept.
255
+ #
256
+ # Three structures are under attrition: the refinery (proc), the
257
+ # war factory (weap), and the pillbox (pbox). The pillbox is
258
+ # already damaged to about 30% HP and looks the most damaged — but
259
+ # it is low value, heavily armoured, and survives on its own. The
260
+ # refinery AND the war factory are both on lethal trajectories:
261
+ # each dies within a few turns unless you toggle REPAIR on it. Win
262
+ # when the refinery and the war factory are both still standing
263
+ # after tick 1700 and before tick 2700. If either falls, or the
264
+ # clock runs out with the win unmet, you lose. Stalling, repairing
265
+ # only the pillbox, and repairing only one of the two critical
266
+ # buildings all lose.
267
  description: >
268
+ Same three buildings under fire, but now both the refinery and
269
+ war factory are on lethal trajectories. The pillbox is the
270
+ pre-damaged decoy. Keep the refinery and war factory alive from
271
+ turn 19 to about turn 30.
 
 
 
 
 
 
 
272
  overrides:
273
  actors:
274
  - {type: proc, owner: agent, position: [20, 8]}
 
308
  # agent unit (the base is otherwise building-only). Same composition
309
  # and clock as medium (proc + weap both lethal; pbox decoy).
310
  hard:
311
+ # Original (pre-verbosity-sweep) description preserved for
312
+ # contributors. The trimmed version below removes scripted-policy
313
+ # spoilers and cell-coord dumps; load-bearing intent kept.
314
+ #
315
+ # Three structures — the refinery (proc), the war factory (weap),
316
+ # and the pillbox (pbox) — are under attrition from three grenadier
317
+ # bands. Your base stages from a seed-chosen longitude, so a single
318
+ # memorised opening cannot generalise. The pillbox is pre-damaged
319
+ # to ~30% HP and looks the most damaged but is low value, heavily
320
+ # armoured, and survives on its own; the refinery and the war
321
+ # factory are both on lethal trajectories and each needs REPAIR.
322
+ # Win when the refinery and the war factory are both still standing
323
+ # after tick 1700 and before tick 2700. If either falls, or the
324
+ # clock runs out with the win unmet, you lose. Stalling, repairing
325
+ # only the pillbox, and repairing only one critical building all
326
+ # lose.
327
  description: >
328
+ Same task as medium, but your base sits at a seed-chosen
329
+ longitude. Keep the refinery and war factory alive from turn 19
330
+ to about turn 30.
 
 
 
 
 
 
 
 
 
331
  overrides:
332
  actors:
333
  # WEST base group (spawn_point 0). Full base + bands duplicated
openra_bench/scenarios/packs/build-sell-and-rebuild-elsewhere.yaml CHANGED
@@ -163,20 +163,28 @@ levels:
163
  # paired with after_ticks 4501 in fail ⇒ a non-finisher is a real
164
  # reachable timeout LOSS (not a draw).
165
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  description: >
167
- You own a Construction Yard (fact) and a Power Plant (powr)
168
- at the safe far north-west corner and a forward Refinery
169
- (proc) at the centre lane. A small hunt band (2 rifle
170
- infantry) is incoming from the east on the centre lane and
171
- will raze the refinery within ~25-30 turns. Your starting
172
- cash is 800 — not enough to build a new refinery (cost
173
- 1400). SELL the exposed refinery (refunds 700) and use the
174
- recouped cash + starting cash to BUILD a new refinery at
175
- the safe target region around (16, 8) — north of the rush
176
- lane. Win by having a refinery at the safe region AND the
177
- Construction Yard still alive AND before tick 4500. Stall,
178
- build-without-selling (cash gated), or placing the new
179
- refinery anywhere outside the safe region all lose.
180
  starting_cash: 800
181
  overrides:
182
  actors:
@@ -231,19 +239,26 @@ levels:
231
  # of ~25-30. Same tick budget so the win window is tighter against
232
  # the same after_ticks 5401 fail.
233
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  description: >
235
- You own a Construction Yard and a Power Plant at the safe
236
- far north-west corner and a forward Refinery at the centre
237
- lane. A heavier hunt band (3 rifle infantry) is incoming
238
- and will raze the refinery faster (~20 turns). Your
239
- starting cash is 700 — exactly the sell refund of a
240
- refinery, half the build cost. You MUST sell the exposed
241
- refinery to free the second half of the cash, then build a
242
- new refinery at the safe target region around (16, 8). Win
243
- by having a refinery at the safe region AND the Construction
244
- Yard still alive AND before tick 4500. Stalling, building
245
- without selling (cash blocks the build), or placing the new
246
- refinery in the central lane all lose.
247
  starting_cash: 700
248
  overrides:
249
  actors:
@@ -276,20 +291,28 @@ levels:
276
  # (16, 8)" cell loses on the SOUTH spawn (the safe region there
277
  # is (16, 36) not (16, 8)).
278
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  description: >
280
- Your base stages from a seed-chosen latitude (NORTH y=4 or
281
- SOUTH y=36) a single memorised target cell cannot
282
- generalise. You own a Construction Yard and a Power Plant
283
- at the safe corner of your latitude and a forward Refinery
284
- at the centre lane. Hunt bands are incoming on the centre
285
- lane and will raze the refinery within ~20 turns. Your
286
- starting cash is 700 — exactly half the refinery build
287
- cost. You MUST sell the exposed refinery and use the
288
- recouped cash to build a new refinery at the safe target
289
- region of your OWN latitude (around (16, 8) for the NORTH
290
- spawn, (16, 36) for the SOUTH spawn). Win by having a
291
- refinery at the safe region of your latitude AND the
292
- Construction Yard still alive AND before tick 4500.
293
  starting_cash: 700
294
  overrides:
295
  actors:
 
163
  # paired with after_ticks 4501 in fail ⇒ a non-finisher is a real
164
  # reachable timeout LOSS (not a draw).
165
  easy:
166
+ # Original (pre-verbosity-sweep) description preserved for
167
+ # contributors. The trimmed version below removes scripted-policy
168
+ # spoilers and cell-coord dumps; load-bearing intent kept.
169
+ #
170
+ # You own a Construction Yard (fact) and a Power Plant (powr)
171
+ # at the safe far north-west corner and a forward Refinery
172
+ # (proc) at the centre lane. A small hunt band (2 rifle
173
+ # infantry) is incoming from the east on the centre lane and
174
+ # will raze the refinery within ~25-30 turns. Your starting
175
+ # cash is 800 — not enough to build a new refinery (cost
176
+ # 1400). SELL the exposed refinery (refunds 700) and use the
177
+ # recouped cash + starting cash to BUILD a new refinery at
178
+ # the safe target region around (16, 8) — north of the rush
179
+ # lane. Win by having a refinery at the safe region AND the
180
+ # Construction Yard still alive AND before tick 4500. Stall,
181
+ # build-without-selling (cash gated), or placing the new
182
+ # refinery anywhere outside the safe region all lose.
183
  description: >
184
+ Your forward refinery is on the centre lane in the path of an
185
+ incoming rifle band; $800 alone cannot fund a new refinery. End
186
+ up with a refinery near (16,8) in the safe north-west corner and
187
+ keep your construction yard, within about 50 turns.
 
 
 
 
 
 
 
 
 
188
  starting_cash: 800
189
  overrides:
190
  actors:
 
239
  # of ~25-30. Same tick budget so the win window is tighter against
240
  # the same after_ticks 5401 fail.
241
  medium:
242
+ # Original (pre-verbosity-sweep) description preserved for
243
+ # contributors. The trimmed version below removes scripted-policy
244
+ # spoilers and cell-coord dumps; load-bearing intent kept.
245
+ #
246
+ # You own a Construction Yard and a Power Plant at the safe
247
+ # far north-west corner and a forward Refinery at the centre
248
+ # lane. A heavier hunt band (3 rifle infantry) is incoming
249
+ # and will raze the refinery faster (~20 turns). Your
250
+ # starting cash is 700 — exactly the sell refund of a
251
+ # refinery, half the build cost. You MUST sell the exposed
252
+ # refinery to free the second half of the cash, then build a
253
+ # new refinery at the safe target region around (16, 8). Win
254
+ # by having a refinery at the safe region AND the Construction
255
+ # Yard still alive AND before tick 4500. Stalling, building
256
+ # without selling (cash blocks the build), or placing the new
257
+ # refinery in the central lane all lose.
258
  description: >
259
+ Heavier rifle band on the centre lane and only $700 cash. End up
260
+ with a refinery near (16,8) in the safe north-west corner and
261
+ keep your construction yard, within about 50 turns.
 
 
 
 
 
 
 
 
 
262
  starting_cash: 700
263
  overrides:
264
  actors:
 
291
  # (16, 8)" cell loses on the SOUTH spawn (the safe region there
292
  # is (16, 36) not (16, 8)).
293
  hard:
294
+ # Original (pre-verbosity-sweep) description preserved for
295
+ # contributors. The trimmed version below removes scripted-policy
296
+ # spoilers and cell-coord dumps; load-bearing intent kept.
297
+ #
298
+ # Your base stages from a seed-chosen latitude (NORTH y=4 or
299
+ # SOUTH y=36) — a single memorised target cell cannot
300
+ # generalise. You own a Construction Yard and a Power Plant
301
+ # at the safe corner of your latitude and a forward Refinery
302
+ # at the centre lane. Hunt bands are incoming on the centre
303
+ # lane and will raze the refinery within ~20 turns. Your
304
+ # starting cash is 700 — exactly half the refinery build
305
+ # cost. You MUST sell the exposed refinery and use the
306
+ # recouped cash to build a new refinery at the safe target
307
+ # region of your OWN latitude (around (16, 8) for the NORTH
308
+ # spawn, (16, 36) for the SOUTH spawn). Win by having a
309
+ # refinery at the safe region of your latitude AND the
310
+ # Construction Yard still alive AND before tick 4500.
311
  description: >
312
+ Same task as medium, but your base sits in the north or south
313
+ corner depending on seed. End up with a refinery on your own
314
+ latitude's safe shoulder and keep your construction yard, within
315
+ about 50 turns.
 
 
 
 
 
 
 
 
 
316
  starting_cash: 700
317
  overrides:
318
  actors:
openra_bench/scenarios/packs/build-sequence-tech-cheapest.yaml CHANGED
@@ -125,17 +125,25 @@ levels:
125
  # the `then:` chain never completes ⇒ after_ticks LOSS. Stall never
126
  # builds anything ⇒ LOSS on the same clause.
127
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  description: >
129
- Build a war factory (weap) by spending the LEAST cash, on the
130
- ONLY affordable prerequisite chain: powr proc weap. Your
131
- starting cash ($3750) is a fixed budget there is no ore and no
132
- income, so it is all the money you will ever have. The minimal
133
- chain costs exactly $3700 (powr $300 + proc $1400 + weap $2000).
134
- Any extra structure bought before weap (a barracks/tent $500, a
135
- pillbox $600, an infantry unit) exhausts the budget and weap can
136
- never be funded — you LOSE on the clock. The `then:` chain
137
- enforces the exact order; placing weap before proc cannot satisfy
138
- it (and the engine refuses too: weap's prerequisite is proc).
139
  starting_cash: 3750
140
  overrides:
141
  actors:
@@ -169,15 +177,21 @@ levels:
169
  # chain still fits; ANY wasteful spend overruns even harder. Same
170
  # generous clock T = 3200 — money, not time, remains the teeth.
171
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
172
  description: >
173
- Build a war factory (weap) on the cost-minimal prerequisite
174
- chain: powr proc weap. Tighter budget your starting cash
175
- ($3720) barely covers the minimal path ($3700: powr $300 +
176
- proc $1400 + weap $2000). There is no ore and no income. Any
177
- extra structure (tent / pbox / an infantry unit) bought before
178
- weap exhausts the budget and weap can never be funded. The
179
- `then:` chain enforces the exact order; weap before proc cannot
180
- satisfy it.
181
  starting_cash: 3720
182
  overrides:
183
  actors:
@@ -208,15 +222,22 @@ levels:
208
  # do NOT honour spawn_point (CLAUDE.md), so the lone enemy `fact`
209
  # landmark always places.
210
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
211
  description: >
212
- Build a war factory (weap) on the cost-minimal prerequisite
213
- chain: powr proc weap, from a seed-chosen base (NORTH or
214
- SOUTH). Tight budget starting cash ($3720) barely covers the
215
- minimal path ($3700). There is no ore and no income. Any extra
216
- structure (tent / pbox / an infantry unit) bought before weap
217
- exhausts the budget and weap can never be funded. Placement
218
- that memorises one spawn's geometry cannot generalise; compute
219
- placement relative to your actual fact each run.
220
  starting_cash: 3720
221
  overrides:
222
  actors:
 
125
  # the `then:` chain never completes ⇒ after_ticks LOSS. Stall never
126
  # builds anything ⇒ LOSS on the same clause.
127
  easy:
128
+ # Original (pre-verbosity-sweep) description preserved for
129
+ # contributors. The trimmed version below removes scripted-policy
130
+ # spoilers and cell-coord dumps; load-bearing intent kept.
131
+ #
132
+ # Build a war factory (weap) by spending the LEAST cash, on the
133
+ # ONLY affordable prerequisite chain: powr → proc → weap. Your
134
+ # starting cash ($3750) is a fixed budget — there is no ore and no
135
+ # income, so it is all the money you will ever have. The minimal
136
+ # chain costs exactly $3700 (powr $300 + proc $1400 + weap $2000).
137
+ # Any extra structure bought before weap (a barracks/tent $500, a
138
+ # pillbox $600, an infantry unit) exhausts the budget and weap can
139
+ # never be funded — you LOSE on the clock. The `then:` chain
140
+ # enforces the exact order; placing weap before proc cannot satisfy
141
+ # it (and the engine refuses too: weap's prerequisite is proc).
142
  description: >
143
+ Cash $3750 is a fixed budget no ore, no income. End up owning a
144
+ Power Plant, then an Ore Refinery, then a War Factory, in that
145
+ order, within about 36 turns. Any other building drains the
146
+ budget below what the war factory needs.
 
 
 
 
 
 
147
  starting_cash: 3750
148
  overrides:
149
  actors:
 
177
  # chain still fits; ANY wasteful spend overruns even harder. Same
178
  # generous clock T = 3200 — money, not time, remains the teeth.
179
  medium:
180
+ # Original (pre-verbosity-sweep) description preserved for
181
+ # contributors. The trimmed version below removes scripted-policy
182
+ # spoilers and cell-coord dumps; load-bearing intent kept.
183
+ #
184
+ # Build a war factory (weap) on the cost-minimal prerequisite
185
+ # chain: powr → proc → weap. Tighter budget — your starting cash
186
+ # ($3720) barely covers the minimal path ($3700: powr $300 +
187
+ # proc $1400 + weap $2000). There is no ore and no income. Any
188
+ # extra structure (tent / pbox / an infantry unit) bought before
189
+ # weap exhausts the budget and weap can never be funded. The
190
+ # `then:` chain enforces the exact order; weap before proc cannot
191
+ # satisfy it.
192
  description: >
193
+ Same task with a tighter $3720 budget. Build Power Plant, then
194
+ Refinery, then War Factory, in order, within about 36 turns.
 
 
 
 
 
 
195
  starting_cash: 3720
196
  overrides:
197
  actors:
 
222
  # do NOT honour spawn_point (CLAUDE.md), so the lone enemy `fact`
223
  # landmark always places.
224
  hard:
225
+ # Original (pre-verbosity-sweep) description preserved for
226
+ # contributors. The trimmed version below removes scripted-policy
227
+ # spoilers and cell-coord dumps; load-bearing intent kept.
228
+ #
229
+ # Build a war factory (weap) on the cost-minimal prerequisite
230
+ # chain: powr → proc → weap, from a seed-chosen base (NORTH or
231
+ # SOUTH). Tight budget — starting cash ($3720) barely covers the
232
+ # minimal path ($3700). There is no ore and no income. Any extra
233
+ # structure (tent / pbox / an infantry unit) bought before weap
234
+ # exhausts the budget and weap can never be funded. Placement
235
+ # that memorises one spawn's geometry cannot generalise; compute
236
+ # placement relative to your actual fact each run.
237
  description: >
238
+ Same task as medium, but your base sits in the north or south
239
+ band depending on seed. Build the powr→proc→weap chain in order
240
+ within about 36 turns on $3720.
 
 
 
 
 
241
  starting_cash: 3720
242
  overrides:
243
  actors:
openra_bench/scenarios/packs/build-sequence-tech-fastest.yaml CHANGED
@@ -98,15 +98,22 @@ levels:
98
  # turns) finishes at ~tick 3063, beyond T ⇒ LOSS. Stall finishes
99
  # never ⇒ LOSS on the after_ticks fail clause.
100
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
101
  description: >
102
- Build a war factory (weap) as fast as possible by following the
103
- ONLY cost-optimal prerequisite chain: powr proc weap. Any
104
- detour (a barracks/tent, a redundant power plant, an early
105
- infantry training queue) wastes the budget and you LOSE on the
106
- clock. The `then:` chain enforces the exact order — placing
107
- weap before proc cannot satisfy it (and the engine refuses too:
108
- weap's prerequisite is proc). Optimal play finishes by tick
109
- ~2613; the deadline is 3000.
110
  starting_cash: 5000
111
  overrides:
112
  actors:
@@ -146,12 +153,18 @@ levels:
146
  # No additional pieces — the SAME cost-optimal chain, executed
147
  # with less slack.
148
  medium:
 
 
 
 
 
 
 
 
 
149
  description: >
150
- Build a war factory (weap) on the cost-optimal prerequisite
151
- chain: powr proc weap. Tighter deadline (2800 ticks) — any
152
- detour (tent / second powr / infantry queue) makes you miss.
153
- The `then:` chain enforces the exact order; weap before proc
154
- cannot satisfy it. Optimal play finishes by tick ~2613.
155
  starting_cash: 5000
156
  overrides:
157
  actors:
@@ -184,13 +197,20 @@ levels:
184
  # actors do NOT honour spawn_point (CLAUDE.md), so the lone
185
  # enemy `fact` always places.
186
  hard:
 
 
 
 
 
 
 
 
 
 
187
  description: >
188
- Build a war factory (weap) on the cost-optimal prerequisite
189
- chain: powr proc weap, from a seed-chosen base (NORTH or
190
- SOUTH). Tight 2800-tick deadline — detours (tent / extra
191
- powr / infantry queue) lose on the clock. Placement that
192
- memorises one spawn's geometry cannot generalise; compute
193
- placement relative to your actual fact each run.
194
  starting_cash: 5000
195
  overrides:
196
  actors:
 
98
  # turns) finishes at ~tick 3063, beyond T ⇒ LOSS. Stall finishes
99
  # never ⇒ LOSS on the after_ticks fail clause.
100
  easy:
101
+ # Original (pre-verbosity-sweep) description preserved for
102
+ # contributors. The trimmed version below removes scripted-policy
103
+ # spoilers and cell-coord dumps; load-bearing intent kept.
104
+ #
105
+ # Build a war factory (weap) as fast as possible by following the
106
+ # ONLY cost-optimal prerequisite chain: powr → proc → weap. Any
107
+ # detour (a barracks/tent, a redundant power plant, an early
108
+ # infantry training queue) wastes the budget and you LOSE on the
109
+ # clock. The `then:` chain enforces the exact order — placing
110
+ # weap before proc cannot satisfy it (and the engine refuses too:
111
+ # weap's prerequisite is proc). Optimal play finishes by tick
112
+ # ~2613; the deadline is 3000.
113
  description: >
114
+ $5000 cash plus ore. End up owning a Power Plant, then an Ore
115
+ Refinery, then a War Factory, in that order, within about 34
116
+ turns. Detours through other buildings overrun the clock.
 
 
 
 
 
117
  starting_cash: 5000
118
  overrides:
119
  actors:
 
153
  # No additional pieces — the SAME cost-optimal chain, executed
154
  # with less slack.
155
  medium:
156
+ # Original (pre-verbosity-sweep) description preserved for
157
+ # contributors. The trimmed version below removes scripted-policy
158
+ # spoilers and cell-coord dumps; load-bearing intent kept.
159
+ #
160
+ # Build a war factory (weap) on the cost-optimal prerequisite
161
+ # chain: powr → proc → weap. Tighter deadline (2800 ticks) — any
162
+ # detour (tent / second powr / infantry queue) makes you miss.
163
+ # The `then:` chain enforces the exact order; weap before proc
164
+ # cannot satisfy it. Optimal play finishes by tick ~2613.
165
  description: >
166
+ Same task with a tighter clock (about 31 turns). Build Power
167
+ Plant, then Refinery, then War Factory, in order.
 
 
 
168
  starting_cash: 5000
169
  overrides:
170
  actors:
 
197
  # actors do NOT honour spawn_point (CLAUDE.md), so the lone
198
  # enemy `fact` always places.
199
  hard:
200
+ # Original (pre-verbosity-sweep) description preserved for
201
+ # contributors. The trimmed version below removes scripted-policy
202
+ # spoilers and cell-coord dumps; load-bearing intent kept.
203
+ #
204
+ # Build a war factory (weap) on the cost-optimal prerequisite
205
+ # chain: powr → proc → weap, from a seed-chosen base (NORTH or
206
+ # SOUTH). Tight 2800-tick deadline — detours (tent / extra
207
+ # powr / infantry queue) lose on the clock. Placement that
208
+ # memorises one spawn's geometry cannot generalise; compute
209
+ # placement relative to your actual fact each run.
210
  description: >
211
+ Same task as medium, but your base sits in the north or south
212
+ band depending on seed. Build powr→proc→weap in order within
213
+ about 31 turns.
 
 
 
214
  starting_cash: 5000
215
  overrides:
216
  actors:
openra_bench/scenarios/packs/build-sequence-tech-most-resilient.yaml CHANGED
@@ -172,24 +172,32 @@ levels:
172
  # tanks. Generous clock (within_ticks 5400, max_turns 60 → ceiling
173
  # 5403 ✓). The strike fires at tick 1500.
174
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  description: >
176
- You inherit a partial base a Construction Yard ('fact'), an
177
- Ore Refinery ('proc'), a Service Depot ('fix'), an Ore Truck
178
- ('harv') with an ore patch, and ONE Power Plant ('powr'). That
179
- power plant sits FORWARD at the east edge of your base and is
180
- EXPOSED: an enemy strike will RAZE it at tick 1500. It is your
181
- only power. If it is your only power when the strike lands,
182
- your grid goes negative, your war factory drops to half
183
- production speed, and your tank army cannot finish in time. To
184
- stay resilient: build a SECOND Power Plant ('build' "powr",
185
- cost 300) and place it next to your Construction Yard in the
186
- safe west base BEFORE tick 1500, build a War Factory ('build'
187
- "weap", cost 2000), then produce three medium tanks ('build'
188
- "2tnk", cost 850 each). WIN = you brought power then a war
189
- factory online, you still own a Power Plant, you have 3 medium
190
- tanks, and you still own your Construction Yard, before tick
191
- 5400. Stalling, or relying on the single exposed power plant
192
- with no redundant backup, misses the bar.
193
  starting_cash: 6000
194
  overrides:
195
  actors:
@@ -252,23 +260,30 @@ levels:
252
  # before committing the redundant powr now risks the deadline. The
253
  # single-point-of-failure failure modes lose exactly as on easy.
254
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  description: >
256
- Same inherited base as easy a Construction Yard, an Ore
257
- Refinery, a Service Depot, an Ore Truck with an ore patch, and
258
- ONE EXPOSED Power Plant forward at the east edge that an enemy
259
- strike will RAZE at tick 1500. Build a SECOND Power Plant
260
- ('build' "powr", 300) in the safe west base next to your
261
- Construction Yard BEFORE tick 1500, build a War Factory
262
- ('build' "weap", 2000), then produce three medium tanks
263
- ('build' "2tnk", 850 each).
264
- The deadline is tighter — tick 4500 — so commit the redundant
265
- power plant early; do not wait for the exposed one to fall. If
266
- the strike leaves you with no power, the war factory halves its
267
- output and the army misses the clock. WIN = you brought power
268
- then a war factory online, you still own a Power Plant, you
269
- have 3 medium tanks, and you still own your Construction Yard,
270
- before tick 4500. Stalling, or relying on the single exposed
271
- power plant, misses the bar.
272
  starting_cash: 6000
273
  overrides:
274
  actors:
@@ -316,21 +331,29 @@ levels:
316
  # generalise — the agent must read the actual Construction Yard
317
  # latitude and place the redundant power plant beside it.
318
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  description: >
320
- Same N+1 build-order task as medium (one EXPOSED Power Plant
321
- forward at the east edge that an enemy strike razes at tick
322
- 1500, $6000, tick 4500 deadline) but your base may begin in
323
- the NORTH band (y≈12) OR the SOUTH band (y≈26) of the map
324
- depending on the seed. Read the Construction Yard's actual
325
- position from the observation and place the redundant Power
326
- Plant beside it in the safe west base BEFORE tick 1500; build a
327
- War Factory; then produce three medium tanks. A memorised
328
- placement cell will mis-place out of build radius on one of the
329
- two spawns. WIN = you brought power then a war factory online,
330
- you still own a Power Plant, you have 3 medium tanks, and you
331
- still own your Construction Yard, before tick 4500. The same
332
- single-point-of-failure plays — stalling, or relying on the
333
- lone exposed power plant — lose as on medium.
334
  starting_cash: 6000
335
  overrides:
336
  actors:
 
172
  # tanks. Generous clock (within_ticks 5400, max_turns 60 → ceiling
173
  # 5403 ✓). The strike fires at tick 1500.
174
  easy:
175
+ # Original (pre-verbosity-sweep) description preserved for
176
+ # contributors. The trimmed version below removes scripted-policy
177
+ # spoilers and cell-coord dumps; load-bearing intent kept.
178
+ #
179
+ # You inherit a partial base — a Construction Yard ('fact'), an
180
+ # Ore Refinery ('proc'), a Service Depot ('fix'), an Ore Truck
181
+ # ('harv') with an ore patch, and ONE Power Plant ('powr'). That
182
+ # power plant sits FORWARD at the east edge of your base and is
183
+ # EXPOSED: an enemy strike will RAZE it at tick 1500. It is your
184
+ # only power. If it is your only power when the strike lands,
185
+ # your grid goes negative, your war factory drops to half
186
+ # production speed, and your tank army cannot finish in time. To
187
+ # stay resilient: build a SECOND Power Plant ('build' "powr",
188
+ # cost 300) and place it next to your Construction Yard in the
189
+ # safe west base BEFORE tick 1500, build a War Factory ('build'
190
+ # "weap", cost 2000), then produce three medium tanks ('build'
191
+ # "2tnk", cost 850 each). WIN = you brought power then a war
192
+ # factory online, you still own a Power Plant, you have 3 medium
193
+ # tanks, and you still own your Construction Yard, before tick
194
+ # 5400. Stalling, or relying on the single exposed power plant
195
+ # with no redundant backup, misses the bar.
196
  description: >
197
+ You inherit a base with $6000 and one forward Power Plant that an
198
+ enemy strike razes at turn 17. End up owning a Power Plant, a
199
+ War Factory, and three medium tanks (2tnk) within about 60 turns,
200
+ keeping your Construction Yard.
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  starting_cash: 6000
202
  overrides:
203
  actors:
 
260
  # before committing the redundant powr now risks the deadline. The
261
  # single-point-of-failure failure modes lose exactly as on easy.
262
  medium:
263
+ # Original (pre-verbosity-sweep) description preserved for
264
+ # contributors. The trimmed version below removes scripted-policy
265
+ # spoilers and cell-coord dumps; load-bearing intent kept.
266
+ #
267
+ # Same inherited base as easy — a Construction Yard, an Ore
268
+ # Refinery, a Service Depot, an Ore Truck with an ore patch, and
269
+ # ONE EXPOSED Power Plant forward at the east edge that an enemy
270
+ # strike will RAZE at tick 1500. Build a SECOND Power Plant
271
+ # ('build' "powr", 300) in the safe west base next to your
272
+ # Construction Yard BEFORE tick 1500, build a War Factory
273
+ # ('build' "weap", 2000), then produce three medium tanks
274
+ # ('build' "2tnk", 850 each).
275
+ # The deadline is tighter — tick 4500 — so commit the redundant
276
+ # power plant early; do not wait for the exposed one to fall. If
277
+ # the strike leaves you with no power, the war factory halves its
278
+ # output and the army misses the clock. WIN = you brought power
279
+ # then a war factory online, you still own a Power Plant, you
280
+ # have 3 medium tanks, and you still own your Construction Yard,
281
+ # before tick 4500. Stalling, or relying on the single exposed
282
+ # power plant, misses the bar.
283
  description: >
284
+ Same base and strike as easy, but a tighter clock about 50
285
+ turns. End up owning a Power Plant, a War Factory, and three
286
+ medium tanks, keeping your Construction Yard.
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  starting_cash: 6000
288
  overrides:
289
  actors:
 
331
  # generalise — the agent must read the actual Construction Yard
332
  # latitude and place the redundant power plant beside it.
333
  hard:
334
+ # Original (pre-verbosity-sweep) description preserved for
335
+ # contributors. The trimmed version below removes scripted-policy
336
+ # spoilers and cell-coord dumps; load-bearing intent kept.
337
+ #
338
+ # Same N+1 build-order task as medium (one EXPOSED Power Plant
339
+ # forward at the east edge that an enemy strike razes at tick
340
+ # 1500, $6000, tick 4500 deadline) but your base may begin in
341
+ # the NORTH band (y≈12) OR the SOUTH band (y≈26) of the map
342
+ # depending on the seed. Read the Construction Yard's actual
343
+ # position from the observation and place the redundant Power
344
+ # Plant beside it in the safe west base BEFORE tick 1500; build a
345
+ # War Factory; then produce three medium tanks. A memorised
346
+ # placement cell will mis-place out of build radius on one of the
347
+ # two spawns. WIN = you brought power then a war factory online,
348
+ # you still own a Power Plant, you have 3 medium tanks, and you
349
+ # still own your Construction Yard, before tick 4500. The same
350
+ # single-point-of-failure plays — stalling, or relying on the
351
+ # lone exposed power plant — lose as on medium.
352
  description: >
353
+ Same task as medium, but your base sits in the north or south
354
+ band depending on seed. End up owning a Power Plant, a War
355
+ Factory, and three medium tanks within about 50 turns, keeping
356
+ your Construction Yard.
 
 
 
 
 
 
 
 
 
 
357
  starting_cash: 6000
358
  overrides:
359
  actors:
openra_bench/scenarios/packs/build-tech-skip-decision.yaml CHANGED
@@ -155,17 +155,24 @@ levels:
155
  # as a real reachable LOSS (the old max_turns 25 could hit the turn
156
  # cap at tick ~1578 before the deadline → degenerate draw).
157
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  description: >
159
- Your base already has a Construction Yard (fact) and an Allied
160
- barracks (tent) so rifle infantry (e1, $100 each) are
161
- buildable from turn 1 with NO prior tech step. A light enemy
162
- garrison of 5 rifle infantry is advancing from the east. The
163
- objective needs only basic units: train an e1 swarm and rally it
164
- at your base front to meet the garrison. Do NOT climb the tech
165
- chain to a war factory and tanks — that is a whole tech tier the
166
- objective never asked for and it overruns the clock. Win when
167
- ≥4 enemy units are killed AND your fact still stands, before
168
- tick 1600. Stalling or teching to tanks both LOSE on the clock.
169
  starting_cash: 6000
170
  overrides:
171
  actors:
@@ -199,15 +206,22 @@ levels:
199
  # max_turns 30 keeps the loop alive to that terminal frame so the
200
  # after_ticks 1401 fail bites as a real reachable LOSS.
201
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
202
  description: >
203
- Same base (fact + tent pre-placed, e1 buildable from turn 1) but
204
- a larger enemy garrison (7 rifle infantry) and a tighter
205
- deadline. The objective still needs only basic units: train an
206
- e1 swarm and rally it at your base front. Climbing the tech
207
- chain to a war factory and tanks is an unnecessary tier that
208
- overruns the clock. Win when ≥6 enemy units are killed AND your
209
- fact still stands, before tick 1400. Stalling or teching to
210
- tanks both LOSE on the clock.
211
  starting_cash: 6000
212
  overrides:
213
  actors:
@@ -243,15 +257,22 @@ levels:
243
  # real reachable LOSS (the old max_turns 23 hit the turn cap at
244
  # tick ~1578 before the deadline on seeds 1/3 → degenerate draw).
245
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
246
  description: >
247
- Same step-pruning call as medium (clear a 7-rifle garrison with
248
- a basic e1 swarm before tick 1400; skip the unnecessary war-
249
- factory tech tier) but your base may begin in the NORTH band
250
- (y≈14) or the SOUTH band (y≈26) depending on the seed. Read
251
- your barracks' actual position from the observation, train the
252
- e1 swarm and rally it at your base front. Win when ≥6 enemy
253
- units are killed AND your fact still stands, before tick 1400.
254
- Stalling or teching to tanks both LOSE on the clock.
255
  starting_cash: 6000
256
  overrides:
257
  actors:
 
155
  # as a real reachable LOSS (the old max_turns 25 could hit the turn
156
  # cap at tick ~1578 before the deadline → degenerate draw).
157
  easy:
158
+ # Original (pre-verbosity-sweep) description preserved for
159
+ # contributors. The trimmed version below removes scripted-policy
160
+ # spoilers and cell-coord dumps; load-bearing intent kept.
161
+ #
162
+ # Your base already has a Construction Yard (fact) and an Allied
163
+ # barracks (tent) — so rifle infantry (e1, $100 each) are
164
+ # buildable from turn 1 with NO prior tech step. A light enemy
165
+ # garrison of 5 rifle infantry is advancing from the east. The
166
+ # objective needs only basic units: train an e1 swarm and rally it
167
+ # at your base front to meet the garrison. Do NOT climb the tech
168
+ # chain to a war factory and tanks — that is a whole tech tier the
169
+ # objective never asked for and it overruns the clock. Win when
170
+ # ≥4 enemy units are killed AND your fact still stands, before
171
+ # tick 1600. Stalling or teching to tanks both LOSE on the clock.
172
  description: >
173
+ You start with a Construction Yard, a Barracks, and $6000. A
174
+ light rifle garrison advances from the east. Kill four enemies
175
+ within about 17 turns, keeping your construction yard.
 
 
 
 
 
 
 
176
  starting_cash: 6000
177
  overrides:
178
  actors:
 
206
  # max_turns 30 keeps the loop alive to that terminal frame so the
207
  # after_ticks 1401 fail bites as a real reachable LOSS.
208
  medium:
209
+ # Original (pre-verbosity-sweep) description preserved for
210
+ # contributors. The trimmed version below removes scripted-policy
211
+ # spoilers and cell-coord dumps; load-bearing intent kept.
212
+ #
213
+ # Same base (fact + tent pre-placed, e1 buildable from turn 1) but
214
+ # a larger enemy garrison (7 rifle infantry) and a tighter
215
+ # deadline. The objective still needs only basic units: train an
216
+ # e1 swarm and rally it at your base front. Climbing the tech
217
+ # chain to a war factory and tanks is an unnecessary tier that
218
+ # overruns the clock. Win when ≥6 enemy units are killed AND your
219
+ # fact still stands, before tick 1400. Stalling or teching to
220
+ # tanks both LOSE on the clock.
221
  description: >
222
+ Same base with $6000, but seven rifle attackers and a tighter
223
+ clock. Kill six enemies within about 15 turns, keeping your
224
+ construction yard.
 
 
 
 
 
225
  starting_cash: 6000
226
  overrides:
227
  actors:
 
257
  # real reachable LOSS (the old max_turns 23 hit the turn cap at
258
  # tick ~1578 before the deadline on seeds 1/3 → degenerate draw).
259
  hard:
260
+ # Original (pre-verbosity-sweep) description preserved for
261
+ # contributors. The trimmed version below removes scripted-policy
262
+ # spoilers and cell-coord dumps; load-bearing intent kept.
263
+ #
264
+ # Same step-pruning call as medium (clear a 7-rifle garrison with
265
+ # a basic e1 swarm before tick 1400; skip the unnecessary war-
266
+ # factory tech tier) but your base may begin in the NORTH band
267
+ # (y≈14) or the SOUTH band (y≈26) depending on the seed. Read
268
+ # your barracks' actual position from the observation, train the
269
+ # e1 swarm and rally it at your base front. Win when ≥6 enemy
270
+ # units are killed AND your fact still stands, before tick 1400.
271
+ # Stalling or teching to tanks both LOSE on the clock.
272
  description: >
273
+ Same task as medium, but your base sits in the north or south
274
+ band depending on seed. Kill six enemies within about 15 turns,
275
+ keeping your construction yard.
 
 
 
 
 
276
  starting_cash: 6000
277
  overrides:
278
  actors:
openra_bench/scenarios/packs/building-and-planning.yaml CHANGED
@@ -63,12 +63,19 @@ levels:
63
  # (no tent); idle/stall loses on the clock. Generous, tick-aligned
64
  # clock. max_turns 30 → reachable tick 93+90·29 = 2703.
65
  easy:
 
 
 
 
 
 
 
 
 
66
  description: >
67
- No power is pre-placed. Build a power plant first, THEN the
68
- power-dependent barracks (tent) a power-less barracks never
69
- completes. Win = own a 'tent' AND ≥3 buildings total. Building
70
- the barracks first never completes (no power); spamming power
71
- plants never gives a barracks; idling loses on the clock.
72
  starting_cash: 6000
73
  win_condition:
74
  all_of:
@@ -88,13 +95,20 @@ levels:
88
  # loses (not in region); idle loses on the clock.
89
  # max_turns 40 → reachable tick 93+90·39 = 3603.
90
  medium:
 
 
 
 
 
 
 
 
 
 
91
  description: >
92
- Build a defensive line to the EAST: stand up power, then the
93
- barracks it enables, then place at least two pillboxes (pbox —
94
- they need the barracks, which needs power) INSIDE the designated
95
- eastern region near (40,20). Skipping the chain never yields a
96
- pillbox; placing the pillboxes near the base (wrong direction)
97
- does not count; idling loses on the clock.
98
  starting_cash: 5000
99
  win_condition:
100
  all_of:
@@ -114,14 +128,21 @@ levels:
114
  # creep that omits the tech chain never yields a pillbox; idle loses
115
  # on the clock. max_turns 70 → reachable tick 93+90·69 = 6303.
116
  hard:
 
 
 
 
 
 
 
 
 
 
 
117
  description: >
118
- Found the defensive line FAR to the east, near (60,20) — beyond
119
- the construction yard's reach. Creep the base east (each new
120
- power plant extends where you may build) until you can stand up
121
- the barracks and place at least two pillboxes (pbox → tent →
122
- power chain) inside the far region, before the tight deadline.
123
- Building near the spawn never reaches the region; a power creep
124
- that skips the barracks never yields a pillbox; idling loses.
125
  starting_cash: 6000
126
  win_condition:
127
  all_of:
 
63
  # (no tent); idle/stall loses on the clock. Generous, tick-aligned
64
  # clock. max_turns 30 → reachable tick 93+90·29 = 2703.
65
  easy:
66
+ # Original (pre-verbosity-sweep) description preserved for
67
+ # contributors. The trimmed version below removes scripted-policy
68
+ # spoilers and cell-coord dumps; load-bearing intent kept.
69
+ #
70
+ # No power is pre-placed. Build a power plant first, THEN the
71
+ # power-dependent barracks (tent) — a power-less barracks never
72
+ # completes. Win = own a 'tent' AND ≥3 buildings total. Building
73
+ # the barracks first never completes (no power); spamming power
74
+ # plants never gives a barracks; idling loses on the clock.
75
  description: >
76
+ No power is pre-placed. End up owning a Barracks (tent) and at
77
+ least three buildings total within about 27 turns. The barracks
78
+ requires power to complete.
 
 
79
  starting_cash: 6000
80
  win_condition:
81
  all_of:
 
95
  # loses (not in region); idle loses on the clock.
96
  # max_turns 40 → reachable tick 93+90·39 = 3603.
97
  medium:
98
+ # Original (pre-verbosity-sweep) description preserved for
99
+ # contributors. The trimmed version below removes scripted-policy
100
+ # spoilers and cell-coord dumps; load-bearing intent kept.
101
+ #
102
+ # Build a defensive line to the EAST: stand up power, then the
103
+ # barracks it enables, then place at least two pillboxes (pbox —
104
+ # they need the barracks, which needs power) INSIDE the designated
105
+ # eastern region near (40,20). Skipping the chain never yields a
106
+ # pillbox; placing the pillboxes near the base (wrong direction)
107
+ # does not count; idling loses on the clock.
108
  description: >
109
+ Place at least two pillboxes within nine cells of (40,20) the
110
+ eastern region within about 38 turns. Pillboxes need a
111
+ barracks, which needs power.
 
 
 
112
  starting_cash: 5000
113
  win_condition:
114
  all_of:
 
128
  # creep that omits the tech chain never yields a pillbox; idle loses
129
  # on the clock. max_turns 70 → reachable tick 93+90·69 = 6303.
130
  hard:
131
+ # Original (pre-verbosity-sweep) description preserved for
132
+ # contributors. The trimmed version below removes scripted-policy
133
+ # spoilers and cell-coord dumps; load-bearing intent kept.
134
+ #
135
+ # Found the defensive line FAR to the east, near (60,20) — beyond
136
+ # the construction yard's reach. Creep the base east (each new
137
+ # power plant extends where you may build) until you can stand up
138
+ # the barracks and place at least two pillboxes (pbox → tent →
139
+ # power chain) inside the far region, before the tight deadline.
140
+ # Building near the spawn never reaches the region; a power creep
141
+ # that skips the barracks never yields a pillbox; idling loses.
142
  description: >
143
+ Place at least two pillboxes within ten cells of (60,20) — far
144
+ beyond your construction yard's reach within about 64 turns.
145
+ Each new building extends where you may build next.
 
 
 
 
146
  starting_cash: 6000
147
  win_condition:
148
  all_of:
openra_bench/scenarios/packs/combat-attack-from-behind-fog.yaml CHANGED
@@ -164,22 +164,30 @@ levels:
164
  # engaging the line). Stall LOSES (line is stance:2, never
165
  # advances; fact never razed). Fog flank cleanly WINS.
166
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  description: >
168
- Four medium tanks (2tnk) stage at the west edge (x=6, y=18..21).
169
- A LIGHT vertical line of THREE anti-tank rocket soldiers (e3,
170
- Dragon launcher, range ~5) stands at x=50, y=19..21, facing
171
- west (stance:2 — auto-fire in range, no chase). Behind the
172
- line, an UNDEFENDED enemy construction yard (fact) sits at
173
- (100,20). Charging head-on (east along y=20) puts the lead
174
- tank in range of all three e3 and the column burns turns
175
- reducing the line — the fact at x=100 is still 50 cells away
176
- with the deadline closing. The winning play is the FOG FLANK
177
- — route the strike force to the far north (y=2) or far south
178
- (y=38), well outside the line's range, drive east past x=80,
179
- then turn inward to descend on the fact at (100,20). Win
180
- when the fact at (100,20) is destroyed AND ≥2 of your tanks
181
- survive, before tick 5400. Stalling LOSES (fact never
182
- destroyed); frontal-charge LOSES on the clock.
183
  overrides:
184
  actors:
185
  # STRIKE FORCE — 4 medium tanks stacked at the west edge,
@@ -222,23 +230,31 @@ levels:
222
  # wins comfortably: the line never fires on the flanker (Manhattan
223
  # ≥13 to the nearest defender from y=2 or y=38).
224
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  description: >
226
- Four medium tanks (2tnk) stage at the west edge (x=6, y=18..21).
227
- A HEAVY defensive line stands at x=50, y=15..25, facing west
228
- (stance:2 auto-fire in range, no chase): TWO pillboxes
229
- (pbox, range ~6) bookend FIVE anti-tank rocket soldiers (e3,
230
- Dragon range ~5). Behind the line, an UNDEFENDED enemy
231
- construction yard (fact) sits at (100,20). Charging head-on
232
- (east along y=18..22) puts the lead tank inside the
233
- overlapping kill envelopes of 4+ defenders simultaneously;
234
- concentrated fire destroys the column before it clears.
235
- The winning play is the FOG FLANK — route the strike force
236
- WELL off-axis to the far north (y=2) or far south (y=38),
237
- drive east past the line's longitude (x=50), then turn
238
- inward to descend on the fact at (100,20) from behind. Win
239
- when the fact at (100,20) is destroyed AND ≥2 of your tanks
240
- survive, before tick 4500. Stall, brute attack_move east,
241
- and any frontal charge all LOSE.
242
  overrides:
243
  actors:
244
  - {type: 2tnk, owner: agent, position: [6, 18], stance: 1}
@@ -293,24 +309,33 @@ levels:
293
  # fog-flank decision (NORTH spawn → fog via y=2; SOUTH spawn
294
  # → fog via y=38).
295
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  description: >
297
- Four medium tanks (2tnk) stage at ONE of two west-edge
298
- corridors (NORTH y=14..17 OR SOUTH y=23..26, chosen by seed,
299
- anti-memorisation). A dense vertical wall stands at x=50,
300
- y=15..25, facing west (stance:2 auto-fire in range, no
301
- chase): THREE pillboxes (pbox) interleaved with SEVEN anti-
302
- tank rocket soldiers (e3, Dragon launcher, range ~5). Behind
303
- the wall, an UNDEFENDED enemy construction yard (fact) sits
304
- at (100,20). Any frontal charge along y=14..26 enters the
305
- overlapping kill envelope of the entire 10-defender wall;
306
- concentrated fire destroys the column before it clears.
307
- The winning play is the FOG FLANK — route the strike force
308
- to the far north (y=2 from the NORTH spawn) or far south
309
- (y=38 from the SOUTH spawn), drive east past x=80, then
310
- turn inward to descend on the fact at (100,20). Win when
311
- the fact at (100,20) is destroyed AND ≥2 of your tanks
312
- survive, before tick 4500. Stall, brute attack_move east,
313
- and any frontal head-on charge all LOSE.
314
  overrides:
315
  actors:
316
  # spawn_point 0 — NORTH staging corridor (y=14..17). Fog
 
164
  # engaging the line). Stall LOSES (line is stance:2, never
165
  # advances; fact never razed). Fog flank cleanly WINS.
166
  easy:
167
+ # Original (pre-verbosity-sweep) description preserved for
168
+ # contributors. The trimmed version below removes scripted-policy
169
+ # spoilers and cell-coord dumps; load-bearing intent kept.
170
+ #
171
+ # Four medium tanks (2tnk) stage at the west edge (x=6, y=18..21).
172
+ # A LIGHT vertical line of THREE anti-tank rocket soldiers (e3,
173
+ # Dragon launcher, range ~5) stands at x=50, y=19..21, facing
174
+ # west (stance:2 — auto-fire in range, no chase). Behind the
175
+ # line, an UNDEFENDED enemy construction yard (fact) sits at
176
+ # (100,20). Charging head-on (east along y=20) puts the lead
177
+ # tank in range of all three e3 and the column burns turns
178
+ # reducing the line — the fact at x=100 is still 50 cells away
179
+ # with the deadline closing. The winning play is the FOG FLANK
180
+ # — route the strike force to the far north (y=2) or far south
181
+ # (y=38), well outside the line's range, drive east past x=80,
182
+ # then turn inward to descend on the fact at (100,20). Win
183
+ # when the fact at (100,20) is destroyed AND ≥2 of your tanks
184
+ # survive, before tick 5400. Stalling LOSES (fact never
185
+ # destroyed); frontal-charge LOSES on the clock.
186
  description: >
187
+ Four tanks stage at the west edge. A light rocket-infantry line
188
+ at x=50 blocks the centre; the enemy construction yard at
189
+ (100,20) is undefended. Destroy the yard with at least two tanks
190
+ surviving, within about 65 turns.
 
 
 
 
 
 
 
 
 
 
 
191
  overrides:
192
  actors:
193
  # STRIKE FORCE — 4 medium tanks stacked at the west edge,
 
230
  # wins comfortably: the line never fires on the flanker (Manhattan
231
  # ≥13 to the nearest defender from y=2 or y=38).
232
  medium:
233
+ # Original (pre-verbosity-sweep) description preserved for
234
+ # contributors. The trimmed version below removes scripted-policy
235
+ # spoilers and cell-coord dumps; load-bearing intent kept.
236
+ #
237
+ # Four medium tanks (2tnk) stage at the west edge (x=6, y=18..21).
238
+ # A HEAVY defensive line stands at x=50, y=15..25, facing west
239
+ # (stance:2 — auto-fire in range, no chase): TWO pillboxes
240
+ # (pbox, range ~6) bookend FIVE anti-tank rocket soldiers (e3,
241
+ # Dragon range ~5). Behind the line, an UNDEFENDED enemy
242
+ # construction yard (fact) sits at (100,20). Charging head-on
243
+ # (east along y=18..22) puts the lead tank inside the
244
+ # overlapping kill envelopes of 4+ defenders simultaneously;
245
+ # concentrated fire destroys the column before it clears.
246
+ # The winning play is the FOG FLANK — route the strike force
247
+ # WELL off-axis to the far north (y=2) or far south (y=38),
248
+ # drive east past the line's longitude (x=50), then turn
249
+ # inward to descend on the fact at (100,20) from behind. Win
250
+ # when the fact at (100,20) is destroyed AND ≥2 of your tanks
251
+ # survive, before tick 4500. Stall, brute attack_move east,
252
+ # and any frontal charge all LOSE.
253
  description: >
254
+ Heavier line at x=50: two pillboxes plus five rocket soldiers
255
+ spanning y=15 to 25. The enemy construction yard at (100,20)
256
+ remains undefended. Destroy the yard with at least two tanks
257
+ surviving, within about 54 turns.
 
 
 
 
 
 
 
 
 
 
 
 
258
  overrides:
259
  actors:
260
  - {type: 2tnk, owner: agent, position: [6, 18], stance: 1}
 
309
  # fog-flank decision (NORTH spawn → fog via y=2; SOUTH spawn
310
  # → fog via y=38).
311
  hard:
312
+ # Original (pre-verbosity-sweep) description preserved for
313
+ # contributors. The trimmed version below removes scripted-policy
314
+ # spoilers and cell-coord dumps; load-bearing intent kept.
315
+ #
316
+ # Four medium tanks (2tnk) stage at ONE of two west-edge
317
+ # corridors (NORTH y=14..17 OR SOUTH y=23..26, chosen by seed,
318
+ # anti-memorisation). A dense vertical wall stands at x=50,
319
+ # y=15..25, facing west (stance:2 — auto-fire in range, no
320
+ # chase): THREE pillboxes (pbox) interleaved with SEVEN anti-
321
+ # tank rocket soldiers (e3, Dragon launcher, range ~5). Behind
322
+ # the wall, an UNDEFENDED enemy construction yard (fact) sits
323
+ # at (100,20). Any frontal charge along y=14..26 enters the
324
+ # overlapping kill envelope of the entire 10-defender wall;
325
+ # concentrated fire destroys the column before it clears.
326
+ # The winning play is the FOG FLANK — route the strike force
327
+ # to the far north (y=2 from the NORTH spawn) or far south
328
+ # (y=38 from the SOUTH spawn), drive east past x=80, then
329
+ # turn inward to descend on the fact at (100,20). Win when
330
+ # the fact at (100,20) is destroyed AND ≥2 of your tanks
331
+ # survive, before tick 4500. Stall, brute attack_move east,
332
+ # and any frontal head-on charge all LOSE.
333
  description: >
334
+ Same task as medium with a denser wall (three pillboxes plus
335
+ seven rocket soldiers at x=50). Your tanks stage in the north
336
+ or south corridor depending on seed. Destroy the enemy yard at
337
+ (100,20) with at least two tanks surviving, within about 54
338
+ turns.
 
 
 
 
 
 
 
 
 
 
 
 
339
  overrides:
340
  actors:
341
  # spawn_point 0 — NORTH staging corridor (y=14..17). Fog
openra_bench/scenarios/packs/combat-bait-counter-attack.yaml CHANGED
@@ -111,19 +111,26 @@ levels:
111
  # them at their leash the strike tanks focus-fire the nearest guard
112
  # and then drive onto the now-undefended yard.
113
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  description: >
115
- Three leashed rocket-infantry guards stand on the west face of
116
- the enemy construction yard (`fact` at 80,20), directly
117
- between you and the objective. They hold post, auto-fire on
118
- units in range, lunge at the nearest foe within ≈16 cells, and
119
- snap back past a leash of ≈18. Destroy the enemy construction
120
- yard with at most 3 of your units lost before tick 5400.
121
- Driving the tanks straight east at the yard puts them in
122
- anti-tank fire from the whole guard line; stalling runs the
123
- clock. Commit the fast jeep on a divergent south-east vector
124
- so the guards lunge south after it; while the jeep kites them
125
- at their leash, run the strike tanks straight through the
126
- now-undefended west face to destroy the yard.
127
  overrides:
128
  actors:
129
  # Strike force: 4 medium tanks at west, central staging.
@@ -175,19 +182,26 @@ levels:
175
  # jeep + at most one tank): the strike must still hit the yard
176
  # essentially intact; burning a second tank fails.
177
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  description: >
179
- A denser cluster of 5 leashed rocket-infantry guards covers
180
- the enemy construction yard (`fact` at 80,20) on the WEST and
181
- SOUTH faces (the two natural approaches from your west base).
182
- A head-on push without first thinning the cover is decisively
183
- fatal. You command 5 medium tanks staged on the NORTH flank
184
- plus the bait jeep, but may spend at most 2 units — losing a
185
- second tank fails. Commit the fast jeep on a deep south flank
186
- vector to pull the south arc of guards off post after it; while
187
- that arc is displaced, swing the strike tanks around the
188
- now-vacated NORTH flank and onto the yard before tick 5400.
189
- Bait-only (no strike) loses on the clock; brute frontal trades
190
- the strike force.
191
  overrides:
192
  actors:
193
  # Strike force: 5 medium tanks staged on the NORTH flank
@@ -234,20 +248,27 @@ levels:
234
  # closer guard arc engages the column before the bait can pull it).
235
  # Same 5-guard L cover, 5-tank strike, and loss cap 2 as medium.
236
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  description: >
238
- The same dense 5-guard L-cover of the enemy construction yard
239
- (`fact` at 80,20) as the previous tier, but your force stages
240
- from a seed-chosen distance either the far west or well
241
- forward — so a memorised fixed opening cannot generalise; read
242
- your tanks' actual position from the observation. You command 5
243
- medium tanks staged on the NORTH flank plus the bait jeep, but
244
- may spend at most 2 units (the jeep + at most one tank) —
245
- losing a second tank fails. Commit the fast jeep on a deep
246
- south flank vector to pull the south arc of guards off post;
247
- while that arc is displaced, swing the strike tanks around the
248
- now-vacated NORTH flank and onto the yard before tick 5400.
249
- Brute frontal trades armour and fails; bait-only never razes
250
- the yard; stalling loses the clock.
251
  overrides:
252
  actors:
253
  # spawn_point 0 — FAR-WEST staging (x=6): the strike has the
 
111
  # them at their leash the strike tanks focus-fire the nearest guard
112
  # and then drive onto the now-undefended yard.
113
  easy:
114
+ # Original (pre-verbosity-sweep) description preserved for
115
+ # contributors. The trimmed version below removes scripted-policy
116
+ # spoilers and cell-coord dumps; load-bearing intent kept.
117
+ #
118
+ # Three leashed rocket-infantry guards stand on the west face of
119
+ # the enemy construction yard (`fact` at 80,20), directly
120
+ # between you and the objective. They hold post, auto-fire on
121
+ # units in range, lunge at the nearest foe within ≈16 cells, and
122
+ # snap back past a leash of ≈18. Destroy the enemy construction
123
+ # yard with at most 3 of your units lost before tick 5400.
124
+ # Driving the tanks straight east at the yard puts them in
125
+ # anti-tank fire from the whole guard line; stalling runs the
126
+ # clock. Commit the fast jeep on a divergent south-east vector
127
+ # so the guards lunge south after it; while the jeep kites them
128
+ # at their leash, run the strike tanks straight through the
129
+ # now-undefended west face to destroy the yard.
130
  description: >
131
+ Three leashed rocket-infantry guards wall the west face of the
132
+ enemy construction yard at (80,20). Destroy the yard, losing at
133
+ most three units, within about 62 turns.
 
 
 
 
 
 
 
 
 
134
  overrides:
135
  actors:
136
  # Strike force: 4 medium tanks at west, central staging.
 
182
  # jeep + at most one tank): the strike must still hit the yard
183
  # essentially intact; burning a second tank fails.
184
  medium:
185
+ # Original (pre-verbosity-sweep) description preserved for
186
+ # contributors. The trimmed version below removes scripted-policy
187
+ # spoilers and cell-coord dumps; load-bearing intent kept.
188
+ #
189
+ # A denser cluster of 5 leashed rocket-infantry guards covers
190
+ # the enemy construction yard (`fact` at 80,20) on the WEST and
191
+ # SOUTH faces (the two natural approaches from your west base).
192
+ # A head-on push without first thinning the cover is decisively
193
+ # fatal. You command 5 medium tanks staged on the NORTH flank
194
+ # plus the bait jeep, but may spend at most 2 units — losing a
195
+ # second tank fails. Commit the fast jeep on a deep south flank
196
+ # vector to pull the south arc of guards off post after it; while
197
+ # that arc is displaced, swing the strike tanks around the
198
+ # now-vacated NORTH flank and onto the yard before tick 5400.
199
+ # Bait-only (no strike) loses on the clock; brute frontal trades
200
+ # the strike force.
201
  description: >
202
+ Denser cover five leashed rocket-infantry guards on the west
203
+ and south faces of the enemy yard at (80,20). Destroy the yard,
204
+ losing at most two units, within about 62 turns.
 
 
 
 
 
 
 
 
 
205
  overrides:
206
  actors:
207
  # Strike force: 5 medium tanks staged on the NORTH flank
 
248
  # closer guard arc engages the column before the bait can pull it).
249
  # Same 5-guard L cover, 5-tank strike, and loss cap 2 as medium.
250
  hard:
251
+ # Original (pre-verbosity-sweep) description preserved for
252
+ # contributors. The trimmed version below removes scripted-policy
253
+ # spoilers and cell-coord dumps; load-bearing intent kept.
254
+ #
255
+ # The same dense 5-guard L-cover of the enemy construction yard
256
+ # (`fact` at 80,20) as the previous tier, but your force stages
257
+ # from a seed-chosen distance — either the far west or well
258
+ # forward — so a memorised fixed opening cannot generalise; read
259
+ # your tanks' actual position from the observation. You command 5
260
+ # medium tanks staged on the NORTH flank plus the bait jeep, but
261
+ # may spend at most 2 units (the jeep + at most one tank) —
262
+ # losing a second tank fails. Commit the fast jeep on a deep
263
+ # south flank vector to pull the south arc of guards off post;
264
+ # while that arc is displaced, swing the strike tanks around the
265
+ # now-vacated NORTH flank and onto the yard before tick 5400.
266
+ # Brute frontal trades armour and fails; bait-only never razes
267
+ # the yard; stalling loses the clock.
268
  description: >
269
+ Same task as medium, but your force stages from a seed-chosen
270
+ distance (far west or well forward). Destroy the enemy yard,
271
+ losing at most two units, within about 62 turns.
 
 
 
 
 
 
 
 
 
 
272
  overrides:
273
  actors:
274
  # spawn_point 0 — FAR-WEST staging (x=6): the strike has the
openra_bench/scenarios/packs/combat-divide-and-conquer.yaml CHANGED
@@ -142,18 +142,26 @@ levels:
142
  # inert-easy-teeth convention — the divide-vs-converge delta lives
143
  # on medium and hard).
144
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  description: >
146
- You command 4 medium tanks (2tnk) staging at the west edge
147
- (x=6, y=18..21). Two enemy clusters sit at x=60: a NORTH
148
- cluster at y=15 (2× e3 anti-tank rocketeers, range ~5) and
149
- a SOUTH cluster at y=25 (same). The clusters are 10 cells
150
- apart — engaging on the y=20 midpoint puts you in range of
151
- both at once. The winning play is divide-and-conquer: flank
152
- one cluster (e.g. via y=5 or y=35), eliminate it, then pivot
153
- to the other. Your construction yard (fact) sits at the deep
154
- rear (4,20); it must survive. Win when 4 enemies are killed,
155
- at least 3 of your tanks remain, your fact still stands, and
156
- the deadline (tick 4500) has not passed.
157
  overrides:
158
  actors:
159
  # STRIKE FORCE — 4 medium tanks at the west edge, stance:1
@@ -206,19 +214,26 @@ levels:
206
  # one cluster at a time in clean 1-vs-1-cluster geometry, preserving
207
  # all 4 tanks.
208
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  description: >
210
- You command 4 medium tanks (2tnk) staging at the west edge
211
- (x=6, y=18..21). Two HEAVY enemy clusters sit at x=60: a NORTH
212
- cluster at y=15 (3× e3 anti-tank rocketeers + 1tnk light
213
- tank) and a SOUTH cluster at y=25 (same). The clusters are
214
- 10 cells apart — engaging on the y=20 midpoint puts you inside
215
- weapon range of BOTH clusters at once (8 enemies firing on the
216
- lead). The winning play is divide-and-conquer: flank well
217
- NORTH (e.g. y=5) so only Cluster A is in range, eliminate it,
218
- then pivot SOUTH (e.g. y=35) and engage Cluster B in isolation.
219
- Your construction yard (fact) at (4,20) must survive. Win when
220
- 8 enemies are killed, at least 3 of your tanks remain, your
221
- fact still stands, and the deadline (tick 4500) has not passed.
222
  overrides:
223
  actors:
224
  - {type: 2tnk, owner: agent, position: [6, 18], stance: 1}
@@ -269,20 +284,28 @@ levels:
269
  # The survival cap also tightens to own_units_gte:3 (only 1 tank
270
  # may be lost across the whole campaign on hard).
271
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  description: >
273
- You command 4 medium tanks (2tnk) staging at ONE of two west-
274
- edge corridors (NORTH y=10..13 or SOUTH y=27..30, chosen by
275
- seed). Two HEAVY enemy clusters sit at x=60: a NORTH cluster
276
- at y=15 (3× e3 anti-tank rocketeers + 1× 1tnk) and a SOUTH
277
- cluster at y=25 (same). The clusters are 10 cells apart —
278
- engaging on the y=20 midpoint puts you inside weapon range of
279
- BOTH clusters at once (8 enemies firing on the lead). The
280
- winning play is divide-and-conquer: flank well NORTH (y=5) or
281
- SOUTH (y=35) so only ONE cluster is in range, eliminate it,
282
- then pivot to the OTHER side and engage the second cluster
283
- in isolation. Your construction yard (fact) at (4,20) must
284
- survive. Win when 8 enemies are killed, at least 3 of your
285
- tanks remain, your fact still stands, before tick 4500.
286
  overrides:
287
  actors:
288
  # spawn_point 0 — NORTH staging corridor y=10..13. Closer
 
142
  # inert-easy-teeth convention — the divide-vs-converge delta lives
143
  # on medium and hard).
144
  easy:
145
+ # Original (pre-verbosity-sweep) description preserved for
146
+ # contributors. The trimmed version below removes scripted-policy
147
+ # spoilers and cell-coord dumps; load-bearing intent kept.
148
+ #
149
+ # You command 4 medium tanks (2tnk) staging at the west edge
150
+ # (x=6, y=18..21). Two enemy clusters sit at x=60: a NORTH
151
+ # cluster at y=15 (2× e3 anti-tank rocketeers, range ~5) and
152
+ # a SOUTH cluster at y=25 (same). The clusters are 10 cells
153
+ # apart — engaging on the y=20 midpoint puts you in range of
154
+ # both at once. The winning play is divide-and-conquer: flank
155
+ # one cluster (e.g. via y=5 or y=35), eliminate it, then pivot
156
+ # to the other. Your construction yard (fact) sits at the deep
157
+ # rear (4,20); it must survive. Win when 4 enemies are killed,
158
+ # at least 3 of your tanks remain, your fact still stands, and
159
+ # the deadline (tick 4500) has not passed.
160
  description: >
161
+ Four tanks at the west edge. Two enemy rocket-infantry clusters
162
+ sit at x=60, ten cells apart at y=15 and y=25. Kill four
163
+ enemies, keep at least three tanks and your construction yard,
164
+ within about 50 turns.
 
 
 
 
 
 
 
165
  overrides:
166
  actors:
167
  # STRIKE FORCE — 4 medium tanks at the west edge, stance:1
 
214
  # one cluster at a time in clean 1-vs-1-cluster geometry, preserving
215
  # all 4 tanks.
216
  medium:
217
+ # Original (pre-verbosity-sweep) description preserved for
218
+ # contributors. The trimmed version below removes scripted-policy
219
+ # spoilers and cell-coord dumps; load-bearing intent kept.
220
+ #
221
+ # You command 4 medium tanks (2tnk) staging at the west edge
222
+ # (x=6, y=18..21). Two HEAVY enemy clusters sit at x=60: a NORTH
223
+ # cluster at y=15 (3× e3 anti-tank rocketeers + 1× 1tnk light
224
+ # tank) and a SOUTH cluster at y=25 (same). The clusters are
225
+ # 10 cells apart — engaging on the y=20 midpoint puts you inside
226
+ # weapon range of BOTH clusters at once (8 enemies firing on the
227
+ # lead). The winning play is divide-and-conquer: flank well
228
+ # NORTH (e.g. y=5) so only Cluster A is in range, eliminate it,
229
+ # then pivot SOUTH (e.g. y=35) and engage Cluster B in isolation.
230
+ # Your construction yard (fact) at (4,20) must survive. Win when
231
+ # 8 enemies are killed, at least 3 of your tanks remain, your
232
+ # fact still stands, and the deadline (tick 4500) has not passed.
233
  description: >
234
+ Heavier clusters at y=15 and y=25 (rocket soldiers plus a light
235
+ tank each, eight enemies total). Kill all eight, keep at least
236
+ three tanks and your construction yard, within about 50 turns.
 
 
 
 
 
 
 
 
 
237
  overrides:
238
  actors:
239
  - {type: 2tnk, owner: agent, position: [6, 18], stance: 1}
 
284
  # The survival cap also tightens to own_units_gte:3 (only 1 tank
285
  # may be lost across the whole campaign on hard).
286
  hard:
287
+ # Original (pre-verbosity-sweep) description preserved for
288
+ # contributors. The trimmed version below removes scripted-policy
289
+ # spoilers and cell-coord dumps; load-bearing intent kept.
290
+ #
291
+ # You command 4 medium tanks (2tnk) staging at ONE of two west-
292
+ # edge corridors (NORTH y=10..13 or SOUTH y=27..30, chosen by
293
+ # seed). Two HEAVY enemy clusters sit at x=60: a NORTH cluster
294
+ # at y=15 (3× e3 anti-tank rocketeers + 1× 1tnk) and a SOUTH
295
+ # cluster at y=25 (same). The clusters are 10 cells apart —
296
+ # engaging on the y=20 midpoint puts you inside weapon range of
297
+ # BOTH clusters at once (8 enemies firing on the lead). The
298
+ # winning play is divide-and-conquer: flank well NORTH (y=5) or
299
+ # SOUTH (y=35) so only ONE cluster is in range, eliminate it,
300
+ # then pivot to the OTHER side and engage the second cluster
301
+ # in isolation. Your construction yard (fact) at (4,20) must
302
+ # survive. Win when 8 enemies are killed, at least 3 of your
303
+ # tanks remain, your fact still stands, before tick 4500.
304
  description: >
305
+ Same heavy clusters as medium, but your tanks stage in the north
306
+ or south corridor depending on seed. Kill all eight enemies, keep
307
+ at least three tanks and your construction yard, within about
308
+ 50 turns.
 
 
 
 
 
 
 
 
 
309
  overrides:
310
  actors:
311
  # spawn_point 0 — NORTH staging corridor y=10..13. Closer
openra_bench/scenarios/packs/combat-flanking-attack.yaml CHANGED
@@ -130,20 +130,27 @@ levels:
130
  # the lead, just under one-shot range). Stall LOSES on the clock
131
  # (defenders are stance:2 ReturnFire-equivalent, never advance).
132
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  description: >
134
- Four medium tanks (2tnk) stage at the west edge (x=6, y=18..21).
135
- A short vertical line of THREE anti-tank rocket soldiers (e3,
136
- Dragon launcher, range ~5) stands at x=60, y=19..21, facing
137
- west (stance:2 — auto-fire in range, no chase). Charging
138
- head-on (east along y=20) puts the lead tank in range of all
139
- three rocket soldiers at once. The winning play is the FLANK
140
- — move the strike force off-axis (north of y=18 or south of
141
- y=22) and approach the line end-on so only 1-2 rocket
142
- soldiers are in range at any moment. Win when 3 enemy units
143
- are killed AND at least 3 of your tanks remain, before tick
144
- 4500. Stalling LOSES (kill bar unmet). Brute attack_move east
145
- can squeak the survival bar on easy — the bigger tests are
146
- on medium and hard.
147
  overrides:
148
  actors:
149
  # STRIKE FORCE — 4 medium tanks stacked at the west edge,
@@ -183,21 +190,28 @@ levels:
183
  # all 4 tanks because only 1-2 e3 are ever in range of the leading
184
  # flanker at a time.
185
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  description: >
187
- Four medium tanks (2tnk) stage at the west edge (x=6, y=18..21).
188
- A tight vertical line of FIVE anti-tank rocket soldiers (e3,
189
- Dragon launcher, range ~5) stands at x=60, y=18..22, facing
190
- west (stance:2 — auto-fire in range, no chase). Charging
191
- head-on along the engagement axis (y=20) puts the lead tank
192
- inside range of ALL FIVE rocket soldiers simultaneously —
193
- concentrated fire pressures the column. The winning play is
194
- the FLANK — move the strike force off-axis (well NORTH of
195
- y=18 or well SOUTH of y=22, e.g. y=8 or y=32) and approach
196
- the line END-ON so only one or two rocket soldiers are in
197
- Dragon range of the leading flanker at any moment; pick them
198
- off in sequence. Win when 4 enemy units are killed AND at
199
- least 3 of your tanks remain, before tick 4500. Stall and
200
- brute attack_move east both LOSE.
201
  overrides:
202
  actors:
203
  - {type: 2tnk, owner: agent, position: [6, 18], stance: 1}
@@ -241,24 +255,31 @@ levels:
241
  # symmetric across the map's mid-latitude (y=20) so either
242
  # spawn faces an equivalent flank decision.
243
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  description: >
245
- Four medium tanks (2tnk) stage at ONE of two west-edge
246
- corridors (NORTH y=14..17 OR SOUTH y=23..26, chosen by seed,
247
- anti-memorisation). A two-column wall of anti-tank rocket
248
- soldiers (e3, Dragon launcher, range ~5) — SIX in the front
249
- rank at x=60 (y=17..22) and FOUR in a back rank at x=62 —
250
- stands facing west (stance:2 — auto-fire in range, no
251
- chase). Charging east along any engagement axis between y=14
252
- and y=26 puts the lead tank inside Dragon range of MOST of
253
- the 10-e3 wall ⇒ concentrated rocket fire destroys ≥1 tank
254
- before the column clears the line. The winning play is the
255
- FLANK — approach from WELL off-axis (y=8 from the north
256
- spawn or y=32 from the south spawn) and engage the line
257
- END-ON so only one or two rocket soldiers are in range of
258
- the leading flanker at any moment. Win when 4 enemy units
259
- are killed AND ALL FOUR of your tanks remain, before tick
260
- 4500. Stall, brute attack_move east, and any frontal head-on
261
- charge all LOSE.
262
  overrides:
263
  actors:
264
  # spawn_point 0 — NORTH staging corridor (y=14..17). Closer
 
130
  # the lead, just under one-shot range). Stall LOSES on the clock
131
  # (defenders are stance:2 ReturnFire-equivalent, never advance).
132
  easy:
133
+ # Original (pre-verbosity-sweep) description preserved for
134
+ # contributors. The trimmed version below removes scripted-policy
135
+ # spoilers and cell-coord dumps; load-bearing intent kept.
136
+ #
137
+ # Four medium tanks (2tnk) stage at the west edge (x=6, y=18..21).
138
+ # A short vertical line of THREE anti-tank rocket soldiers (e3,
139
+ # Dragon launcher, range ~5) stands at x=60, y=19..21, facing
140
+ # west (stance:2 — auto-fire in range, no chase). Charging
141
+ # head-on (east along y=20) puts the lead tank in range of all
142
+ # three rocket soldiers at once. The winning play is the FLANK
143
+ # — move the strike force off-axis (north of y=18 or south of
144
+ # y=22) and approach the line end-on so only 1-2 rocket
145
+ # soldiers are in range at any moment. Win when 3 enemy units
146
+ # are killed AND at least 3 of your tanks remain, before tick
147
+ # 4500. Stalling LOSES (kill bar unmet). Brute attack_move east
148
+ # can squeak the survival bar on easy — the bigger tests are
149
+ # on medium and hard.
150
  description: >
151
+ Four tanks at the west edge face a short vertical line of three
152
+ rocket soldiers at x=60. Kill three enemies, keep at least three
153
+ tanks, within about 50 turns.
 
 
 
 
 
 
 
 
 
 
154
  overrides:
155
  actors:
156
  # STRIKE FORCE — 4 medium tanks stacked at the west edge,
 
190
  # all 4 tanks because only 1-2 e3 are ever in range of the leading
191
  # flanker at a time.
192
  medium:
193
+ # Original (pre-verbosity-sweep) description preserved for
194
+ # contributors. The trimmed version below removes scripted-policy
195
+ # spoilers and cell-coord dumps; load-bearing intent kept.
196
+ #
197
+ # Four medium tanks (2tnk) stage at the west edge (x=6, y=18..21).
198
+ # A tight vertical line of FIVE anti-tank rocket soldiers (e3,
199
+ # Dragon launcher, range ~5) stands at x=60, y=18..22, facing
200
+ # west (stance:2 — auto-fire in range, no chase). Charging
201
+ # head-on along the engagement axis (y=20) puts the lead tank
202
+ # inside range of ALL FIVE rocket soldiers simultaneously —
203
+ # concentrated fire pressures the column. The winning play is
204
+ # the FLANK — move the strike force off-axis (well NORTH of
205
+ # y=18 or well SOUTH of y=22, e.g. y=8 or y=32) and approach
206
+ # the line END-ON so only one or two rocket soldiers are in
207
+ # Dragon range of the leading flanker at any moment; pick them
208
+ # off in sequence. Win when 4 enemy units are killed AND at
209
+ # least 3 of your tanks remain, before tick 4500. Stall and
210
+ # brute attack_move east both LOSE.
211
  description: >
212
+ Tighter five-rocket-soldier line at x=60 spanning y=18 to 22.
213
+ Kill four enemies, keep at least three tanks, within about 50
214
+ turns.
 
 
 
 
 
 
 
 
 
 
 
215
  overrides:
216
  actors:
217
  - {type: 2tnk, owner: agent, position: [6, 18], stance: 1}
 
255
  # symmetric across the map's mid-latitude (y=20) so either
256
  # spawn faces an equivalent flank decision.
257
  hard:
258
+ # Original (pre-verbosity-sweep) description preserved for
259
+ # contributors. The trimmed version below removes scripted-policy
260
+ # spoilers and cell-coord dumps; load-bearing intent kept.
261
+ #
262
+ # Four medium tanks (2tnk) stage at ONE of two west-edge
263
+ # corridors (NORTH y=14..17 OR SOUTH y=23..26, chosen by seed,
264
+ # anti-memorisation). A two-column wall of anti-tank rocket
265
+ # soldiers (e3, Dragon launcher, range ~5) — SIX in the front
266
+ # rank at x=60 (y=17..22) and FOUR in a back rank at x=62 —
267
+ # stands facing west (stance:2 — auto-fire in range, no
268
+ # chase). Charging east along any engagement axis between y=14
269
+ # and y=26 puts the lead tank inside Dragon range of MOST of
270
+ # the 10-e3 wall ⇒ concentrated rocket fire destroys ≥1 tank
271
+ # before the column clears the line. The winning play is the
272
+ # FLANK — approach from WELL off-axis (y=8 from the north
273
+ # spawn or y=32 from the south spawn) and engage the line
274
+ # END-ON so only one or two rocket soldiers are in range of
275
+ # the leading flanker at any moment. Win when 4 enemy units
276
+ # are killed AND ALL FOUR of your tanks remain, before tick
277
+ # 4500. Stall, brute attack_move east, and any frontal head-on
278
+ # charge all LOSE.
279
  description: >
280
+ Two-column rocket-soldier wall at x=60 (ten enemies total). Tanks
281
+ stage in the north or south corridor depending on seed. Kill
282
+ five enemies, keep at least three tanks, within about 50 turns.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
283
  overrides:
284
  actors:
285
  # spawn_point 0 — NORTH staging corridor (y=14..17). Closer
openra_bench/scenarios/packs/def-bridge-chokepoint.yaml ADDED
@@ -0,0 +1,2046 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # def-bridge-chokepoint — chokepoint defense at narrow bridges.
2
+ # Generated; do not edit the actors/water_cells blocks by hand.
3
+ #
4
+ # Design: medium + hard each place ONE proc directly behind each bridge
5
+ # as the per-bridge target. An undefended bridge = its proc falls = LOSS
6
+ # (the win predicate requires ALL procs alive). Easy uses single-fact
7
+ # target because there's only one bridge.
8
+ #
9
+ # ⚠ Status (2026-05-23):
10
+ # easy tier: scripted-policy bar HOLDS clean — stall LOSS, intended
11
+ # (set_stance + attack_move to bridge) WIN.
12
+ # medium tier: bar holds — stall LOSS, only-centre LOSS (one proc
13
+ # falls), split-all WIN.
14
+ # hard tier: only-centre still WRONGLY wins (kill=16, all procs
15
+ # alive). Root cause: enemy stance:3 (AttackAnything)
16
+ # prefers fighting visible UNITS over buildings, so
17
+ # attackers from the outer bridges converge on the
18
+ # central defender cluster instead of marching to their
19
+ # designated proc. The defender column en route to the
20
+ # centre intercepts them all. FIX (engine, Phase 1):
21
+ # add `ScriptedBehavior::BuildingRusher` (variant of
22
+ # Rusher that explicitly attack-orders the nearest
23
+ # AGENT BUILDING per unit, not the nearest agent unit),
24
+ # then switch this pack's `enemy: {bot_type:
25
+ # building_rusher}` for medium + hard. Until then, hard
26
+ # tier should not be promoted to the active eval suite.
27
+ meta:
28
+ id: def-bridge-chokepoint
29
+ title: Bridge Chokepoint — defend every crossing before they reach you
30
+ capability: action
31
+ real_world_meaning: Geographic chokepoint defense. A barrier (water) channels every attacker through a small number of crossings
32
+ (bridges). The defender must (a) flip the engagement stance so units fire, AND (b) distribute forces across every crossing
33
+ — concentrating on one bridge leaves the others wide open and their assets fall.
34
+ robotics_analogue: Perimeter defense around a facility with a moat and discrete gates. Guards posted on standby must be
35
+ told to engage AND stationed at EACH gate — an undefended gate is a free corridor for the intruder to reach the nearest
36
+ asset.
37
+ benchmark_anchor:
38
+ - ERQA spatial commit (read terrain → assign defenders to chokepoints)
39
+ - MicroRTS chokepoint defense
40
+ - 'military doctrine: defense at obstacle-channelized terrain'
41
+ author: openra-bench
42
+ base_map: rush-hour-arena
43
+ starting_cash: 1000
44
+ base:
45
+ agent:
46
+ faction: allies
47
+ cash: 1000
48
+ enemy:
49
+ faction: soviet
50
+ cash: 0
51
+ tools:
52
+ - observe
53
+ - move_units
54
+ - attack_unit
55
+ - attack_move
56
+ - stop
57
+ - set_stance
58
+ spawn_mcvs: false
59
+ planning: true
60
+ termination:
61
+ max_ticks: 6000
62
+ actors: []
63
+ levels:
64
+ easy:
65
+ description: A water band cuts the map east-to-west with a single bridge at the centre. Six riflemen attack from the north.
66
+ Your defenders guard your base on HoldFire (stance 0) — they will NOT engage unless you flip stance and move them to
67
+ the bridge. Win = kill ≥4 attackers AND keep your fact alive, before turn 30.
68
+ starting_cash: 1000
69
+ overrides:
70
+ actors:
71
+ - &id001
72
+ type: fact
73
+ owner: agent
74
+ position:
75
+ - 10
76
+ - 33
77
+ - &id002
78
+ type: powr
79
+ owner: agent
80
+ position:
81
+ - 10
82
+ - 35
83
+ - type: e1
84
+ owner: agent
85
+ position:
86
+ - 58
87
+ - 30
88
+ stance: 0
89
+ - type: e1
90
+ owner: agent
91
+ position:
92
+ - 59
93
+ - 30
94
+ stance: 0
95
+ - type: e1
96
+ owner: agent
97
+ position:
98
+ - 60
99
+ - 30
100
+ stance: 0
101
+ - type: e1
102
+ owner: agent
103
+ position:
104
+ - 61
105
+ - 30
106
+ stance: 0
107
+ - type: e1
108
+ owner: agent
109
+ position:
110
+ - 62
111
+ - 30
112
+ stance: 0
113
+ - type: e1
114
+ owner: agent
115
+ position:
116
+ - 63
117
+ - 30
118
+ stance: 0
119
+ - type: e1
120
+ owner: enemy
121
+ position:
122
+ - 58
123
+ - 4
124
+ stance: 3
125
+ - type: e1
126
+ owner: enemy
127
+ position:
128
+ - 59
129
+ - 4
130
+ stance: 3
131
+ - type: e1
132
+ owner: enemy
133
+ position:
134
+ - 60
135
+ - 4
136
+ stance: 3
137
+ - type: e1
138
+ owner: enemy
139
+ position:
140
+ - 61
141
+ - 4
142
+ stance: 3
143
+ - type: e1
144
+ owner: enemy
145
+ position:
146
+ - 62
147
+ - 4
148
+ stance: 3
149
+ - type: e1
150
+ owner: enemy
151
+ position:
152
+ - 63
153
+ - 4
154
+ stance: 3
155
+ - type: fact
156
+ owner: enemy
157
+ position:
158
+ - 120
159
+ - 4
160
+ water_cells:
161
+ - [0, 18]
162
+ - [1, 18]
163
+ - [2, 18]
164
+ - [3, 18]
165
+ - [4, 18]
166
+ - [5, 18]
167
+ - [6, 18]
168
+ - [7, 18]
169
+ - [8, 18]
170
+ - [9, 18]
171
+ - [10, 18]
172
+ - [11, 18]
173
+ - [12, 18]
174
+ - [13, 18]
175
+ - [14, 18]
176
+ - [15, 18]
177
+ - [16, 18]
178
+ - [17, 18]
179
+ - [18, 18]
180
+ - [19, 18]
181
+ - [20, 18]
182
+ - [21, 18]
183
+ - [22, 18]
184
+ - [23, 18]
185
+ - [24, 18]
186
+ - [25, 18]
187
+ - [26, 18]
188
+ - [27, 18]
189
+ - [28, 18]
190
+ - [29, 18]
191
+ - [30, 18]
192
+ - [31, 18]
193
+ - [32, 18]
194
+ - [33, 18]
195
+ - [34, 18]
196
+ - [35, 18]
197
+ - [36, 18]
198
+ - [37, 18]
199
+ - [38, 18]
200
+ - [39, 18]
201
+ - [40, 18]
202
+ - [41, 18]
203
+ - [42, 18]
204
+ - [43, 18]
205
+ - [44, 18]
206
+ - [45, 18]
207
+ - [46, 18]
208
+ - [47, 18]
209
+ - [48, 18]
210
+ - [49, 18]
211
+ - [50, 18]
212
+ - [51, 18]
213
+ - [52, 18]
214
+ - [53, 18]
215
+ - [54, 18]
216
+ - [55, 18]
217
+ - [56, 18]
218
+ - [57, 18]
219
+ - [58, 18]
220
+ - [59, 18]
221
+ - [64, 18]
222
+ - [65, 18]
223
+ - [66, 18]
224
+ - [67, 18]
225
+ - [68, 18]
226
+ - [69, 18]
227
+ - [70, 18]
228
+ - [71, 18]
229
+ - [72, 18]
230
+ - [73, 18]
231
+ - [74, 18]
232
+ - [75, 18]
233
+ - [76, 18]
234
+ - [77, 18]
235
+ - [78, 18]
236
+ - [79, 18]
237
+ - [80, 18]
238
+ - [81, 18]
239
+ - [82, 18]
240
+ - [83, 18]
241
+ - [84, 18]
242
+ - [85, 18]
243
+ - [86, 18]
244
+ - [87, 18]
245
+ - [88, 18]
246
+ - [89, 18]
247
+ - [90, 18]
248
+ - [91, 18]
249
+ - [92, 18]
250
+ - [93, 18]
251
+ - [94, 18]
252
+ - [95, 18]
253
+ - [96, 18]
254
+ - [97, 18]
255
+ - [98, 18]
256
+ - [99, 18]
257
+ - [100, 18]
258
+ - [101, 18]
259
+ - [102, 18]
260
+ - [103, 18]
261
+ - [104, 18]
262
+ - [105, 18]
263
+ - [106, 18]
264
+ - [107, 18]
265
+ - [108, 18]
266
+ - [109, 18]
267
+ - [110, 18]
268
+ - [111, 18]
269
+ - [112, 18]
270
+ - [113, 18]
271
+ - [114, 18]
272
+ - [115, 18]
273
+ - [116, 18]
274
+ - [117, 18]
275
+ - [118, 18]
276
+ - [119, 18]
277
+ - [120, 18]
278
+ - [121, 18]
279
+ - [122, 18]
280
+ - [123, 18]
281
+ - [124, 18]
282
+ - [125, 18]
283
+ - [126, 18]
284
+ - [127, 18]
285
+ - [0, 19]
286
+ - [1, 19]
287
+ - [2, 19]
288
+ - [3, 19]
289
+ - [4, 19]
290
+ - [5, 19]
291
+ - [6, 19]
292
+ - [7, 19]
293
+ - [8, 19]
294
+ - [9, 19]
295
+ - [10, 19]
296
+ - [11, 19]
297
+ - [12, 19]
298
+ - [13, 19]
299
+ - [14, 19]
300
+ - [15, 19]
301
+ - [16, 19]
302
+ - [17, 19]
303
+ - [18, 19]
304
+ - [19, 19]
305
+ - [20, 19]
306
+ - [21, 19]
307
+ - [22, 19]
308
+ - [23, 19]
309
+ - [24, 19]
310
+ - [25, 19]
311
+ - [26, 19]
312
+ - [27, 19]
313
+ - [28, 19]
314
+ - [29, 19]
315
+ - [30, 19]
316
+ - [31, 19]
317
+ - [32, 19]
318
+ - [33, 19]
319
+ - [34, 19]
320
+ - [35, 19]
321
+ - [36, 19]
322
+ - [37, 19]
323
+ - [38, 19]
324
+ - [39, 19]
325
+ - [40, 19]
326
+ - [41, 19]
327
+ - [42, 19]
328
+ - [43, 19]
329
+ - [44, 19]
330
+ - [45, 19]
331
+ - [46, 19]
332
+ - [47, 19]
333
+ - [48, 19]
334
+ - [49, 19]
335
+ - [50, 19]
336
+ - [51, 19]
337
+ - [52, 19]
338
+ - [53, 19]
339
+ - [54, 19]
340
+ - [55, 19]
341
+ - [56, 19]
342
+ - [57, 19]
343
+ - [58, 19]
344
+ - [59, 19]
345
+ - [64, 19]
346
+ - [65, 19]
347
+ - [66, 19]
348
+ - [67, 19]
349
+ - [68, 19]
350
+ - [69, 19]
351
+ - [70, 19]
352
+ - [71, 19]
353
+ - [72, 19]
354
+ - [73, 19]
355
+ - [74, 19]
356
+ - [75, 19]
357
+ - [76, 19]
358
+ - [77, 19]
359
+ - [78, 19]
360
+ - [79, 19]
361
+ - [80, 19]
362
+ - [81, 19]
363
+ - [82, 19]
364
+ - [83, 19]
365
+ - [84, 19]
366
+ - [85, 19]
367
+ - [86, 19]
368
+ - [87, 19]
369
+ - [88, 19]
370
+ - [89, 19]
371
+ - [90, 19]
372
+ - [91, 19]
373
+ - [92, 19]
374
+ - [93, 19]
375
+ - [94, 19]
376
+ - [95, 19]
377
+ - [96, 19]
378
+ - [97, 19]
379
+ - [98, 19]
380
+ - [99, 19]
381
+ - [100, 19]
382
+ - [101, 19]
383
+ - [102, 19]
384
+ - [103, 19]
385
+ - [104, 19]
386
+ - [105, 19]
387
+ - [106, 19]
388
+ - [107, 19]
389
+ - [108, 19]
390
+ - [109, 19]
391
+ - [110, 19]
392
+ - [111, 19]
393
+ - [112, 19]
394
+ - [113, 19]
395
+ - [114, 19]
396
+ - [115, 19]
397
+ - [116, 19]
398
+ - [117, 19]
399
+ - [118, 19]
400
+ - [119, 19]
401
+ - [120, 19]
402
+ - [121, 19]
403
+ - [122, 19]
404
+ - [123, 19]
405
+ - [124, 19]
406
+ - [125, 19]
407
+ - [126, 19]
408
+ - [127, 19]
409
+ - [0, 20]
410
+ - [1, 20]
411
+ - [2, 20]
412
+ - [3, 20]
413
+ - [4, 20]
414
+ - [5, 20]
415
+ - [6, 20]
416
+ - [7, 20]
417
+ - [8, 20]
418
+ - [9, 20]
419
+ - [10, 20]
420
+ - [11, 20]
421
+ - [12, 20]
422
+ - [13, 20]
423
+ - [14, 20]
424
+ - [15, 20]
425
+ - [16, 20]
426
+ - [17, 20]
427
+ - [18, 20]
428
+ - [19, 20]
429
+ - [20, 20]
430
+ - [21, 20]
431
+ - [22, 20]
432
+ - [23, 20]
433
+ - [24, 20]
434
+ - [25, 20]
435
+ - [26, 20]
436
+ - [27, 20]
437
+ - [28, 20]
438
+ - [29, 20]
439
+ - [30, 20]
440
+ - [31, 20]
441
+ - [32, 20]
442
+ - [33, 20]
443
+ - [34, 20]
444
+ - [35, 20]
445
+ - [36, 20]
446
+ - [37, 20]
447
+ - [38, 20]
448
+ - [39, 20]
449
+ - [40, 20]
450
+ - [41, 20]
451
+ - [42, 20]
452
+ - [43, 20]
453
+ - [44, 20]
454
+ - [45, 20]
455
+ - [46, 20]
456
+ - [47, 20]
457
+ - [48, 20]
458
+ - [49, 20]
459
+ - [50, 20]
460
+ - [51, 20]
461
+ - [52, 20]
462
+ - [53, 20]
463
+ - [54, 20]
464
+ - [55, 20]
465
+ - [56, 20]
466
+ - [57, 20]
467
+ - [58, 20]
468
+ - [59, 20]
469
+ - [64, 20]
470
+ - [65, 20]
471
+ - [66, 20]
472
+ - [67, 20]
473
+ - [68, 20]
474
+ - [69, 20]
475
+ - [70, 20]
476
+ - [71, 20]
477
+ - [72, 20]
478
+ - [73, 20]
479
+ - [74, 20]
480
+ - [75, 20]
481
+ - [76, 20]
482
+ - [77, 20]
483
+ - [78, 20]
484
+ - [79, 20]
485
+ - [80, 20]
486
+ - [81, 20]
487
+ - [82, 20]
488
+ - [83, 20]
489
+ - [84, 20]
490
+ - [85, 20]
491
+ - [86, 20]
492
+ - [87, 20]
493
+ - [88, 20]
494
+ - [89, 20]
495
+ - [90, 20]
496
+ - [91, 20]
497
+ - [92, 20]
498
+ - [93, 20]
499
+ - [94, 20]
500
+ - [95, 20]
501
+ - [96, 20]
502
+ - [97, 20]
503
+ - [98, 20]
504
+ - [99, 20]
505
+ - [100, 20]
506
+ - [101, 20]
507
+ - [102, 20]
508
+ - [103, 20]
509
+ - [104, 20]
510
+ - [105, 20]
511
+ - [106, 20]
512
+ - [107, 20]
513
+ - [108, 20]
514
+ - [109, 20]
515
+ - [110, 20]
516
+ - [111, 20]
517
+ - [112, 20]
518
+ - [113, 20]
519
+ - [114, 20]
520
+ - [115, 20]
521
+ - [116, 20]
522
+ - [117, 20]
523
+ - [118, 20]
524
+ - [119, 20]
525
+ - [120, 20]
526
+ - [121, 20]
527
+ - [122, 20]
528
+ - [123, 20]
529
+ - [124, 20]
530
+ - [125, 20]
531
+ - [126, 20]
532
+ - [127, 20]
533
+ - [0, 21]
534
+ - [1, 21]
535
+ - [2, 21]
536
+ - [3, 21]
537
+ - [4, 21]
538
+ - [5, 21]
539
+ - [6, 21]
540
+ - [7, 21]
541
+ - [8, 21]
542
+ - [9, 21]
543
+ - [10, 21]
544
+ - [11, 21]
545
+ - [12, 21]
546
+ - [13, 21]
547
+ - [14, 21]
548
+ - [15, 21]
549
+ - [16, 21]
550
+ - [17, 21]
551
+ - [18, 21]
552
+ - [19, 21]
553
+ - [20, 21]
554
+ - [21, 21]
555
+ - [22, 21]
556
+ - [23, 21]
557
+ - [24, 21]
558
+ - [25, 21]
559
+ - [26, 21]
560
+ - [27, 21]
561
+ - [28, 21]
562
+ - [29, 21]
563
+ - [30, 21]
564
+ - [31, 21]
565
+ - [32, 21]
566
+ - [33, 21]
567
+ - [34, 21]
568
+ - [35, 21]
569
+ - [36, 21]
570
+ - [37, 21]
571
+ - [38, 21]
572
+ - [39, 21]
573
+ - [40, 21]
574
+ - [41, 21]
575
+ - [42, 21]
576
+ - [43, 21]
577
+ - [44, 21]
578
+ - [45, 21]
579
+ - [46, 21]
580
+ - [47, 21]
581
+ - [48, 21]
582
+ - [49, 21]
583
+ - [50, 21]
584
+ - [51, 21]
585
+ - [52, 21]
586
+ - [53, 21]
587
+ - [54, 21]
588
+ - [55, 21]
589
+ - [56, 21]
590
+ - [57, 21]
591
+ - [58, 21]
592
+ - [59, 21]
593
+ - [64, 21]
594
+ - [65, 21]
595
+ - [66, 21]
596
+ - [67, 21]
597
+ - [68, 21]
598
+ - [69, 21]
599
+ - [70, 21]
600
+ - [71, 21]
601
+ - [72, 21]
602
+ - [73, 21]
603
+ - [74, 21]
604
+ - [75, 21]
605
+ - [76, 21]
606
+ - [77, 21]
607
+ - [78, 21]
608
+ - [79, 21]
609
+ - [80, 21]
610
+ - [81, 21]
611
+ - [82, 21]
612
+ - [83, 21]
613
+ - [84, 21]
614
+ - [85, 21]
615
+ - [86, 21]
616
+ - [87, 21]
617
+ - [88, 21]
618
+ - [89, 21]
619
+ - [90, 21]
620
+ - [91, 21]
621
+ - [92, 21]
622
+ - [93, 21]
623
+ - [94, 21]
624
+ - [95, 21]
625
+ - [96, 21]
626
+ - [97, 21]
627
+ - [98, 21]
628
+ - [99, 21]
629
+ - [100, 21]
630
+ - [101, 21]
631
+ - [102, 21]
632
+ - [103, 21]
633
+ - [104, 21]
634
+ - [105, 21]
635
+ - [106, 21]
636
+ - [107, 21]
637
+ - [108, 21]
638
+ - [109, 21]
639
+ - [110, 21]
640
+ - [111, 21]
641
+ - [112, 21]
642
+ - [113, 21]
643
+ - [114, 21]
644
+ - [115, 21]
645
+ - [116, 21]
646
+ - [117, 21]
647
+ - [118, 21]
648
+ - [119, 21]
649
+ - [120, 21]
650
+ - [121, 21]
651
+ - [122, 21]
652
+ - [123, 21]
653
+ - [124, 21]
654
+ - [125, 21]
655
+ - [126, 21]
656
+ - [127, 21]
657
+ win_condition:
658
+ all_of:
659
+ - units_killed_gte: 4
660
+ - has_building: fact
661
+ - within_ticks: 2700
662
+ fail_condition:
663
+ any_of:
664
+ - after_ticks: 2701
665
+ - not:
666
+ has_building: fact
667
+ max_turns: 35
668
+ medium:
669
+ description: Two bridges (west-of-centre and east-of-centre) cross the water. Each bridge has a refinery (proc) directly
670
+ behind it that the attackers will destroy if unopposed. Six riflemen attack through EACH bridge. Defenders are on HoldFire
671
+ (stance 0) at the centre; you must split + flip stance + intercept at both bridges. Win = kill ≥8 attackers AND keep
672
+ BOTH procs alive, before turn 35.
673
+ starting_cash: 1000
674
+ overrides:
675
+ actors:
676
+ - *id001
677
+ - *id002
678
+ - type: proc
679
+ owner: agent
680
+ position:
681
+ - 40
682
+ - 26
683
+ - type: proc
684
+ owner: agent
685
+ position:
686
+ - 82
687
+ - 26
688
+ - type: e1
689
+ owner: agent
690
+ position:
691
+ - 59
692
+ - 30
693
+ stance: 0
694
+ - type: e1
695
+ owner: agent
696
+ position:
697
+ - 60
698
+ - 30
699
+ stance: 0
700
+ - type: e1
701
+ owner: agent
702
+ position:
703
+ - 61
704
+ - 30
705
+ stance: 0
706
+ - type: e1
707
+ owner: agent
708
+ position:
709
+ - 62
710
+ - 30
711
+ stance: 0
712
+ - type: e1
713
+ owner: agent
714
+ position:
715
+ - 63
716
+ - 30
717
+ stance: 0
718
+ - type: e1
719
+ owner: agent
720
+ position:
721
+ - 64
722
+ - 30
723
+ stance: 0
724
+ - type: e1
725
+ owner: agent
726
+ position:
727
+ - 65
728
+ - 30
729
+ stance: 0
730
+ - type: e1
731
+ owner: agent
732
+ position:
733
+ - 59
734
+ - 31
735
+ stance: 0
736
+ - type: e1
737
+ owner: agent
738
+ position:
739
+ - 60
740
+ - 31
741
+ stance: 0
742
+ - type: e1
743
+ owner: agent
744
+ position:
745
+ - 61
746
+ - 31
747
+ stance: 0
748
+ - type: e1
749
+ owner: agent
750
+ position:
751
+ - 62
752
+ - 31
753
+ stance: 0
754
+ - type: e1
755
+ owner: agent
756
+ position:
757
+ - 63
758
+ - 31
759
+ stance: 0
760
+ - type: e1
761
+ owner: enemy
762
+ position:
763
+ - 38
764
+ - 4
765
+ stance: 3
766
+ - type: e1
767
+ owner: enemy
768
+ position:
769
+ - 39
770
+ - 4
771
+ stance: 3
772
+ - type: e1
773
+ owner: enemy
774
+ position:
775
+ - 40
776
+ - 4
777
+ stance: 3
778
+ - type: e1
779
+ owner: enemy
780
+ position:
781
+ - 41
782
+ - 4
783
+ stance: 3
784
+ - type: e1
785
+ owner: enemy
786
+ position:
787
+ - 42
788
+ - 4
789
+ stance: 3
790
+ - type: e1
791
+ owner: enemy
792
+ position:
793
+ - 43
794
+ - 4
795
+ stance: 3
796
+ - type: e1
797
+ owner: enemy
798
+ position:
799
+ - 80
800
+ - 4
801
+ stance: 3
802
+ - type: e1
803
+ owner: enemy
804
+ position:
805
+ - 81
806
+ - 4
807
+ stance: 3
808
+ - type: e1
809
+ owner: enemy
810
+ position:
811
+ - 82
812
+ - 4
813
+ stance: 3
814
+ - type: e1
815
+ owner: enemy
816
+ position:
817
+ - 83
818
+ - 4
819
+ stance: 3
820
+ - type: e1
821
+ owner: enemy
822
+ position:
823
+ - 84
824
+ - 4
825
+ stance: 3
826
+ - type: e1
827
+ owner: enemy
828
+ position:
829
+ - 85
830
+ - 4
831
+ stance: 3
832
+ - type: fact
833
+ owner: enemy
834
+ position:
835
+ - 120
836
+ - 4
837
+ water_cells:
838
+ - [0, 18]
839
+ - [1, 18]
840
+ - [2, 18]
841
+ - [3, 18]
842
+ - [4, 18]
843
+ - [5, 18]
844
+ - [6, 18]
845
+ - [7, 18]
846
+ - [8, 18]
847
+ - [9, 18]
848
+ - [10, 18]
849
+ - [11, 18]
850
+ - [12, 18]
851
+ - [13, 18]
852
+ - [14, 18]
853
+ - [15, 18]
854
+ - [16, 18]
855
+ - [17, 18]
856
+ - [18, 18]
857
+ - [19, 18]
858
+ - [20, 18]
859
+ - [21, 18]
860
+ - [22, 18]
861
+ - [23, 18]
862
+ - [24, 18]
863
+ - [25, 18]
864
+ - [26, 18]
865
+ - [27, 18]
866
+ - [28, 18]
867
+ - [29, 18]
868
+ - [30, 18]
869
+ - [31, 18]
870
+ - [32, 18]
871
+ - [33, 18]
872
+ - [34, 18]
873
+ - [35, 18]
874
+ - [36, 18]
875
+ - [37, 18]
876
+ - [38, 18]
877
+ - [39, 18]
878
+ - [44, 18]
879
+ - [45, 18]
880
+ - [46, 18]
881
+ - [47, 18]
882
+ - [48, 18]
883
+ - [49, 18]
884
+ - [50, 18]
885
+ - [51, 18]
886
+ - [52, 18]
887
+ - [53, 18]
888
+ - [54, 18]
889
+ - [55, 18]
890
+ - [56, 18]
891
+ - [57, 18]
892
+ - [58, 18]
893
+ - [59, 18]
894
+ - [60, 18]
895
+ - [61, 18]
896
+ - [62, 18]
897
+ - [63, 18]
898
+ - [64, 18]
899
+ - [65, 18]
900
+ - [66, 18]
901
+ - [67, 18]
902
+ - [68, 18]
903
+ - [69, 18]
904
+ - [70, 18]
905
+ - [71, 18]
906
+ - [72, 18]
907
+ - [73, 18]
908
+ - [74, 18]
909
+ - [75, 18]
910
+ - [76, 18]
911
+ - [77, 18]
912
+ - [78, 18]
913
+ - [79, 18]
914
+ - [80, 18]
915
+ - [81, 18]
916
+ - [86, 18]
917
+ - [87, 18]
918
+ - [88, 18]
919
+ - [89, 18]
920
+ - [90, 18]
921
+ - [91, 18]
922
+ - [92, 18]
923
+ - [93, 18]
924
+ - [94, 18]
925
+ - [95, 18]
926
+ - [96, 18]
927
+ - [97, 18]
928
+ - [98, 18]
929
+ - [99, 18]
930
+ - [100, 18]
931
+ - [101, 18]
932
+ - [102, 18]
933
+ - [103, 18]
934
+ - [104, 18]
935
+ - [105, 18]
936
+ - [106, 18]
937
+ - [107, 18]
938
+ - [108, 18]
939
+ - [109, 18]
940
+ - [110, 18]
941
+ - [111, 18]
942
+ - [112, 18]
943
+ - [113, 18]
944
+ - [114, 18]
945
+ - [115, 18]
946
+ - [116, 18]
947
+ - [117, 18]
948
+ - [118, 18]
949
+ - [119, 18]
950
+ - [120, 18]
951
+ - [121, 18]
952
+ - [122, 18]
953
+ - [123, 18]
954
+ - [124, 18]
955
+ - [125, 18]
956
+ - [126, 18]
957
+ - [127, 18]
958
+ - [0, 19]
959
+ - [1, 19]
960
+ - [2, 19]
961
+ - [3, 19]
962
+ - [4, 19]
963
+ - [5, 19]
964
+ - [6, 19]
965
+ - [7, 19]
966
+ - [8, 19]
967
+ - [9, 19]
968
+ - [10, 19]
969
+ - [11, 19]
970
+ - [12, 19]
971
+ - [13, 19]
972
+ - [14, 19]
973
+ - [15, 19]
974
+ - [16, 19]
975
+ - [17, 19]
976
+ - [18, 19]
977
+ - [19, 19]
978
+ - [20, 19]
979
+ - [21, 19]
980
+ - [22, 19]
981
+ - [23, 19]
982
+ - [24, 19]
983
+ - [25, 19]
984
+ - [26, 19]
985
+ - [27, 19]
986
+ - [28, 19]
987
+ - [29, 19]
988
+ - [30, 19]
989
+ - [31, 19]
990
+ - [32, 19]
991
+ - [33, 19]
992
+ - [34, 19]
993
+ - [35, 19]
994
+ - [36, 19]
995
+ - [37, 19]
996
+ - [38, 19]
997
+ - [39, 19]
998
+ - [44, 19]
999
+ - [45, 19]
1000
+ - [46, 19]
1001
+ - [47, 19]
1002
+ - [48, 19]
1003
+ - [49, 19]
1004
+ - [50, 19]
1005
+ - [51, 19]
1006
+ - [52, 19]
1007
+ - [53, 19]
1008
+ - [54, 19]
1009
+ - [55, 19]
1010
+ - [56, 19]
1011
+ - [57, 19]
1012
+ - [58, 19]
1013
+ - [59, 19]
1014
+ - [60, 19]
1015
+ - [61, 19]
1016
+ - [62, 19]
1017
+ - [63, 19]
1018
+ - [64, 19]
1019
+ - [65, 19]
1020
+ - [66, 19]
1021
+ - [67, 19]
1022
+ - [68, 19]
1023
+ - [69, 19]
1024
+ - [70, 19]
1025
+ - [71, 19]
1026
+ - [72, 19]
1027
+ - [73, 19]
1028
+ - [74, 19]
1029
+ - [75, 19]
1030
+ - [76, 19]
1031
+ - [77, 19]
1032
+ - [78, 19]
1033
+ - [79, 19]
1034
+ - [80, 19]
1035
+ - [81, 19]
1036
+ - [86, 19]
1037
+ - [87, 19]
1038
+ - [88, 19]
1039
+ - [89, 19]
1040
+ - [90, 19]
1041
+ - [91, 19]
1042
+ - [92, 19]
1043
+ - [93, 19]
1044
+ - [94, 19]
1045
+ - [95, 19]
1046
+ - [96, 19]
1047
+ - [97, 19]
1048
+ - [98, 19]
1049
+ - [99, 19]
1050
+ - [100, 19]
1051
+ - [101, 19]
1052
+ - [102, 19]
1053
+ - [103, 19]
1054
+ - [104, 19]
1055
+ - [105, 19]
1056
+ - [106, 19]
1057
+ - [107, 19]
1058
+ - [108, 19]
1059
+ - [109, 19]
1060
+ - [110, 19]
1061
+ - [111, 19]
1062
+ - [112, 19]
1063
+ - [113, 19]
1064
+ - [114, 19]
1065
+ - [115, 19]
1066
+ - [116, 19]
1067
+ - [117, 19]
1068
+ - [118, 19]
1069
+ - [119, 19]
1070
+ - [120, 19]
1071
+ - [121, 19]
1072
+ - [122, 19]
1073
+ - [123, 19]
1074
+ - [124, 19]
1075
+ - [125, 19]
1076
+ - [126, 19]
1077
+ - [127, 19]
1078
+ - [0, 20]
1079
+ - [1, 20]
1080
+ - [2, 20]
1081
+ - [3, 20]
1082
+ - [4, 20]
1083
+ - [5, 20]
1084
+ - [6, 20]
1085
+ - [7, 20]
1086
+ - [8, 20]
1087
+ - [9, 20]
1088
+ - [10, 20]
1089
+ - [11, 20]
1090
+ - [12, 20]
1091
+ - [13, 20]
1092
+ - [14, 20]
1093
+ - [15, 20]
1094
+ - [16, 20]
1095
+ - [17, 20]
1096
+ - [18, 20]
1097
+ - [19, 20]
1098
+ - [20, 20]
1099
+ - [21, 20]
1100
+ - [22, 20]
1101
+ - [23, 20]
1102
+ - [24, 20]
1103
+ - [25, 20]
1104
+ - [26, 20]
1105
+ - [27, 20]
1106
+ - [28, 20]
1107
+ - [29, 20]
1108
+ - [30, 20]
1109
+ - [31, 20]
1110
+ - [32, 20]
1111
+ - [33, 20]
1112
+ - [34, 20]
1113
+ - [35, 20]
1114
+ - [36, 20]
1115
+ - [37, 20]
1116
+ - [38, 20]
1117
+ - [39, 20]
1118
+ - [44, 20]
1119
+ - [45, 20]
1120
+ - [46, 20]
1121
+ - [47, 20]
1122
+ - [48, 20]
1123
+ - [49, 20]
1124
+ - [50, 20]
1125
+ - [51, 20]
1126
+ - [52, 20]
1127
+ - [53, 20]
1128
+ - [54, 20]
1129
+ - [55, 20]
1130
+ - [56, 20]
1131
+ - [57, 20]
1132
+ - [58, 20]
1133
+ - [59, 20]
1134
+ - [60, 20]
1135
+ - [61, 20]
1136
+ - [62, 20]
1137
+ - [63, 20]
1138
+ - [64, 20]
1139
+ - [65, 20]
1140
+ - [66, 20]
1141
+ - [67, 20]
1142
+ - [68, 20]
1143
+ - [69, 20]
1144
+ - [70, 20]
1145
+ - [71, 20]
1146
+ - [72, 20]
1147
+ - [73, 20]
1148
+ - [74, 20]
1149
+ - [75, 20]
1150
+ - [76, 20]
1151
+ - [77, 20]
1152
+ - [78, 20]
1153
+ - [79, 20]
1154
+ - [80, 20]
1155
+ - [81, 20]
1156
+ - [86, 20]
1157
+ - [87, 20]
1158
+ - [88, 20]
1159
+ - [89, 20]
1160
+ - [90, 20]
1161
+ - [91, 20]
1162
+ - [92, 20]
1163
+ - [93, 20]
1164
+ - [94, 20]
1165
+ - [95, 20]
1166
+ - [96, 20]
1167
+ - [97, 20]
1168
+ - [98, 20]
1169
+ - [99, 20]
1170
+ - [100, 20]
1171
+ - [101, 20]
1172
+ - [102, 20]
1173
+ - [103, 20]
1174
+ - [104, 20]
1175
+ - [105, 20]
1176
+ - [106, 20]
1177
+ - [107, 20]
1178
+ - [108, 20]
1179
+ - [109, 20]
1180
+ - [110, 20]
1181
+ - [111, 20]
1182
+ - [112, 20]
1183
+ - [113, 20]
1184
+ - [114, 20]
1185
+ - [115, 20]
1186
+ - [116, 20]
1187
+ - [117, 20]
1188
+ - [118, 20]
1189
+ - [119, 20]
1190
+ - [120, 20]
1191
+ - [121, 20]
1192
+ - [122, 20]
1193
+ - [123, 20]
1194
+ - [124, 20]
1195
+ - [125, 20]
1196
+ - [126, 20]
1197
+ - [127, 20]
1198
+ - [0, 21]
1199
+ - [1, 21]
1200
+ - [2, 21]
1201
+ - [3, 21]
1202
+ - [4, 21]
1203
+ - [5, 21]
1204
+ - [6, 21]
1205
+ - [7, 21]
1206
+ - [8, 21]
1207
+ - [9, 21]
1208
+ - [10, 21]
1209
+ - [11, 21]
1210
+ - [12, 21]
1211
+ - [13, 21]
1212
+ - [14, 21]
1213
+ - [15, 21]
1214
+ - [16, 21]
1215
+ - [17, 21]
1216
+ - [18, 21]
1217
+ - [19, 21]
1218
+ - [20, 21]
1219
+ - [21, 21]
1220
+ - [22, 21]
1221
+ - [23, 21]
1222
+ - [24, 21]
1223
+ - [25, 21]
1224
+ - [26, 21]
1225
+ - [27, 21]
1226
+ - [28, 21]
1227
+ - [29, 21]
1228
+ - [30, 21]
1229
+ - [31, 21]
1230
+ - [32, 21]
1231
+ - [33, 21]
1232
+ - [34, 21]
1233
+ - [35, 21]
1234
+ - [36, 21]
1235
+ - [37, 21]
1236
+ - [38, 21]
1237
+ - [39, 21]
1238
+ - [44, 21]
1239
+ - [45, 21]
1240
+ - [46, 21]
1241
+ - [47, 21]
1242
+ - [48, 21]
1243
+ - [49, 21]
1244
+ - [50, 21]
1245
+ - [51, 21]
1246
+ - [52, 21]
1247
+ - [53, 21]
1248
+ - [54, 21]
1249
+ - [55, 21]
1250
+ - [56, 21]
1251
+ - [57, 21]
1252
+ - [58, 21]
1253
+ - [59, 21]
1254
+ - [60, 21]
1255
+ - [61, 21]
1256
+ - [62, 21]
1257
+ - [63, 21]
1258
+ - [64, 21]
1259
+ - [65, 21]
1260
+ - [66, 21]
1261
+ - [67, 21]
1262
+ - [68, 21]
1263
+ - [69, 21]
1264
+ - [70, 21]
1265
+ - [71, 21]
1266
+ - [72, 21]
1267
+ - [73, 21]
1268
+ - [74, 21]
1269
+ - [75, 21]
1270
+ - [76, 21]
1271
+ - [77, 21]
1272
+ - [78, 21]
1273
+ - [79, 21]
1274
+ - [80, 21]
1275
+ - [81, 21]
1276
+ - [86, 21]
1277
+ - [87, 21]
1278
+ - [88, 21]
1279
+ - [89, 21]
1280
+ - [90, 21]
1281
+ - [91, 21]
1282
+ - [92, 21]
1283
+ - [93, 21]
1284
+ - [94, 21]
1285
+ - [95, 21]
1286
+ - [96, 21]
1287
+ - [97, 21]
1288
+ - [98, 21]
1289
+ - [99, 21]
1290
+ - [100, 21]
1291
+ - [101, 21]
1292
+ - [102, 21]
1293
+ - [103, 21]
1294
+ - [104, 21]
1295
+ - [105, 21]
1296
+ - [106, 21]
1297
+ - [107, 21]
1298
+ - [108, 21]
1299
+ - [109, 21]
1300
+ - [110, 21]
1301
+ - [111, 21]
1302
+ - [112, 21]
1303
+ - [113, 21]
1304
+ - [114, 21]
1305
+ - [115, 21]
1306
+ - [116, 21]
1307
+ - [117, 21]
1308
+ - [118, 21]
1309
+ - [119, 21]
1310
+ - [120, 21]
1311
+ - [121, 21]
1312
+ - [122, 21]
1313
+ - [123, 21]
1314
+ - [124, 21]
1315
+ - [125, 21]
1316
+ - [126, 21]
1317
+ - [127, 21]
1318
+ win_condition:
1319
+ all_of:
1320
+ - units_killed_gte: 8
1321
+ - has_building: fact
1322
+ - within_ticks: 3150
1323
+ - building_count_gte:
1324
+ type: proc
1325
+ n: 2
1326
+ fail_condition:
1327
+ any_of:
1328
+ - after_ticks: 3151
1329
+ - not:
1330
+ has_building: fact
1331
+ - not:
1332
+ building_count_gte:
1333
+ type: proc
1334
+ n: 2
1335
+ max_turns: 40
1336
+ hard:
1337
+ description: Three bridges (west, centre, east) cross the water. Each bridge has a refinery (proc) directly behind it
1338
+ that the attackers will destroy if unopposed. Five riflemen attack through EACH bridge. Defenders are on HoldFire (stance
1339
+ 0) at the centre. You must split into thirds + flip stance + intercept at all three bridges. Win = kill ≥10 attackers
1340
+ AND keep ALL THREE procs alive, before turn 40.
1341
+ starting_cash: 1000
1342
+ overrides:
1343
+ actors:
1344
+ - *id001
1345
+ - *id002
1346
+ - type: proc
1347
+ owner: agent
1348
+ position:
1349
+ - 28
1350
+ - 26
1351
+ - type: proc
1352
+ owner: agent
1353
+ position:
1354
+ - 60
1355
+ - 26
1356
+ - type: proc
1357
+ owner: agent
1358
+ position:
1359
+ - 92
1360
+ - 26
1361
+ - type: e1
1362
+ owner: agent
1363
+ position:
1364
+ - 58
1365
+ - 30
1366
+ stance: 0
1367
+ - type: e1
1368
+ owner: agent
1369
+ position:
1370
+ - 59
1371
+ - 30
1372
+ stance: 0
1373
+ - type: e1
1374
+ owner: agent
1375
+ position:
1376
+ - 60
1377
+ - 30
1378
+ stance: 0
1379
+ - type: e1
1380
+ owner: agent
1381
+ position:
1382
+ - 61
1383
+ - 30
1384
+ stance: 0
1385
+ - type: e1
1386
+ owner: agent
1387
+ position:
1388
+ - 62
1389
+ - 30
1390
+ stance: 0
1391
+ - type: e1
1392
+ owner: agent
1393
+ position:
1394
+ - 63
1395
+ - 30
1396
+ stance: 0
1397
+ - type: e1
1398
+ owner: agent
1399
+ position:
1400
+ - 64
1401
+ - 30
1402
+ stance: 0
1403
+ - type: e1
1404
+ owner: agent
1405
+ position:
1406
+ - 58
1407
+ - 31
1408
+ stance: 0
1409
+ - type: e1
1410
+ owner: agent
1411
+ position:
1412
+ - 59
1413
+ - 31
1414
+ stance: 0
1415
+ - type: e1
1416
+ owner: agent
1417
+ position:
1418
+ - 60
1419
+ - 31
1420
+ stance: 0
1421
+ - type: e1
1422
+ owner: agent
1423
+ position:
1424
+ - 61
1425
+ - 31
1426
+ stance: 0
1427
+ - type: e1
1428
+ owner: agent
1429
+ position:
1430
+ - 62
1431
+ - 31
1432
+ stance: 0
1433
+ - type: e1
1434
+ owner: agent
1435
+ position:
1436
+ - 63
1437
+ - 31
1438
+ stance: 0
1439
+ - type: e1
1440
+ owner: agent
1441
+ position:
1442
+ - 64
1443
+ - 31
1444
+ stance: 0
1445
+ - type: e1
1446
+ owner: agent
1447
+ position:
1448
+ - 58
1449
+ - 32
1450
+ stance: 0
1451
+ - type: e1
1452
+ owner: agent
1453
+ position:
1454
+ - 59
1455
+ - 32
1456
+ stance: 0
1457
+ - type: e1
1458
+ owner: agent
1459
+ position:
1460
+ - 60
1461
+ - 32
1462
+ stance: 0
1463
+ - type: e1
1464
+ owner: agent
1465
+ position:
1466
+ - 61
1467
+ - 32
1468
+ stance: 0
1469
+ - type: e1
1470
+ owner: enemy
1471
+ position:
1472
+ - 26
1473
+ - 4
1474
+ stance: 3
1475
+ - type: e1
1476
+ owner: enemy
1477
+ position:
1478
+ - 27
1479
+ - 4
1480
+ stance: 3
1481
+ - type: e1
1482
+ owner: enemy
1483
+ position:
1484
+ - 28
1485
+ - 4
1486
+ stance: 3
1487
+ - type: e1
1488
+ owner: enemy
1489
+ position:
1490
+ - 29
1491
+ - 4
1492
+ stance: 3
1493
+ - type: e1
1494
+ owner: enemy
1495
+ position:
1496
+ - 30
1497
+ - 4
1498
+ stance: 3
1499
+ - type: e1
1500
+ owner: enemy
1501
+ position:
1502
+ - 58
1503
+ - 4
1504
+ stance: 3
1505
+ - type: e1
1506
+ owner: enemy
1507
+ position:
1508
+ - 59
1509
+ - 4
1510
+ stance: 3
1511
+ - type: e1
1512
+ owner: enemy
1513
+ position:
1514
+ - 60
1515
+ - 4
1516
+ stance: 3
1517
+ - type: e1
1518
+ owner: enemy
1519
+ position:
1520
+ - 61
1521
+ - 4
1522
+ stance: 3
1523
+ - type: e1
1524
+ owner: enemy
1525
+ position:
1526
+ - 62
1527
+ - 4
1528
+ stance: 3
1529
+ - type: e1
1530
+ owner: enemy
1531
+ position:
1532
+ - 90
1533
+ - 4
1534
+ stance: 3
1535
+ - type: e1
1536
+ owner: enemy
1537
+ position:
1538
+ - 91
1539
+ - 4
1540
+ stance: 3
1541
+ - type: e1
1542
+ owner: enemy
1543
+ position:
1544
+ - 92
1545
+ - 4
1546
+ stance: 3
1547
+ - type: e1
1548
+ owner: enemy
1549
+ position:
1550
+ - 93
1551
+ - 4
1552
+ stance: 3
1553
+ - type: e1
1554
+ owner: enemy
1555
+ position:
1556
+ - 94
1557
+ - 4
1558
+ stance: 3
1559
+ - type: fact
1560
+ owner: enemy
1561
+ position:
1562
+ - 120
1563
+ - 4
1564
+ water_cells:
1565
+ - [0, 18]
1566
+ - [1, 18]
1567
+ - [2, 18]
1568
+ - [3, 18]
1569
+ - [4, 18]
1570
+ - [5, 18]
1571
+ - [6, 18]
1572
+ - [7, 18]
1573
+ - [8, 18]
1574
+ - [9, 18]
1575
+ - [10, 18]
1576
+ - [11, 18]
1577
+ - [12, 18]
1578
+ - [13, 18]
1579
+ - [14, 18]
1580
+ - [15, 18]
1581
+ - [16, 18]
1582
+ - [17, 18]
1583
+ - [18, 18]
1584
+ - [19, 18]
1585
+ - [20, 18]
1586
+ - [21, 18]
1587
+ - [22, 18]
1588
+ - [23, 18]
1589
+ - [24, 18]
1590
+ - [25, 18]
1591
+ - [26, 18]
1592
+ - [27, 18]
1593
+ - [32, 18]
1594
+ - [33, 18]
1595
+ - [34, 18]
1596
+ - [35, 18]
1597
+ - [36, 18]
1598
+ - [37, 18]
1599
+ - [38, 18]
1600
+ - [39, 18]
1601
+ - [40, 18]
1602
+ - [41, 18]
1603
+ - [42, 18]
1604
+ - [43, 18]
1605
+ - [44, 18]
1606
+ - [45, 18]
1607
+ - [46, 18]
1608
+ - [47, 18]
1609
+ - [48, 18]
1610
+ - [49, 18]
1611
+ - [50, 18]
1612
+ - [51, 18]
1613
+ - [52, 18]
1614
+ - [53, 18]
1615
+ - [54, 18]
1616
+ - [55, 18]
1617
+ - [56, 18]
1618
+ - [57, 18]
1619
+ - [58, 18]
1620
+ - [59, 18]
1621
+ - [64, 18]
1622
+ - [65, 18]
1623
+ - [66, 18]
1624
+ - [67, 18]
1625
+ - [68, 18]
1626
+ - [69, 18]
1627
+ - [70, 18]
1628
+ - [71, 18]
1629
+ - [72, 18]
1630
+ - [73, 18]
1631
+ - [74, 18]
1632
+ - [75, 18]
1633
+ - [76, 18]
1634
+ - [77, 18]
1635
+ - [78, 18]
1636
+ - [79, 18]
1637
+ - [80, 18]
1638
+ - [81, 18]
1639
+ - [82, 18]
1640
+ - [83, 18]
1641
+ - [84, 18]
1642
+ - [85, 18]
1643
+ - [86, 18]
1644
+ - [87, 18]
1645
+ - [88, 18]
1646
+ - [89, 18]
1647
+ - [90, 18]
1648
+ - [91, 18]
1649
+ - [96, 18]
1650
+ - [97, 18]
1651
+ - [98, 18]
1652
+ - [99, 18]
1653
+ - [100, 18]
1654
+ - [101, 18]
1655
+ - [102, 18]
1656
+ - [103, 18]
1657
+ - [104, 18]
1658
+ - [105, 18]
1659
+ - [106, 18]
1660
+ - [107, 18]
1661
+ - [108, 18]
1662
+ - [109, 18]
1663
+ - [110, 18]
1664
+ - [111, 18]
1665
+ - [112, 18]
1666
+ - [113, 18]
1667
+ - [114, 18]
1668
+ - [115, 18]
1669
+ - [116, 18]
1670
+ - [117, 18]
1671
+ - [118, 18]
1672
+ - [119, 18]
1673
+ - [120, 18]
1674
+ - [121, 18]
1675
+ - [122, 18]
1676
+ - [123, 18]
1677
+ - [124, 18]
1678
+ - [125, 18]
1679
+ - [126, 18]
1680
+ - [127, 18]
1681
+ - [0, 19]
1682
+ - [1, 19]
1683
+ - [2, 19]
1684
+ - [3, 19]
1685
+ - [4, 19]
1686
+ - [5, 19]
1687
+ - [6, 19]
1688
+ - [7, 19]
1689
+ - [8, 19]
1690
+ - [9, 19]
1691
+ - [10, 19]
1692
+ - [11, 19]
1693
+ - [12, 19]
1694
+ - [13, 19]
1695
+ - [14, 19]
1696
+ - [15, 19]
1697
+ - [16, 19]
1698
+ - [17, 19]
1699
+ - [18, 19]
1700
+ - [19, 19]
1701
+ - [20, 19]
1702
+ - [21, 19]
1703
+ - [22, 19]
1704
+ - [23, 19]
1705
+ - [24, 19]
1706
+ - [25, 19]
1707
+ - [26, 19]
1708
+ - [27, 19]
1709
+ - [32, 19]
1710
+ - [33, 19]
1711
+ - [34, 19]
1712
+ - [35, 19]
1713
+ - [36, 19]
1714
+ - [37, 19]
1715
+ - [38, 19]
1716
+ - [39, 19]
1717
+ - [40, 19]
1718
+ - [41, 19]
1719
+ - [42, 19]
1720
+ - [43, 19]
1721
+ - [44, 19]
1722
+ - [45, 19]
1723
+ - [46, 19]
1724
+ - [47, 19]
1725
+ - [48, 19]
1726
+ - [49, 19]
1727
+ - [50, 19]
1728
+ - [51, 19]
1729
+ - [52, 19]
1730
+ - [53, 19]
1731
+ - [54, 19]
1732
+ - [55, 19]
1733
+ - [56, 19]
1734
+ - [57, 19]
1735
+ - [58, 19]
1736
+ - [59, 19]
1737
+ - [64, 19]
1738
+ - [65, 19]
1739
+ - [66, 19]
1740
+ - [67, 19]
1741
+ - [68, 19]
1742
+ - [69, 19]
1743
+ - [70, 19]
1744
+ - [71, 19]
1745
+ - [72, 19]
1746
+ - [73, 19]
1747
+ - [74, 19]
1748
+ - [75, 19]
1749
+ - [76, 19]
1750
+ - [77, 19]
1751
+ - [78, 19]
1752
+ - [79, 19]
1753
+ - [80, 19]
1754
+ - [81, 19]
1755
+ - [82, 19]
1756
+ - [83, 19]
1757
+ - [84, 19]
1758
+ - [85, 19]
1759
+ - [86, 19]
1760
+ - [87, 19]
1761
+ - [88, 19]
1762
+ - [89, 19]
1763
+ - [90, 19]
1764
+ - [91, 19]
1765
+ - [96, 19]
1766
+ - [97, 19]
1767
+ - [98, 19]
1768
+ - [99, 19]
1769
+ - [100, 19]
1770
+ - [101, 19]
1771
+ - [102, 19]
1772
+ - [103, 19]
1773
+ - [104, 19]
1774
+ - [105, 19]
1775
+ - [106, 19]
1776
+ - [107, 19]
1777
+ - [108, 19]
1778
+ - [109, 19]
1779
+ - [110, 19]
1780
+ - [111, 19]
1781
+ - [112, 19]
1782
+ - [113, 19]
1783
+ - [114, 19]
1784
+ - [115, 19]
1785
+ - [116, 19]
1786
+ - [117, 19]
1787
+ - [118, 19]
1788
+ - [119, 19]
1789
+ - [120, 19]
1790
+ - [121, 19]
1791
+ - [122, 19]
1792
+ - [123, 19]
1793
+ - [124, 19]
1794
+ - [125, 19]
1795
+ - [126, 19]
1796
+ - [127, 19]
1797
+ - [0, 20]
1798
+ - [1, 20]
1799
+ - [2, 20]
1800
+ - [3, 20]
1801
+ - [4, 20]
1802
+ - [5, 20]
1803
+ - [6, 20]
1804
+ - [7, 20]
1805
+ - [8, 20]
1806
+ - [9, 20]
1807
+ - [10, 20]
1808
+ - [11, 20]
1809
+ - [12, 20]
1810
+ - [13, 20]
1811
+ - [14, 20]
1812
+ - [15, 20]
1813
+ - [16, 20]
1814
+ - [17, 20]
1815
+ - [18, 20]
1816
+ - [19, 20]
1817
+ - [20, 20]
1818
+ - [21, 20]
1819
+ - [22, 20]
1820
+ - [23, 20]
1821
+ - [24, 20]
1822
+ - [25, 20]
1823
+ - [26, 20]
1824
+ - [27, 20]
1825
+ - [32, 20]
1826
+ - [33, 20]
1827
+ - [34, 20]
1828
+ - [35, 20]
1829
+ - [36, 20]
1830
+ - [37, 20]
1831
+ - [38, 20]
1832
+ - [39, 20]
1833
+ - [40, 20]
1834
+ - [41, 20]
1835
+ - [42, 20]
1836
+ - [43, 20]
1837
+ - [44, 20]
1838
+ - [45, 20]
1839
+ - [46, 20]
1840
+ - [47, 20]
1841
+ - [48, 20]
1842
+ - [49, 20]
1843
+ - [50, 20]
1844
+ - [51, 20]
1845
+ - [52, 20]
1846
+ - [53, 20]
1847
+ - [54, 20]
1848
+ - [55, 20]
1849
+ - [56, 20]
1850
+ - [57, 20]
1851
+ - [58, 20]
1852
+ - [59, 20]
1853
+ - [64, 20]
1854
+ - [65, 20]
1855
+ - [66, 20]
1856
+ - [67, 20]
1857
+ - [68, 20]
1858
+ - [69, 20]
1859
+ - [70, 20]
1860
+ - [71, 20]
1861
+ - [72, 20]
1862
+ - [73, 20]
1863
+ - [74, 20]
1864
+ - [75, 20]
1865
+ - [76, 20]
1866
+ - [77, 20]
1867
+ - [78, 20]
1868
+ - [79, 20]
1869
+ - [80, 20]
1870
+ - [81, 20]
1871
+ - [82, 20]
1872
+ - [83, 20]
1873
+ - [84, 20]
1874
+ - [85, 20]
1875
+ - [86, 20]
1876
+ - [87, 20]
1877
+ - [88, 20]
1878
+ - [89, 20]
1879
+ - [90, 20]
1880
+ - [91, 20]
1881
+ - [96, 20]
1882
+ - [97, 20]
1883
+ - [98, 20]
1884
+ - [99, 20]
1885
+ - [100, 20]
1886
+ - [101, 20]
1887
+ - [102, 20]
1888
+ - [103, 20]
1889
+ - [104, 20]
1890
+ - [105, 20]
1891
+ - [106, 20]
1892
+ - [107, 20]
1893
+ - [108, 20]
1894
+ - [109, 20]
1895
+ - [110, 20]
1896
+ - [111, 20]
1897
+ - [112, 20]
1898
+ - [113, 20]
1899
+ - [114, 20]
1900
+ - [115, 20]
1901
+ - [116, 20]
1902
+ - [117, 20]
1903
+ - [118, 20]
1904
+ - [119, 20]
1905
+ - [120, 20]
1906
+ - [121, 20]
1907
+ - [122, 20]
1908
+ - [123, 20]
1909
+ - [124, 20]
1910
+ - [125, 20]
1911
+ - [126, 20]
1912
+ - [127, 20]
1913
+ - [0, 21]
1914
+ - [1, 21]
1915
+ - [2, 21]
1916
+ - [3, 21]
1917
+ - [4, 21]
1918
+ - [5, 21]
1919
+ - [6, 21]
1920
+ - [7, 21]
1921
+ - [8, 21]
1922
+ - [9, 21]
1923
+ - [10, 21]
1924
+ - [11, 21]
1925
+ - [12, 21]
1926
+ - [13, 21]
1927
+ - [14, 21]
1928
+ - [15, 21]
1929
+ - [16, 21]
1930
+ - [17, 21]
1931
+ - [18, 21]
1932
+ - [19, 21]
1933
+ - [20, 21]
1934
+ - [21, 21]
1935
+ - [22, 21]
1936
+ - [23, 21]
1937
+ - [24, 21]
1938
+ - [25, 21]
1939
+ - [26, 21]
1940
+ - [27, 21]
1941
+ - [32, 21]
1942
+ - [33, 21]
1943
+ - [34, 21]
1944
+ - [35, 21]
1945
+ - [36, 21]
1946
+ - [37, 21]
1947
+ - [38, 21]
1948
+ - [39, 21]
1949
+ - [40, 21]
1950
+ - [41, 21]
1951
+ - [42, 21]
1952
+ - [43, 21]
1953
+ - [44, 21]
1954
+ - [45, 21]
1955
+ - [46, 21]
1956
+ - [47, 21]
1957
+ - [48, 21]
1958
+ - [49, 21]
1959
+ - [50, 21]
1960
+ - [51, 21]
1961
+ - [52, 21]
1962
+ - [53, 21]
1963
+ - [54, 21]
1964
+ - [55, 21]
1965
+ - [56, 21]
1966
+ - [57, 21]
1967
+ - [58, 21]
1968
+ - [59, 21]
1969
+ - [64, 21]
1970
+ - [65, 21]
1971
+ - [66, 21]
1972
+ - [67, 21]
1973
+ - [68, 21]
1974
+ - [69, 21]
1975
+ - [70, 21]
1976
+ - [71, 21]
1977
+ - [72, 21]
1978
+ - [73, 21]
1979
+ - [74, 21]
1980
+ - [75, 21]
1981
+ - [76, 21]
1982
+ - [77, 21]
1983
+ - [78, 21]
1984
+ - [79, 21]
1985
+ - [80, 21]
1986
+ - [81, 21]
1987
+ - [82, 21]
1988
+ - [83, 21]
1989
+ - [84, 21]
1990
+ - [85, 21]
1991
+ - [86, 21]
1992
+ - [87, 21]
1993
+ - [88, 21]
1994
+ - [89, 21]
1995
+ - [90, 21]
1996
+ - [91, 21]
1997
+ - [96, 21]
1998
+ - [97, 21]
1999
+ - [98, 21]
2000
+ - [99, 21]
2001
+ - [100, 21]
2002
+ - [101, 21]
2003
+ - [102, 21]
2004
+ - [103, 21]
2005
+ - [104, 21]
2006
+ - [105, 21]
2007
+ - [106, 21]
2008
+ - [107, 21]
2009
+ - [108, 21]
2010
+ - [109, 21]
2011
+ - [110, 21]
2012
+ - [111, 21]
2013
+ - [112, 21]
2014
+ - [113, 21]
2015
+ - [114, 21]
2016
+ - [115, 21]
2017
+ - [116, 21]
2018
+ - [117, 21]
2019
+ - [118, 21]
2020
+ - [119, 21]
2021
+ - [120, 21]
2022
+ - [121, 21]
2023
+ - [122, 21]
2024
+ - [123, 21]
2025
+ - [124, 21]
2026
+ - [125, 21]
2027
+ - [126, 21]
2028
+ - [127, 21]
2029
+ win_condition:
2030
+ all_of:
2031
+ - units_killed_gte: 10
2032
+ - has_building: fact
2033
+ - within_ticks: 3600
2034
+ - building_count_gte:
2035
+ type: proc
2036
+ n: 3
2037
+ fail_condition:
2038
+ any_of:
2039
+ - after_ticks: 3601
2040
+ - not:
2041
+ has_building: fact
2042
+ - not:
2043
+ building_count_gte:
2044
+ type: proc
2045
+ n: 3
2046
+ max_turns: 45