yxc20098 commited on
Commit
2e5c9a4
·
1 Parent(s): b3ebc0e

Hard-tier curation: action/perception/reasoning/strict/rush-hour + exhaustive classification

Browse files

Seed-driven spawn variation (+ fail_condition where an adversary
exists) applied to every remaining applicable pack:
- action-multiunit-coordination, action-sequenced-execution: 2 spawn
groups (same fixed regions/waypoints), + force-loss fail
- perception-frontier-reading, perception-target-vs-fog: scouts staged
from a seed-chosen corner so the fog-reading order flips by seed, + fail
- reasoning-frontier-commit: single scout, seed-chosen corner → the
commitment decision must generalise, + fail
- strict-sequence, custom-map-no-enemy: 2 spawn groups (no-adversary →
no force-loss fail, documented in _NO_ENEMY)
- rush-hour: already round-robins base spawn 0/1 by seed; added the
missing force-loss fail_condition

Consciously N/A (documented w/ reasons in NOT_APPLICABLE): economy-*/
building-and-planning/strict-production-bom (non-spatial),
reasoning-risk-route (tuned single seam), strategy-* (win-predicate
redesign needed, separate rigor item).

test_hard_tier.py: 15 packs in UPGRADED, live seed→distinct-start
integ check per pack, fail_condition check, + exhaustive
classification test (every active pack is UPGRADED or N/A). Full
suite 305 passed, 1 skipped.

openra_bench/scenarios/packs/action-multiunit-coordination.yaml CHANGED
@@ -88,10 +88,17 @@ levels:
88
  genuine parallel multi-group control can satisfy all clauses.
89
  overrides:
90
  actors:
91
- - {type: 2tnk, owner: agent, position: [5, 6], count: 3}
92
- - {type: 1tnk, owner: agent, position: [7, 10], count: 3}
93
- - {type: jeep, owner: agent, position: [5, 14], count: 2}
94
- - {type: apc, owner: agent, position: [9, 18], count: 2}
 
 
 
 
 
 
 
95
  - {type: fact, owner: enemy, position: [112, 5]}
96
  - {type: proc, owner: enemy, position: [112, 35]}
97
  - {type: powr, owner: enemy, position: [115, 20]}
@@ -107,4 +114,6 @@ levels:
107
  - {reach_region: {x: 115, y: 20, radius: 7}}
108
  - {within_ticks: 4200}
109
  - {units_lost_lte: 2}
 
 
110
  max_turns: 44
 
88
  genuine parallel multi-group control can satisfy all clauses.
89
  overrides:
90
  actors:
91
+ # spawn_point 0 — full force, northern staging
92
+ - {type: 2tnk, owner: agent, position: [5, 6], count: 3, spawn_point: 0}
93
+ - {type: 1tnk, owner: agent, position: [7, 10], count: 3, spawn_point: 0}
94
+ - {type: jeep, owner: agent, position: [5, 14], count: 2, spawn_point: 0}
95
+ - {type: apc, owner: agent, position: [9, 18], count: 2, spawn_point: 0}
96
+ # spawn_point 1 — full force, southern staging (same 3 fixed
97
+ # regions to cover; seed picks the start cluster)
98
+ - {type: 2tnk, owner: agent, position: [5, 20], count: 3, spawn_point: 1}
99
+ - {type: 1tnk, owner: agent, position: [7, 24], count: 3, spawn_point: 1}
100
+ - {type: jeep, owner: agent, position: [5, 28], count: 2, spawn_point: 1}
101
+ - {type: apc, owner: agent, position: [9, 32], count: 2, spawn_point: 1}
102
  - {type: fact, owner: enemy, position: [112, 5]}
103
  - {type: proc, owner: enemy, position: [112, 35]}
104
  - {type: powr, owner: enemy, position: [115, 20]}
 
114
  - {reach_region: {x: 115, y: 20, radius: 7}}
115
  - {within_ticks: 4200}
116
  - {units_lost_lte: 2}
117
+ fail_condition:
118
+ not: {own_units_gte: 1}
119
  max_turns: 44
openra_bench/scenarios/packs/action-sequenced-execution.yaml CHANGED
@@ -87,10 +87,17 @@ levels:
87
  leg overruns the clock or loses too many units.
88
  overrides:
89
  actors:
90
- - {type: 2tnk, owner: agent, position: [5, 8], count: 2}
91
- - {type: 1tnk, owner: agent, position: [7, 12], count: 2}
92
- - {type: jeep, owner: agent, position: [5, 16]}
93
- - {type: apc, owner: agent, position: [9, 20]}
 
 
 
 
 
 
 
94
  - {type: powr, owner: enemy, position: [45, 33]} # W1
95
  - {type: proc, owner: enemy, position: [80, 6]} # W2
96
  - {type: tsla, owner: enemy, position: [110, 30]} # W3
@@ -104,4 +111,6 @@ levels:
104
  - {after_ticks: 3400}
105
  - {within_ticks: 4400}
106
  - {units_lost_lte: 1}
 
 
107
  max_turns: 46
 
87
  leg overruns the clock or loses too many units.
88
  overrides:
89
  actors:
90
+ # spawn_point 0 — northern staging
91
+ - {type: 2tnk, owner: agent, position: [5, 8], count: 2, spawn_point: 0}
92
+ - {type: 1tnk, owner: agent, position: [7, 12], count: 2, spawn_point: 0}
93
+ - {type: jeep, owner: agent, position: [5, 16], spawn_point: 0}
94
+ - {type: apc, owner: agent, position: [9, 20], spawn_point: 0}
95
+ # spawn_point 1 — southern staging (same serpentine to the same
96
+ # fixed waypoints/final; seed picks the start)
97
+ - {type: 2tnk, owner: agent, position: [5, 22], count: 2, spawn_point: 1}
98
+ - {type: 1tnk, owner: agent, position: [7, 26], count: 2, spawn_point: 1}
99
+ - {type: jeep, owner: agent, position: [5, 30], spawn_point: 1}
100
+ - {type: apc, owner: agent, position: [9, 34], spawn_point: 1}
101
  - {type: powr, owner: enemy, position: [45, 33]} # W1
102
  - {type: proc, owner: enemy, position: [80, 6]} # W2
103
  - {type: tsla, owner: enemy, position: [110, 30]} # W3
 
111
  - {after_ticks: 3400}
112
  - {within_ticks: 4400}
113
  - {units_lost_lte: 1}
114
+ fail_condition:
115
+ not: {own_units_gte: 1}
116
  max_turns: 46
openra_bench/scenarios/packs/custom-map-no-enemy.yaml CHANGED
@@ -63,7 +63,16 @@ levels:
63
  hard:
64
  description: >
65
  Whole squad must reach the far corner of the playable bounds
66
- together within a short deadline.
 
 
 
 
 
 
 
 
 
67
  win_condition:
68
  all_of:
69
  - all_units_in_region: {x: 70, y: 24, radius: 6}
 
63
  hard:
64
  description: >
65
  Whole squad must reach the far corner of the playable bounds
66
+ together within a short deadline, starting from a seed-chosen
67
+ position (two spawn_point groups) so the route can't be replayed
68
+ from memory. (No adversary on this map → no force-loss fail; the
69
+ only failure mode is missing the deadline.)
70
+ overrides:
71
+ actors:
72
+ - {type: jeep, owner: agent, position: [8, 12], spawn_point: 0}
73
+ - {type: e1, owner: agent, position: [9, 14], count: 2, spawn_point: 0}
74
+ - {type: jeep, owner: agent, position: [8, 28], spawn_point: 1}
75
+ - {type: e1, owner: agent, position: [9, 26], count: 2, spawn_point: 1}
76
  win_condition:
77
  all_of:
78
  - all_units_in_region: {x: 70, y: 24, radius: 6}
openra_bench/scenarios/packs/perception-frontier-reading.yaml CHANGED
@@ -83,8 +83,13 @@ levels:
83
  reading of all three fog pockets clears the coverage bar in time.
84
  overrides:
85
  actors:
86
- - {type: jeep, owner: agent, position: [5, 33], count: 3}
87
- - {type: jeep, owner: agent, position: [7, 30], count: 2}
 
 
 
 
 
88
  # Center decoys (already-explored zone) — combat bait.
89
  - {type: e1, owner: enemy, position: [55, 20], stance: 2, count: 3}
90
  - {type: e3, owner: enemy, position: [58, 22], stance: 2}
@@ -98,4 +103,6 @@ levels:
98
  - {explored_pct_gte: 62}
99
  - {within_ticks: 4800}
100
  - {units_lost_lte: 1}
 
 
101
  max_turns: 40
 
83
  reading of all three fog pockets clears the coverage bar in time.
84
  overrides:
85
  actors:
86
+ # spawn_point 0 — scouts staged SW
87
+ - {type: jeep, owner: agent, position: [5, 33], count: 3, spawn_point: 0}
88
+ - {type: jeep, owner: agent, position: [7, 30], count: 2, spawn_point: 0}
89
+ # spawn_point 1 — scouts staged NW (the three fog pockets are
90
+ # fixed, so the reading order that works flips by seed)
91
+ - {type: jeep, owner: agent, position: [5, 6], count: 3, spawn_point: 1}
92
+ - {type: jeep, owner: agent, position: [7, 9], count: 2, spawn_point: 1}
93
  # Center decoys (already-explored zone) — combat bait.
94
  - {type: e1, owner: enemy, position: [55, 20], stance: 2, count: 3}
95
  - {type: e3, owner: enemy, position: [58, 22], stance: 2}
 
103
  - {explored_pct_gte: 62}
104
  - {within_ticks: 4800}
105
  - {units_lost_lte: 1}
106
+ fail_condition:
107
+ not: {own_units_gte: 1}
108
  max_turns: 40
openra_bench/scenarios/packs/perception-target-vs-fog.yaml CHANGED
@@ -91,8 +91,13 @@ levels:
91
  — after the near ones are ruled out — discovers it in time.
92
  overrides:
93
  actors:
94
- - {type: jeep, owner: agent, position: [6, 5], count: 3}
95
- - {type: 1tnk, owner: agent, position: [8, 8], count: 2}
 
 
 
 
 
96
  # Decoy A — near NE, loud enemy squad, no target building.
97
  - {type: e1, owner: enemy, position: [60, 6], stance: 2, count: 3}
98
  - {type: dog, owner: enemy, position: [64, 9], stance: 2, count: 2}
@@ -108,4 +113,6 @@ levels:
108
  - {buildings_discovered_gte: 1}
109
  - {within_ticks: 4800}
110
  - {units_lost_lte: 1}
 
 
111
  max_turns: 40
 
91
  — after the near ones are ruled out — discovers it in time.
92
  overrides:
93
  actors:
94
+ # spawn_point 0 — staged NW
95
+ - {type: jeep, owner: agent, position: [6, 5], count: 3, spawn_point: 0}
96
+ - {type: 1tnk, owner: agent, position: [8, 8], count: 2, spawn_point: 0}
97
+ # spawn_point 1 — staged SW (target/decoys fixed, so which
98
+ # pocket is "counter-intuitive" flips by seed)
99
+ - {type: jeep, owner: agent, position: [6, 33], count: 3, spawn_point: 1}
100
+ - {type: 1tnk, owner: agent, position: [8, 30], count: 2, spawn_point: 1}
101
  # Decoy A — near NE, loud enemy squad, no target building.
102
  - {type: e1, owner: enemy, position: [60, 6], stance: 2, count: 3}
103
  - {type: dog, owner: enemy, position: [64, 9], stance: 2, count: 2}
 
113
  - {buildings_discovered_gte: 1}
114
  - {within_ticks: 4800}
115
  - {units_lost_lte: 1}
116
+ fail_condition:
117
+ not: {own_units_gte: 1}
118
  max_turns: 40
openra_bench/scenarios/packs/reasoning-frontier-commit.yaml CHANGED
@@ -94,7 +94,10 @@ levels:
94
  time without splitting (only one unit).
95
  overrides:
96
  actors:
97
- - {type: jeep, owner: agent, position: [6, 6], count: 1}
 
 
 
98
  # Decoy A (SE) — hostile cluster; a route through it risks the
99
  # lone scout (units_lost_lte: 0 constraint below).
100
  - {type: e3, owner: enemy, position: [110, 34], stance: 2, count: 3}
@@ -108,4 +111,6 @@ levels:
108
  - {buildings_discovered_gte: 1}
109
  - {within_ticks: 4200}
110
  - {units_lost_lte: 0}
 
 
111
  max_turns: 38
 
94
  time without splitting (only one unit).
95
  overrides:
96
  actors:
97
+ # Single scout, seed-chosen corner — the commitment decision
98
+ # must generalise across start positions, not be memorised.
99
+ - {type: jeep, owner: agent, position: [6, 6], count: 1, spawn_point: 0}
100
+ - {type: jeep, owner: agent, position: [6, 33], count: 1, spawn_point: 1}
101
  # Decoy A (SE) — hostile cluster; a route through it risks the
102
  # lone scout (units_lost_lte: 0 constraint below).
103
  - {type: e3, owner: enemy, position: [110, 34], stance: 2, count: 3}
 
111
  - {buildings_discovered_gte: 1}
112
  - {within_ticks: 4200}
113
  - {units_lost_lte: 0}
114
+ fail_condition:
115
+ not: {own_units_gte: 1}
116
  max_turns: 38
openra_bench/scenarios/packs/rush-hour.yaml CHANGED
@@ -523,9 +523,15 @@ levels:
523
  - units_killed_gte: 6
524
  - within_ticks: 13000
525
  hard:
526
- description: "Rush Hour Sweep \u2014 hard difficulty (ported)."
 
 
 
 
527
  win_condition:
528
  all_of:
529
  - units_killed_gte: 9
530
  - within_ticks: 10000
531
  - units_lost_lte: 3
 
 
 
523
  - units_killed_gte: 6
524
  - within_ticks: 13000
525
  hard:
526
+ description: >
527
+ Rush Hour Sweep \u2014 hard. The agent force already round-robins
528
+ between two base spawn_point groups by seed (start corner varies,
529
+ anti-memorisation); hard adds a tight clock, an attrition cap,
530
+ and a real loss on force destruction.
531
  win_condition:
532
  all_of:
533
  - units_killed_gte: 9
534
  - within_ticks: 10000
535
  - units_lost_lte: 3
536
+ fail_condition:
537
+ not: {own_units_gte: 1}
openra_bench/scenarios/packs/strict-sequence.yaml CHANGED
@@ -58,6 +58,26 @@ levels:
58
  description: >
59
  Tight ordered window: reach the region within a narrow tick band
60
  (not before 4000, not after 9000) — precise timing compliance.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  win_condition:
62
  all_of:
63
  - reach_region: {x: 26, y: 30, radius: 3}
 
58
  description: >
59
  Tight ordered window: reach the region within a narrow tick band
60
  (not before 4000, not after 9000) — precise timing compliance.
61
+ The unit stages from a seed-chosen position (two spawn_point
62
+ groups) so the exact timed move can't be replayed from memory.
63
+ overrides:
64
+ actors:
65
+ - type: jeep
66
+ owner: agent
67
+ position: [6, 8]
68
+ stance: 0
69
+ health: 100
70
+ facing: -1
71
+ count: 1
72
+ spawn_point: 0
73
+ - type: jeep
74
+ owner: agent
75
+ position: [6, 30]
76
+ stance: 0
77
+ health: 100
78
+ facing: -1
79
+ count: 1
80
+ spawn_point: 1
81
  win_condition:
82
  all_of:
83
  - reach_region: {x: 26, y: 30, radius: 3}
tests/test_hard_tier.py CHANGED
@@ -31,8 +31,37 @@ UPGRADED = [
31
  "artofwar-indirect-approach",
32
  "artofwar-lure-the-tiger",
33
  "artofwar-sequenced-citadel",
 
 
 
 
 
 
 
 
34
  ]
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  def _agent_spawn_points(pack_id: str, level: str) -> set:
38
  c = compile_level(load_pack(PACKS / f"{pack_id}.yaml"), level)
@@ -89,7 +118,31 @@ def test_curated_hard_still_compiles_and_runs(pid):
89
 
90
  def test_fail_condition_present_on_curated_hard():
91
  # Curated hard tiers must be able to emit a loss (no loss==draw
92
- # degeneracy on the packs we've touched).
 
93
  for pid in UPGRADED:
94
  c = compile_level(load_pack(PACKS / f"{pid}.yaml"), "hard")
 
 
95
  assert c.fail_condition is not None, f"{pid}:hard needs a fail_condition"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  "artofwar-indirect-approach",
32
  "artofwar-lure-the-tiger",
33
  "artofwar-sequenced-citadel",
34
+ "action-multiunit-coordination",
35
+ "action-sequenced-execution",
36
+ "strict-sequence",
37
+ "perception-frontier-reading",
38
+ "perception-target-vs-fog",
39
+ "reasoning-frontier-commit",
40
+ "rush-hour",
41
+ "custom-map-no-enemy",
42
  ]
43
 
44
+ # Consciously NOT spawn-varied, with the reason (keeps the curation
45
+ # exhaustive — every active pack is classified, see the coverage test).
46
+ NOT_APPLICABLE = {
47
+ "economy-investment": "non-spatial: capital allocation, start pos irrelevant",
48
+ "economy-time-box": "non-spatial: budget-under-clock",
49
+ "economy-force-buildup": "non-spatial: production economy",
50
+ "building-and-planning": "non-spatial: build-order/tech, fixed base",
51
+ "strict-production-bom": "non-spatial: exact bill-of-materials spec",
52
+ "reasoning-risk-route": "rigor 5/5 from one tuned safe seam — varying "
53
+ "the start would break the single-solution tuning / seed parity",
54
+ "strategy-dilemma": "needs win-predicate redesign (gameable "
55
+ "buildings_discovered_gte:1), not spawn variation — separate rigor item",
56
+ "strategy-gauntlet": "dual-entry design + gameable win — separate item",
57
+ "strategy-twobody": "two simultaneously-controlled groups IS the task; "
58
+ "spawn-alternatives would break intent — separate item",
59
+ }
60
+
61
+ # No-adversary maps: spawn variation applies but a force-loss
62
+ # fail_condition is impossible (nothing can destroy the force).
63
+ _NO_ENEMY = {"strict-sequence", "custom-map-no-enemy"}
64
+
65
 
66
  def _agent_spawn_points(pack_id: str, level: str) -> set:
67
  c = compile_level(load_pack(PACKS / f"{pack_id}.yaml"), level)
 
118
 
119
  def test_fail_condition_present_on_curated_hard():
120
  # Curated hard tiers must be able to emit a loss (no loss==draw
121
+ # degeneracy) — except no-adversary maps where force-loss is
122
+ # impossible by construction (documented in _NO_ENEMY).
123
  for pid in UPGRADED:
124
  c = compile_level(load_pack(PACKS / f"{pid}.yaml"), "hard")
125
+ if pid in _NO_ENEMY:
126
+ continue
127
  assert c.fail_condition is not None, f"{pid}:hard needs a fail_condition"
128
+
129
+
130
+ def test_every_active_pack_is_classified():
131
+ """Curation is exhaustive: every active pack is either spawn-varied
132
+ (UPGRADED) or consciously NOT_APPLICABLE with a stated reason — no
133
+ pack silently skipped."""
134
+ import glob
135
+ import os
136
+
137
+ classified = set(UPGRADED) | set(NOT_APPLICABLE)
138
+ missing = []
139
+ for f in glob.glob(str(PACKS / "*.yaml")):
140
+ b = os.path.basename(f)
141
+ if b.startswith(("_", "TEMPLATE")):
142
+ continue
143
+ m = load_pack(f).meta
144
+ if m.status != "active":
145
+ continue
146
+ if m.id not in classified:
147
+ missing.append(m.id)
148
+ assert not missing, f"unclassified active packs (curate or mark N/A): {missing}"