Spaces:
Running
Defensive fix: strip agent.cash=0 when starting_cash>0 in tmp YAML
Browse filesRoot cause: openra_rl_training.scenario.PlayerSetup.cash defaults to
int=0 (not Optional). Any pack with 'agent: {faction: ...}' (no
explicit cash:) serializes as 'agent: {faction: ..., cash: 0}' —
which under the per-player cash plumbing engine fix (commit a5014a5)
silently overrides the top-level 'starting_cash: N' with 0,
breaking the production queue (consumed=0, items.len grows,
PLACE BLOCKED). This was the root cause of the original P0
regression that broke test_parallel_production, test_pbox_fires,
test_repair_building_id (commit 859aa77 patched the symptoms per-
test; this commit prevents the entire class for ALL packs).
Bench-side defensive fix in _scenario_to_tmp_yaml: when
agent.cash / enemy.cash == 0 AND starting_cash > 0, STRIP the
per-player cash field so the engine falls back to the top-level
starting_cash. Doesn't change behavior for packs that genuinely
declare cash=0 alongside starting_cash=0 (no fallback to inherit).
Audited via 62 packs that previously serialized 'agent: {cash: 0}'
under non-zero starting_cash — all now correctly serialize without
the spurious cash key.
Tests: 103/103 of the engine-feature + production-related smoke
set pass post-fix (was 103/103 also, but had needed pack-side
workarounds in test_parallel_production / test_pbox_fires /
test_repair_building_id and the mfb-mirror / mfb-two-base packs).
Those workarounds can stay in place — they're explicit and don't
regress under this fix.
- openra_bench/eval_core.py +17 -0
|
@@ -55,6 +55,23 @@ def _scenario_to_tmp_yaml(compiled: CompiledLevel) -> str:
|
|
| 55 |
# Rust scenario parser reads (default 5000 when unset).
|
| 56 |
if compiled.starting_cash is not None:
|
| 57 |
data["starting_cash"] = compiled.starting_cash
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
# The Rust engine defaults spawn_mcvs:true → it auto-seeds MCVs at
|
| 59 |
# the map's built-in spawn points (e.g. (124,36)), which reveal fog
|
| 60 |
# and pollute unit counts for scenarios that never asked for them.
|
|
|
|
| 55 |
# Rust scenario parser reads (default 5000 when unset).
|
| 56 |
if compiled.starting_cash is not None:
|
| 57 |
data["starting_cash"] = compiled.starting_cash
|
| 58 |
+
# Per-player cash plumbing footgun: `PlayerSetup.cash` defaults to
|
| 59 |
+
# `int = 0` (not Optional), so an unset `agent: {faction: ...}` in
|
| 60 |
+
# the pack serializes as `{faction: ..., cash: 0}`. With the
|
| 61 |
+
# engine's per-player-cash fix landed, that 0 silently OVERRIDES
|
| 62 |
+
# the top-level `starting_cash:` — production stalls because the
|
| 63 |
+
# agent has no money to consume. Defensive bench-side fix: if
|
| 64 |
+
# `agent.cash` / `enemy.cash` is 0 AND `starting_cash` > 0, STRIP
|
| 65 |
+
# the per-player cash field so the engine falls back to the
|
| 66 |
+
# top-level. (A pack that genuinely wants cash=0 should also set
|
| 67 |
+
# `starting_cash: 0`, in which case there's nothing to fall back
|
| 68 |
+
# to and the behavior is unchanged.)
|
| 69 |
+
_top_cash = int(data.get("starting_cash") or 0)
|
| 70 |
+
for _side in ("agent", "enemy"):
|
| 71 |
+
_block = data.get(_side)
|
| 72 |
+
if isinstance(_block, dict) and _block.get("cash", None) == 0 and _top_cash > 0:
|
| 73 |
+
_block.pop("cash", None)
|
| 74 |
+
data[_side] = _block
|
| 75 |
# The Rust engine defaults spawn_mcvs:true → it auto-seeds MCVs at
|
| 76 |
# the map's built-in spawn points (e.g. (124,36)), which reveal fog
|
| 77 |
# and pollute unit counts for scenarios that never asked for them.
|