yxc20098 commited on
Commit
30c7fcc
·
1 Parent(s): e34b586

feat(predicates): units_of_type_in_region_gte — type-filtered region count

Browse files

Wave-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 = ("reach_region", "units_in_region_gte", "all_units_in_region")
 
 
 
 
 
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},