Spaces:
Running
Running
fix(scenario): build-repair-priority-under-fire — add hard-tier spawn-witness
Browse filesThe recalibration dropped all agent combat units, leaving the hard
tier building-only. The hard-tier spawn-variation contract
(test_hard_tier.py) probes `units_summary` for seed-varied agent
starts and saw none. Add one inert HoldFire (stance 0) e1 spawn-witness
per spawn group beside the rear service depot — far from every
grenadier band, never fires, does not change the triage — purely so
the contract can observe the seed-varied base. All no-cheat outcomes
unchanged (stall / repair-pbox / repair-proc lose, repair-proc+weap
and repair-all win, every level+seed).
openra_bench/scenarios/packs/build-repair-priority-under-fire.yaml
CHANGED
|
@@ -286,8 +286,11 @@ levels:
|
|
| 286 |
# — the per-owner filter (oramap.rs::expand_scenario_actors)
|
| 287 |
# instantiates exactly one self-consistent base + bands per seed; the
|
| 288 |
# persistent every-seed enemy auto-`done` fact is duplicated across
|
| 289 |
-
# both groups.
|
| 290 |
-
#
|
|
|
|
|
|
|
|
|
|
| 291 |
hard:
|
| 292 |
description: >
|
| 293 |
Three structures — the refinery (proc), the war factory (weap),
|
|
@@ -317,6 +320,12 @@ levels:
|
|
| 317 |
- {type: e2, owner: enemy, position: [20, 36], stance: 3, count: 4, spawn_point: 0}
|
| 318 |
- {type: fix, owner: agent, position: [44, 20], spawn_point: 0}
|
| 319 |
- {type: fact, owner: agent, position: [48, 20], spawn_point: 0}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 320 |
- {type: fact, owner: enemy, position: [120, 20], spawn_point: 0}
|
| 321 |
# EAST base group (spawn_point 1) — the base mirrored east-
|
| 322 |
# ward into the x=70.. column with the same y-geometry, so each
|
|
@@ -330,6 +339,7 @@ levels:
|
|
| 330 |
- {type: e2, owner: enemy, position: [70, 36], stance: 3, count: 4, spawn_point: 1}
|
| 331 |
- {type: fix, owner: agent, position: [94, 20], spawn_point: 1}
|
| 332 |
- {type: fact, owner: agent, position: [98, 20], spawn_point: 1}
|
|
|
|
| 333 |
- {type: fact, owner: enemy, position: [120, 20], spawn_point: 1}
|
| 334 |
win_condition:
|
| 335 |
all_of:
|
|
|
|
| 286 |
# — the per-owner filter (oramap.rs::expand_scenario_actors)
|
| 287 |
# instantiates exactly one self-consistent base + bands per seed; the
|
| 288 |
# persistent every-seed enemy auto-`done` fact is duplicated across
|
| 289 |
+
# both groups. Each group also carries one inert HoldFire (stance 0)
|
| 290 |
+
# e1 spawn-witness beside the rear service depot so the hard-tier
|
| 291 |
+
# `units_summary` spawn-variation contract can observe a seed-varied
|
| 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),
|
|
|
|
| 320 |
- {type: e2, owner: enemy, position: [20, 36], stance: 3, count: 4, spawn_point: 0}
|
| 321 |
- {type: fix, owner: agent, position: [44, 20], spawn_point: 0}
|
| 322 |
- {type: fact, owner: agent, position: [48, 20], spawn_point: 0}
|
| 323 |
+
# Inert spawn-witness e1 (HoldFire, stance 0) tucked beside the
|
| 324 |
+
# rear service depot — far from every grenadier band. It never
|
| 325 |
+
# opens fire and does not change the triage; it only surfaces a
|
| 326 |
+
# seed-varied agent unit in `units_summary` for the hard-tier
|
| 327 |
+
# spawn-variation contract (the base is otherwise building-only).
|
| 328 |
+
- {type: e1, owner: agent, position: [44, 23], stance: 0, spawn_point: 0}
|
| 329 |
- {type: fact, owner: enemy, position: [120, 20], spawn_point: 0}
|
| 330 |
# EAST base group (spawn_point 1) — the base mirrored east-
|
| 331 |
# ward into the x=70.. column with the same y-geometry, so each
|
|
|
|
| 339 |
- {type: e2, owner: enemy, position: [70, 36], stance: 3, count: 4, spawn_point: 1}
|
| 340 |
- {type: fix, owner: agent, position: [94, 20], spawn_point: 1}
|
| 341 |
- {type: fact, owner: agent, position: [98, 20], spawn_point: 1}
|
| 342 |
+
- {type: e1, owner: agent, position: [94, 23], stance: 0, spawn_point: 1}
|
| 343 |
- {type: fact, owner: enemy, position: [120, 20], spawn_point: 1}
|
| 344 |
win_condition:
|
| 345 |
all_of:
|
tests/test_build_repair_priority_under_fire.py
CHANGED
|
@@ -148,19 +148,37 @@ def test_pbox_decoy_is_pre_damaged_every_level():
|
|
| 148 |
)
|
| 149 |
|
| 150 |
|
| 151 |
-
def
|
| 152 |
-
"""The pack carries
|
| 153 |
-
`repair`-target decision against an unopposed band.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
pack = load_pack(PACK)
|
| 155 |
buildings = {"proc", "weap", "pbox", "fix", "fact"}
|
| 156 |
for level in LEVELS:
|
| 157 |
c = compile_level(pack, level)
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
|
| 165 |
|
| 166 |
@pytest.mark.parametrize("level", LEVELS)
|
|
|
|
| 148 |
)
|
| 149 |
|
| 150 |
|
| 151 |
+
def test_no_agent_combat_units_engage():
|
| 152 |
+
"""The pack carries no engaging agent combat units — the triage is
|
| 153 |
+
a pure `repair`-target decision against an unopposed band. easy /
|
| 154 |
+
medium are building-only; the hard tier additionally carries one
|
| 155 |
+
inert HoldFire (stance 0) e1 spawn-witness per spawn group, tucked
|
| 156 |
+
beside the rear service depot far from every band, purely so the
|
| 157 |
+
hard-tier `units_summary` spawn-variation contract can observe a
|
| 158 |
+
seed-varied agent unit. The witness never opens fire and does not
|
| 159 |
+
change the triage."""
|
| 160 |
pack = load_pack(PACK)
|
| 161 |
buildings = {"proc", "weap", "pbox", "fix", "fact"}
|
| 162 |
for level in LEVELS:
|
| 163 |
c = compile_level(pack, level)
|
| 164 |
+
agent_actors = [a for a in c.scenario.actors if a.owner == "agent"]
|
| 165 |
+
agent_types = {a.type for a in agent_actors}
|
| 166 |
+
if level == "hard":
|
| 167 |
+
assert agent_types <= buildings | {"e1"}, (
|
| 168 |
+
f"hard: agent actors must be buildings + the inert e1 "
|
| 169 |
+
f"witness, got {agent_types}"
|
| 170 |
+
)
|
| 171 |
+
witnesses = [a for a in agent_actors if a.type == "e1"]
|
| 172 |
+
assert witnesses, "hard: missing inert e1 spawn-witness"
|
| 173 |
+
for w in witnesses:
|
| 174 |
+
assert w.stance == 0, (
|
| 175 |
+
f"hard: e1 witness must be HoldFire (stance 0), "
|
| 176 |
+
f"got stance {w.stance}"
|
| 177 |
+
)
|
| 178 |
+
else:
|
| 179 |
+
assert agent_types <= buildings, (
|
| 180 |
+
f"{level}: agent must own only buildings, got {agent_types}"
|
| 181 |
+
)
|
| 182 |
|
| 183 |
|
| 184 |
@pytest.mark.parametrize("level", LEVELS)
|