Spaces:
Running
Running
feat(predicates): units_of_type_in_region_gte — type-filtered region count
Browse filesWave-5 coord-squad-handoff and friends need to enforce SQUAD IDENTITY
at a waypoint ('Squad A — 3 jeeps — at P1; Squad B — 3 tanks — at
P2') so a single-squad tour cannot satisfy two type-distinct clauses
in series. Pair with then: composite for ordered squad handoff.
Form: {type, x, y, radius, n}
Phrase translation + test coverage included. Confined to bench-side
(no Rust changes needed).
Author: a Wave-5 worktree agent (untracked when their push was
blocked; salvaged and committed from the main session).
openra_bench/game_knowledge.py
CHANGED
|
@@ -142,7 +142,12 @@ def scenario_primer(compiled: Any) -> str:
|
|
| 142 |
|
| 143 |
# ── win/fail predicate → plain language ────────────────────────────────────
|
| 144 |
|
| 145 |
-
_REGION_KEYS = (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
|
| 147 |
|
| 148 |
def _region(x: Any, coords: str = "exact") -> str:
|
|
@@ -176,6 +181,11 @@ _PHRASES: dict[str, Any] = {
|
|
| 176 |
f"get ≥{(v if isinstance(v, dict) else {}).get('n', 1)} "
|
| 177 |
f"units into {_region(v)}"
|
| 178 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
"all_units_in_region": lambda v: f"get EVERY unit into {_region(v)}",
|
| 180 |
"own_units_gte": lambda v: f"keep ≥{v} units alive",
|
| 181 |
"cash_gte": lambda v: f"hold ≥{v} credits",
|
|
@@ -216,6 +226,11 @@ _REGION_PHRASES: dict[str, Any] = {
|
|
| 216 |
f"get ≥{(v if isinstance(v, dict) else {}).get('n', 1)} "
|
| 217 |
f"units into {_region(v, c)}"
|
| 218 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
"all_units_in_region": lambda v, c: f"get EVERY unit into {_region(v, c)}",
|
| 220 |
"waypoint_sequence": lambda v, c: (
|
| 221 |
"reach these waypoints IN ORDER (no skipping, no idling): "
|
|
|
|
| 142 |
|
| 143 |
# ── win/fail predicate → plain language ────────────────────────────────────
|
| 144 |
|
| 145 |
+
_REGION_KEYS = (
|
| 146 |
+
"reach_region",
|
| 147 |
+
"units_in_region_gte",
|
| 148 |
+
"units_of_type_in_region_gte",
|
| 149 |
+
"all_units_in_region",
|
| 150 |
+
)
|
| 151 |
|
| 152 |
|
| 153 |
def _region(x: Any, coords: str = "exact") -> str:
|
|
|
|
| 181 |
f"get ≥{(v if isinstance(v, dict) else {}).get('n', 1)} "
|
| 182 |
f"units into {_region(v)}"
|
| 183 |
),
|
| 184 |
+
"units_of_type_in_region_gte": lambda v: (
|
| 185 |
+
f"get ≥{(v if isinstance(v, dict) else {}).get('n', 1)} "
|
| 186 |
+
f"'{(v if isinstance(v, dict) else {}).get('type')}' "
|
| 187 |
+
f"units into {_region(v)}"
|
| 188 |
+
),
|
| 189 |
"all_units_in_region": lambda v: f"get EVERY unit into {_region(v)}",
|
| 190 |
"own_units_gte": lambda v: f"keep ≥{v} units alive",
|
| 191 |
"cash_gte": lambda v: f"hold ≥{v} credits",
|
|
|
|
| 226 |
f"get ≥{(v if isinstance(v, dict) else {}).get('n', 1)} "
|
| 227 |
f"units into {_region(v, c)}"
|
| 228 |
),
|
| 229 |
+
"units_of_type_in_region_gte": lambda v, c: (
|
| 230 |
+
f"get ≥{(v if isinstance(v, dict) else {}).get('n', 1)} "
|
| 231 |
+
f"'{(v if isinstance(v, dict) else {}).get('type')}' "
|
| 232 |
+
f"units into {_region(v, c)}"
|
| 233 |
+
),
|
| 234 |
"all_units_in_region": lambda v, c: f"get EVERY unit into {_region(v, c)}",
|
| 235 |
"waypoint_sequence": lambda v, c: (
|
| 236 |
"reach these waypoints IN ORDER (no skipping, no idling): "
|
openra_bench/scenarios/win_conditions.py
CHANGED
|
@@ -138,6 +138,18 @@ _PREDICATES: dict[str, Callable[[WinContext, Any], bool]] = {
|
|
| 138 |
_agent_units(c), int(v["x"]), int(v["y"]), float(v.get("radius", 3))
|
| 139 |
)
|
| 140 |
>= int(v.get("n", 1)),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
# Stateful ordered-route latch (see _waypoint_sequence). Lets a
|
| 142 |
# scenario require visiting W1→W2→…→Wk IN ORDER (skip/idle ⇒ never
|
| 143 |
# satisfied), which stateless region predicates cannot express.
|
|
|
|
| 138 |
_agent_units(c), int(v["x"]), int(v["y"]), float(v.get("radius", 3))
|
| 139 |
)
|
| 140 |
>= int(v.get("n", 1)),
|
| 141 |
+
# Type-filtered region count: ≥ n agent units of a given type within
|
| 142 |
+
# radius of (x,y). Lets a scenario enforce SQUAD IDENTITY at a
|
| 143 |
+
# waypoint ("Squad A — 3 jeeps — at P1; Squad B — 3 tanks — at
|
| 144 |
+
# P2") so a single-squad tour cannot satisfy two type-distinct
|
| 145 |
+
# clauses in series. Pair with `then:` for ordered squad handoff.
|
| 146 |
+
# {type, x, y, radius, n}
|
| 147 |
+
"units_of_type_in_region_gte": lambda c, v: sum(
|
| 148 |
+
1 for u in _agent_units(c)
|
| 149 |
+
if str(u.get("type", "")).lower() == str(v["type"]).lower()
|
| 150 |
+
and (u["cell_x"] - int(v["x"])) ** 2 + (u["cell_y"] - int(v["y"])) ** 2
|
| 151 |
+
<= float(v.get("radius", 3)) ** 2
|
| 152 |
+
) >= int(v.get("n", 1)),
|
| 153 |
# Stateful ordered-route latch (see _waypoint_sequence). Lets a
|
| 154 |
# scenario require visiting W1→W2→…→Wk IN ORDER (skip/idle ⇒ never
|
| 155 |
# satisfied), which stateless region predicates cannot express.
|
tests/test_game_knowledge.py
CHANGED
|
@@ -100,6 +100,9 @@ def test_all_predicate_keys_have_a_translation():
|
|
| 100 |
sample = {
|
| 101 |
"reach_region": {"x": 1, "y": 2, "radius": 3},
|
| 102 |
"all_units_in_region": {"x": 1, "y": 2, "radius": 3},
|
|
|
|
|
|
|
|
|
|
| 103 |
"building_count_gte": {"type": "powr", "n": 2},
|
| 104 |
"building_in_region": {"x": 1, "y": 2, "count": 1},
|
| 105 |
"unit_type_count_eq": {"type": "e1", "n": 3},
|
|
|
|
| 100 |
sample = {
|
| 101 |
"reach_region": {"x": 1, "y": 2, "radius": 3},
|
| 102 |
"all_units_in_region": {"x": 1, "y": 2, "radius": 3},
|
| 103 |
+
"units_of_type_in_region_gte": {
|
| 104 |
+
"type": "jeep", "x": 1, "y": 2, "radius": 3, "n": 3,
|
| 105 |
+
},
|
| 106 |
"building_count_gte": {"type": "powr", "n": 2},
|
| 107 |
"building_in_region": {"x": 1, "y": 2, "count": 1},
|
| 108 |
"unit_type_count_eq": {"type": "e1", "n": 3},
|