Spaces:
Running
Running
fix(scenario): mid-concede-vs-hold — recalibrate after engine movement fixes
Browse files
openra_bench/scenarios/packs/mid-concede-vs-hold.yaml
CHANGED
|
@@ -14,35 +14,52 @@
|
|
| 14 |
# and EAST (78,20), each with a small tank garrison; a 5-unit flex
|
| 15 |
# squad parks at the centre (51,20). A scripted `hunt` enemy pushes
|
| 16 |
# BOTH bases simultaneously, asymmetrically — one push is light
|
| 17 |
-
# (WEST,
|
| 18 |
# push fully razes the east base even against a wrong-side
|
| 19 |
# consolidation; the LIGHT push overruns a bare WEST garrison but
|
| 20 |
-
# the WEST garrison + FULL flex
|
| 21 |
-
#
|
| 22 |
-
#
|
| 23 |
-
#
|
|
|
|
| 24 |
#
|
| 25 |
-
# RECALIBRATION (engine
|
| 26 |
-
#
|
| 27 |
-
#
|
| 28 |
-
#
|
| 29 |
-
#
|
| 30 |
-
#
|
| 31 |
-
#
|
| 32 |
-
#
|
| 33 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
#
|
| 35 |
-
# * Stall ⇒
|
| 36 |
-
#
|
|
|
|
| 37 |
# * Split-defend (half flex to each side) ⇒ each base gets too few
|
| 38 |
# reinforcements; both fall ⇒ LOSS.
|
| 39 |
# * Reinforce-both-back-and-forth ⇒ flex never lands long enough
|
| 40 |
# anywhere to make the trade; both fall ⇒ LOSS.
|
| 41 |
# * Consolidate flex on the WRONG (heavy EAST) side ⇒ the heavy
|
| 42 |
-
# push razes the east base anyway AND the un-garrisoned
|
| 43 |
-
# base falls ⇒ every refinery lost ⇒ LOSS.
|
| 44 |
-
# * Consolidate flex on the WEST (light) side
|
| 45 |
-
# base falls (conceded by design)
|
|
|
|
| 46 |
#
|
| 47 |
# Map: arena 160x60, cordon 4 (generated on demand via mapgen). The
|
| 48 |
# rush-hour-arena top-level base_map is only the support-flag sentinel
|
|
@@ -137,39 +154,49 @@ levels:
|
|
| 137 |
actors:
|
| 138 |
# ── WEST agent base ──
|
| 139 |
# fact/proc/powr at stance:2 so they don't trigger any
|
| 140 |
-
# spurious movement; the agent's mobile garrison
|
| 141 |
-
# stance:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
- {type: fact, owner: agent, position: [24, 20], stance: 2, health: 100}
|
| 143 |
- {type: proc, owner: agent, position: [24, 23], stance: 2, health: 100}
|
| 144 |
- {type: powr, owner: agent, position: [22, 20], stance: 2, health: 100}
|
| 145 |
-
- {type: 2tnk, owner: agent, position: [27, 19], stance:
|
| 146 |
-
- {type: 1tnk, owner: agent, position: [27, 21], stance:
|
| 147 |
-
- {type: e3, owner: agent, position: [26, 20], stance:
|
| 148 |
# ── EAST agent base — symmetric layout ──
|
| 149 |
- {type: fact, owner: agent, position: [78, 20], stance: 2, health: 100}
|
| 150 |
- {type: proc, owner: agent, position: [78, 23], stance: 2, health: 100}
|
| 151 |
- {type: powr, owner: agent, position: [80, 20], stance: 2, health: 100}
|
| 152 |
-
- {type: 2tnk, owner: agent, position: [75, 19], stance:
|
| 153 |
-
- {type: 1tnk, owner: agent, position: [75, 21], stance:
|
| 154 |
-
- {type: e3, owner: agent, position: [76, 20], stance:
|
| 155 |
# ── Flex squad — parked centre, must be committed to ONE side
|
| 156 |
-
- {type: 2tnk, owner: agent, position: [51, 20], stance:
|
| 157 |
-
- {type: 1tnk, owner: agent, position: [51, 22], stance:
|
| 158 |
-
- {type: jeep, owner: agent, position: [51, 18], stance:
|
| 159 |
-
# ── WEST enemy push (LIGHT) — far west edge. Sized (
|
| 160 |
-
# so the WEST garrison
|
| 161 |
-
#
|
| 162 |
-
#
|
| 163 |
-
#
|
| 164 |
-
|
| 165 |
-
#
|
| 166 |
-
- {type: e1, owner: enemy, position: [5, 20], stance: 3, count: 16, health: 100}
|
| 167 |
-
# ── EAST enemy push (HEAVY) — far east edge. Sized (36 e1)
|
| 168 |
# so it FULLY razes the east base (fact + proc) even against
|
| 169 |
-
# a wrong-side consolidation: the heavy side is genuinely
|
| 170 |
-
# unsavable. Re-tuned UP
|
| 171 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 172 |
- {type: e1, owner: enemy, position: [155, 21], stance: 3, count: 36, health: 100}
|
|
|
|
| 173 |
# Persistent unarmed enemy `fact` marker far off-map (anti
|
| 174 |
# auto-DRAW): keeps the episode alive past the survival floor
|
| 175 |
# so the building-survival win/fail evaluates instead of the
|
|
@@ -234,27 +261,30 @@ levels:
|
|
| 234 |
- {type: fact, owner: agent, position: [24, 20], stance: 2, health: 100}
|
| 235 |
- {type: proc, owner: agent, position: [24, 23], stance: 2, health: 100}
|
| 236 |
- {type: powr, owner: agent, position: [22, 20], stance: 2, health: 100}
|
| 237 |
-
- {type: 2tnk, owner: agent, position: [27, 19], stance:
|
| 238 |
-
- {type: 1tnk, owner: agent, position: [27, 21], stance:
|
| 239 |
-
- {type: e3, owner: agent, position: [26, 20], stance:
|
| 240 |
# ── EAST agent base ──
|
| 241 |
- {type: fact, owner: agent, position: [78, 20], stance: 2, health: 100}
|
| 242 |
- {type: proc, owner: agent, position: [78, 23], stance: 2, health: 100}
|
| 243 |
- {type: powr, owner: agent, position: [80, 20], stance: 2, health: 100}
|
| 244 |
-
- {type: 2tnk, owner: agent, position: [75, 19], stance:
|
| 245 |
-
- {type: 1tnk, owner: agent, position: [75, 21], stance:
|
| 246 |
-
- {type: e3, owner: agent, position: [76, 20], stance:
|
| 247 |
# ── Flex squad ──
|
| 248 |
-
- {type: 2tnk, owner: agent, position: [51, 20], stance:
|
| 249 |
-
- {type: 1tnk, owner: agent, position: [51, 22], stance:
|
| 250 |
-
- {type: jeep, owner: agent, position: [51, 18], stance:
|
| 251 |
-
# ── WEST enemy push (LIGHT) — same
|
| 252 |
-
# overruns
|
| 253 |
-
- {type: e1, owner: enemy, position: [5, 20], stance: 3, count:
|
| 254 |
-
# ── EAST enemy push (HEAVY) —
|
| 255 |
-
#
|
| 256 |
-
#
|
| 257 |
-
|
|
|
|
|
|
|
|
|
|
| 258 |
# Persistent unarmed enemy `fact` marker (anti auto-DRAW).
|
| 259 |
- {type: fact, owner: enemy, position: [150, 5]}
|
| 260 |
# Same survival band as easy; max_turns 60 → tick reach 5403 ≥ 5401.
|
|
@@ -333,36 +363,36 @@ levels:
|
|
| 333 |
- {type: fact, owner: agent, position: [24, 20], stance: 2, health: 100, spawn_point: 0}
|
| 334 |
- {type: proc, owner: agent, position: [24, 23], stance: 2, health: 100, spawn_point: 0}
|
| 335 |
- {type: powr, owner: agent, position: [22, 20], stance: 2, health: 100, spawn_point: 0}
|
| 336 |
-
- {type: 2tnk, owner: agent, position: [27, 19], stance:
|
| 337 |
-
- {type: 1tnk, owner: agent, position: [27, 21], stance:
|
| 338 |
-
- {type: e3, owner: agent, position: [26, 20], stance:
|
| 339 |
# ── WEST agent base — spawn_point 1 copy ──
|
| 340 |
- {type: fact, owner: agent, position: [24, 20], stance: 2, health: 100, spawn_point: 1}
|
| 341 |
- {type: proc, owner: agent, position: [24, 23], stance: 2, health: 100, spawn_point: 1}
|
| 342 |
- {type: powr, owner: agent, position: [22, 20], stance: 2, health: 100, spawn_point: 1}
|
| 343 |
-
- {type: 2tnk, owner: agent, position: [27, 19], stance:
|
| 344 |
-
- {type: 1tnk, owner: agent, position: [27, 21], stance:
|
| 345 |
-
- {type: e3, owner: agent, position: [26, 20], stance:
|
| 346 |
# ── EAST agent base — spawn_point 0 copy ──
|
| 347 |
- {type: fact, owner: agent, position: [78, 20], stance: 2, health: 100, spawn_point: 0}
|
| 348 |
- {type: proc, owner: agent, position: [78, 23], stance: 2, health: 100, spawn_point: 0}
|
| 349 |
- {type: powr, owner: agent, position: [80, 20], stance: 2, health: 100, spawn_point: 0}
|
| 350 |
-
- {type: 2tnk, owner: agent, position: [75, 19], stance:
|
| 351 |
-
- {type: 1tnk, owner: agent, position: [75, 21], stance:
|
| 352 |
-
- {type: e3, owner: agent, position: [76, 20], stance:
|
| 353 |
# ── EAST agent base — spawn_point 1 copy ──
|
| 354 |
- {type: fact, owner: agent, position: [78, 20], stance: 2, health: 100, spawn_point: 1}
|
| 355 |
- {type: proc, owner: agent, position: [78, 23], stance: 2, health: 100, spawn_point: 1}
|
| 356 |
- {type: powr, owner: agent, position: [80, 20], stance: 2, health: 100, spawn_point: 1}
|
| 357 |
-
- {type: 2tnk, owner: agent, position: [75, 19], stance:
|
| 358 |
-
- {type: 1tnk, owner: agent, position: [75, 21], stance:
|
| 359 |
-
- {type: e3, owner: agent, position: [76, 20], stance:
|
| 360 |
# ── Flex squad, spawn_point 0 — pre-positioned WEST of centre
|
| 361 |
# (closer to the weak-side base). The model still has to scout
|
| 362 |
# and decide; the start nudges the right policy.
|
| 363 |
-
- {type: 2tnk, owner: agent, position: [48, 20], stance:
|
| 364 |
-
- {type: 1tnk, owner: agent, position: [48, 22], stance:
|
| 365 |
-
- {type: jeep, owner: agent, position: [48, 18], stance:
|
| 366 |
# ── Flex squad, spawn_point 1 — pre-positioned EAST of centre
|
| 367 |
# (x=54), on the far side of the centre line from the weak
|
| 368 |
# WEST base: a "wrong-side" start that punishes a memorised
|
|
@@ -374,23 +404,27 @@ levels:
|
|
| 374 |
# combat rebalance made those survivors numerous enough that
|
| 375 |
# a too-distant flex arrived after the WEST base was already
|
| 376 |
# being overrun.
|
| 377 |
-
- {type: 2tnk, owner: agent, position: [54, 20], stance:
|
| 378 |
-
- {type: 1tnk, owner: agent, position: [54, 22], stance:
|
| 379 |
-
- {type: jeep, owner: agent, position: [54, 18], stance:
|
| 380 |
-
# ── WEST enemy push (LIGHT) — far west, same
|
| 381 |
# as easy/medium.
|
| 382 |
-
- {type: e1, owner: enemy, position: [5, 20], stance: 3, count:
|
| 383 |
-
# ── EAST enemy push (HEAVY) — far east,
|
| 384 |
-
#
|
| 385 |
-
#
|
| 386 |
-
#
|
| 387 |
-
#
|
| 388 |
-
#
|
| 389 |
-
#
|
| 390 |
-
#
|
| 391 |
-
#
|
| 392 |
-
#
|
| 393 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
# Persistent unarmed enemy `fact` marker (anti auto-DRAW).
|
| 395 |
# Enemy actors do not honour spawn_point, so a single marker
|
| 396 |
# (no spawn_point) places under either seed.
|
|
|
|
| 14 |
# and EAST (78,20), each with a small tank garrison; a 5-unit flex
|
| 15 |
# squad parks at the centre (51,20). A scripted `hunt` enemy pushes
|
| 16 |
# BOTH bases simultaneously, asymmetrically — one push is light
|
| 17 |
+
# (WEST, 20 rifles), one is heavy (EAST, 72 rifles). The HEAVY
|
| 18 |
# push fully razes the east base even against a wrong-side
|
| 19 |
# consolidation; the LIGHT push overruns a bare WEST garrison but
|
| 20 |
+
# the WEST garrison + FULL flex, ordered to attack-move into the
|
| 21 |
+
# push, holds it past the survival floor. The win is keeping
|
| 22 |
+
# fact+proc alive at AT LEAST ONE base in the [after_ticks,
|
| 23 |
+
# within_ticks] band; the loss is the timeout (5401) OR no complete
|
| 24 |
+
# base left (no fact OR no proc — both bases razed).
|
| 25 |
#
|
| 26 |
+
# RECALIBRATION (engine MOVEMENT fixes): two engine fixes shifted
|
| 27 |
+
# combat bench-wide — (A) `attack_unit` on an out-of-sight target
|
| 28 |
+
# now paths normally instead of teleporting; (B) a unit executing a
|
| 29 |
+
# move both FIRES and TAKES FIRE en route (no sprint-invincibility),
|
| 30 |
+
# respecting stance. The net effect here: a fully-consolidated force
|
| 31 |
+
# that ATTACK-MOVES into a push is far more effective than before,
|
| 32 |
+
# and — critically — agent units left at `stance:3` AttackAnything
|
| 33 |
+
# now auto-advance to intercept any visible enemy, so a STALL policy
|
| 34 |
+
# self-played the scenario (the flex + garrisons hunted the pushes
|
| 35 |
+
# unaided) and won for free. Re-tuned:
|
| 36 |
+
# * every agent mobile unit set to `stance:1` ReturnFire — it
|
| 37 |
+
# fires back when shot but never advances/initiates on its own,
|
| 38 |
+
# so the capability (explicitly committing the flex) is
|
| 39 |
+
# load-bearing again; the intended policy must ATTACK-MOVE.
|
| 40 |
+
# * WEST light push 16→20, EAST heavy push 36/42/34→72 (uniform).
|
| 41 |
+
# The buffed consolidated attack-move force could hold the EAST
|
| 42 |
+
# base against the old 34-42 e1 heavy push (wrong-side
|
| 43 |
+
# consolidate WON); 72 e1 razes the conceded base before the
|
| 44 |
+
# force clears it, on every level and seed.
|
| 45 |
+
# (Earlier recalibration, still in effect: `enemy_units_killed`
|
| 46 |
+
# termination dropped — the win is a survival-band check; a
|
| 47 |
+
# `not proc:1` fail clause; a persistent unarmed enemy `fact`
|
| 48 |
+
# marker (anti auto-DRAW); the hard survival floor at tick 2400.)
|
| 49 |
#
|
| 50 |
+
# * Stall ⇒ stance:1 units only return fire, never advance: the
|
| 51 |
+
# bare west garrison is overrun by the light push, the east
|
| 52 |
+
# garrison falls to the heavy push ⇒ both razed ⇒ LOSS.
|
| 53 |
# * Split-defend (half flex to each side) ⇒ each base gets too few
|
| 54 |
# reinforcements; both fall ⇒ LOSS.
|
| 55 |
# * Reinforce-both-back-and-forth ⇒ flex never lands long enough
|
| 56 |
# anywhere to make the trade; both fall ⇒ LOSS.
|
| 57 |
# * Consolidate flex on the WRONG (heavy EAST) side ⇒ the heavy
|
| 58 |
+
# 72-e1 push razes the east base anyway AND the un-garrisoned
|
| 59 |
+
# west base falls ⇒ every refinery lost ⇒ LOSS.
|
| 60 |
+
# * Consolidate flex on the WEST (light) side and ATTACK-MOVE into
|
| 61 |
+
# the push ⇒ west holds, east base falls (conceded by design)
|
| 62 |
+
# ⇒ ONE base saved ⇒ WIN.
|
| 63 |
#
|
| 64 |
# Map: arena 160x60, cordon 4 (generated on demand via mapgen). The
|
| 65 |
# rush-hour-arena top-level base_map is only the support-flag sentinel
|
|
|
|
| 154 |
actors:
|
| 155 |
# ── WEST agent base ──
|
| 156 |
# fact/proc/powr at stance:2 so they don't trigger any
|
| 157 |
+
# spurious movement; the agent's mobile garrison and the
|
| 158 |
+
# flex squad are stance:1 ReturnFire — they fire back when
|
| 159 |
+
# shot but never advance/initiate, so the agent must
|
| 160 |
+
# explicitly ATTACK-MOVE the consolidated force into the
|
| 161 |
+
# push for the concede-vs-hold capability to be load-bearing
|
| 162 |
+
# (a stall policy cannot self-play the defence).
|
| 163 |
- {type: fact, owner: agent, position: [24, 20], stance: 2, health: 100}
|
| 164 |
- {type: proc, owner: agent, position: [24, 23], stance: 2, health: 100}
|
| 165 |
- {type: powr, owner: agent, position: [22, 20], stance: 2, health: 100}
|
| 166 |
+
- {type: 2tnk, owner: agent, position: [27, 19], stance: 1, count: 2, health: 100}
|
| 167 |
+
- {type: 1tnk, owner: agent, position: [27, 21], stance: 1, count: 2, health: 100}
|
| 168 |
+
- {type: e3, owner: agent, position: [26, 20], stance: 1, count: 2, health: 100}
|
| 169 |
# ── EAST agent base — symmetric layout ──
|
| 170 |
- {type: fact, owner: agent, position: [78, 20], stance: 2, health: 100}
|
| 171 |
- {type: proc, owner: agent, position: [78, 23], stance: 2, health: 100}
|
| 172 |
- {type: powr, owner: agent, position: [80, 20], stance: 2, health: 100}
|
| 173 |
+
- {type: 2tnk, owner: agent, position: [75, 19], stance: 1, count: 2, health: 100}
|
| 174 |
+
- {type: 1tnk, owner: agent, position: [75, 21], stance: 1, count: 2, health: 100}
|
| 175 |
+
- {type: e3, owner: agent, position: [76, 20], stance: 1, count: 2, health: 100}
|
| 176 |
# ── Flex squad — parked centre, must be committed to ONE side
|
| 177 |
+
- {type: 2tnk, owner: agent, position: [51, 20], stance: 1, count: 2, health: 100}
|
| 178 |
+
- {type: 1tnk, owner: agent, position: [51, 22], stance: 1, count: 2, health: 100}
|
| 179 |
+
- {type: jeep, owner: agent, position: [51, 18], stance: 1, health: 100}
|
| 180 |
+
# ── WEST enemy push (LIGHT) — far west edge. Sized (20 e1)
|
| 181 |
+
# so the bare WEST garrison (return-fire only — see the
|
| 182 |
+
# stance note) is overrun, but the WEST garrison + the
|
| 183 |
+
# consolidated flex, ordered to ATTACK-MOVE into the push,
|
| 184 |
+
# clears it and holds the base past the survival floor.
|
| 185 |
+
- {type: e1, owner: enemy, position: [5, 20], stance: 3, count: 20, health: 100}
|
| 186 |
+
# ── EAST enemy push (HEAVY) — far east edge. Sized (72 e1)
|
|
|
|
|
|
|
| 187 |
# so it FULLY razes the east base (fact + proc) even against
|
| 188 |
+
# a wrong-side full consolidation: the heavy side is genuinely
|
| 189 |
+
# unsavable. Re-tuned UP after the engine MOVEMENT fixes
|
| 190 |
+
# (attack_unit no longer teleports; a moving unit both fires
|
| 191 |
+
# and takes fire en route) made a consolidated stance:1
|
| 192 |
+
# attack-move force strong enough to hold the EAST base
|
| 193 |
+
# against the old 34-42 e1 push — at that size a wrong-side
|
| 194 |
+
# consolidate WON, collapsing the concede-vs-hold bar. 72 e1
|
| 195 |
+
# razes the EAST base before the force can clear it.
|
| 196 |
+
# EAST push split into two stacked actor entries (36+36=72)
|
| 197 |
+
# because the scenario schema caps a single actor count at 50.
|
| 198 |
- {type: e1, owner: enemy, position: [155, 21], stance: 3, count: 36, health: 100}
|
| 199 |
+
- {type: e1, owner: enemy, position: [155, 24], stance: 3, count: 36, health: 100}
|
| 200 |
# Persistent unarmed enemy `fact` marker far off-map (anti
|
| 201 |
# auto-DRAW): keeps the episode alive past the survival floor
|
| 202 |
# so the building-survival win/fail evaluates instead of the
|
|
|
|
| 261 |
- {type: fact, owner: agent, position: [24, 20], stance: 2, health: 100}
|
| 262 |
- {type: proc, owner: agent, position: [24, 23], stance: 2, health: 100}
|
| 263 |
- {type: powr, owner: agent, position: [22, 20], stance: 2, health: 100}
|
| 264 |
+
- {type: 2tnk, owner: agent, position: [27, 19], stance: 1, count: 2, health: 100}
|
| 265 |
+
- {type: 1tnk, owner: agent, position: [27, 21], stance: 1, count: 2, health: 100}
|
| 266 |
+
- {type: e3, owner: agent, position: [26, 20], stance: 1, count: 2, health: 100}
|
| 267 |
# ── EAST agent base ──
|
| 268 |
- {type: fact, owner: agent, position: [78, 20], stance: 2, health: 100}
|
| 269 |
- {type: proc, owner: agent, position: [78, 23], stance: 2, health: 100}
|
| 270 |
- {type: powr, owner: agent, position: [80, 20], stance: 2, health: 100}
|
| 271 |
+
- {type: 2tnk, owner: agent, position: [75, 19], stance: 1, count: 2, health: 100}
|
| 272 |
+
- {type: 1tnk, owner: agent, position: [75, 21], stance: 1, count: 2, health: 100}
|
| 273 |
+
- {type: e3, owner: agent, position: [76, 20], stance: 1, count: 2, health: 100}
|
| 274 |
# ── Flex squad ──
|
| 275 |
+
- {type: 2tnk, owner: agent, position: [51, 20], stance: 1, count: 2, health: 100}
|
| 276 |
+
- {type: 1tnk, owner: agent, position: [51, 22], stance: 1, count: 2, health: 100}
|
| 277 |
+
- {type: jeep, owner: agent, position: [51, 18], stance: 1, health: 100}
|
| 278 |
+
# ── WEST enemy push (LIGHT) — same 20-rifle band as easy:
|
| 279 |
+
# overruns the bare garrison, held by the consolidated force.
|
| 280 |
+
- {type: e1, owner: enemy, position: [5, 20], stance: 3, count: 20, health: 100}
|
| 281 |
+
# ── EAST enemy push (HEAVY) — same 72-rifle unsavable band
|
| 282 |
+
# as easy: a wrong-side full consolidation still loses the
|
| 283 |
+
# EAST base (and the un-garrisoned WEST base falls too).
|
| 284 |
+
# EAST push split into two stacked actor entries (36+36=72)
|
| 285 |
+
# because the scenario schema caps a single actor count at 50.
|
| 286 |
+
- {type: e1, owner: enemy, position: [155, 21], stance: 3, count: 36, health: 100}
|
| 287 |
+
- {type: e1, owner: enemy, position: [155, 24], stance: 3, count: 36, health: 100}
|
| 288 |
# Persistent unarmed enemy `fact` marker (anti auto-DRAW).
|
| 289 |
- {type: fact, owner: enemy, position: [150, 5]}
|
| 290 |
# Same survival band as easy; max_turns 60 → tick reach 5403 ≥ 5401.
|
|
|
|
| 363 |
- {type: fact, owner: agent, position: [24, 20], stance: 2, health: 100, spawn_point: 0}
|
| 364 |
- {type: proc, owner: agent, position: [24, 23], stance: 2, health: 100, spawn_point: 0}
|
| 365 |
- {type: powr, owner: agent, position: [22, 20], stance: 2, health: 100, spawn_point: 0}
|
| 366 |
+
- {type: 2tnk, owner: agent, position: [27, 19], stance: 1, count: 2, health: 100, spawn_point: 0}
|
| 367 |
+
- {type: 1tnk, owner: agent, position: [27, 21], stance: 1, count: 2, health: 100, spawn_point: 0}
|
| 368 |
+
- {type: e3, owner: agent, position: [26, 20], stance: 1, count: 2, health: 100, spawn_point: 0}
|
| 369 |
# ── WEST agent base — spawn_point 1 copy ──
|
| 370 |
- {type: fact, owner: agent, position: [24, 20], stance: 2, health: 100, spawn_point: 1}
|
| 371 |
- {type: proc, owner: agent, position: [24, 23], stance: 2, health: 100, spawn_point: 1}
|
| 372 |
- {type: powr, owner: agent, position: [22, 20], stance: 2, health: 100, spawn_point: 1}
|
| 373 |
+
- {type: 2tnk, owner: agent, position: [27, 19], stance: 1, count: 2, health: 100, spawn_point: 1}
|
| 374 |
+
- {type: 1tnk, owner: agent, position: [27, 21], stance: 1, count: 2, health: 100, spawn_point: 1}
|
| 375 |
+
- {type: e3, owner: agent, position: [26, 20], stance: 1, count: 2, health: 100, spawn_point: 1}
|
| 376 |
# ── EAST agent base — spawn_point 0 copy ──
|
| 377 |
- {type: fact, owner: agent, position: [78, 20], stance: 2, health: 100, spawn_point: 0}
|
| 378 |
- {type: proc, owner: agent, position: [78, 23], stance: 2, health: 100, spawn_point: 0}
|
| 379 |
- {type: powr, owner: agent, position: [80, 20], stance: 2, health: 100, spawn_point: 0}
|
| 380 |
+
- {type: 2tnk, owner: agent, position: [75, 19], stance: 1, count: 2, health: 100, spawn_point: 0}
|
| 381 |
+
- {type: 1tnk, owner: agent, position: [75, 21], stance: 1, count: 2, health: 100, spawn_point: 0}
|
| 382 |
+
- {type: e3, owner: agent, position: [76, 20], stance: 1, count: 2, health: 100, spawn_point: 0}
|
| 383 |
# ── EAST agent base — spawn_point 1 copy ──
|
| 384 |
- {type: fact, owner: agent, position: [78, 20], stance: 2, health: 100, spawn_point: 1}
|
| 385 |
- {type: proc, owner: agent, position: [78, 23], stance: 2, health: 100, spawn_point: 1}
|
| 386 |
- {type: powr, owner: agent, position: [80, 20], stance: 2, health: 100, spawn_point: 1}
|
| 387 |
+
- {type: 2tnk, owner: agent, position: [75, 19], stance: 1, count: 2, health: 100, spawn_point: 1}
|
| 388 |
+
- {type: 1tnk, owner: agent, position: [75, 21], stance: 1, count: 2, health: 100, spawn_point: 1}
|
| 389 |
+
- {type: e3, owner: agent, position: [76, 20], stance: 1, count: 2, health: 100, spawn_point: 1}
|
| 390 |
# ── Flex squad, spawn_point 0 — pre-positioned WEST of centre
|
| 391 |
# (closer to the weak-side base). The model still has to scout
|
| 392 |
# and decide; the start nudges the right policy.
|
| 393 |
+
- {type: 2tnk, owner: agent, position: [48, 20], stance: 1, count: 2, health: 100, spawn_point: 0}
|
| 394 |
+
- {type: 1tnk, owner: agent, position: [48, 22], stance: 1, count: 2, health: 100, spawn_point: 0}
|
| 395 |
+
- {type: jeep, owner: agent, position: [48, 18], stance: 1, health: 100, spawn_point: 0}
|
| 396 |
# ── Flex squad, spawn_point 1 — pre-positioned EAST of centre
|
| 397 |
# (x=54), on the far side of the centre line from the weak
|
| 398 |
# WEST base: a "wrong-side" start that punishes a memorised
|
|
|
|
| 404 |
# combat rebalance made those survivors numerous enough that
|
| 405 |
# a too-distant flex arrived after the WEST base was already
|
| 406 |
# being overrun.
|
| 407 |
+
- {type: 2tnk, owner: agent, position: [54, 20], stance: 1, count: 2, health: 100, spawn_point: 1}
|
| 408 |
+
- {type: 1tnk, owner: agent, position: [54, 22], stance: 1, count: 2, health: 100, spawn_point: 1}
|
| 409 |
+
- {type: jeep, owner: agent, position: [54, 18], stance: 1, health: 100, spawn_point: 1}
|
| 410 |
+
# ── WEST enemy push (LIGHT) — far west, same 20-rifle band
|
| 411 |
# as easy/medium.
|
| 412 |
+
- {type: e1, owner: enemy, position: [5, 20], stance: 3, count: 20, health: 100}
|
| 413 |
+
# ── EAST enemy push (HEAVY) — far east, same 72-rifle band
|
| 414 |
+
# as easy/medium. Hard's earlier survival floor (2400 vs
|
| 415 |
+
# easy/medium's 3000) is what makes the heavy push need to be
|
| 416 |
+
# this large: a wrong-side full consolidation can hold the
|
| 417 |
+
# EAST base intact past tick 2400 against a lighter push
|
| 418 |
+
# (a 34-58 e1 EAST push was still standing at tick 2400 → a
|
| 419 |
+
# wrong-side consolidate snapshotted a WIN), so the EAST push
|
| 420 |
+
# must be heavy enough to RAZE the EAST base before the
|
| 421 |
+
# 2400 floor. 72 e1 does — `cons_east` loses every refinery
|
| 422 |
+
# on every seed — while the LIGHT west push stays winnable
|
| 423 |
+
# by the consolidated attack-move force.
|
| 424 |
+
# EAST push split into two stacked actor entries (36+36=72)
|
| 425 |
+
# because the scenario schema caps a single actor count at 50.
|
| 426 |
+
- {type: e1, owner: enemy, position: [155, 21], stance: 3, count: 36, health: 100}
|
| 427 |
+
- {type: e1, owner: enemy, position: [155, 24], stance: 3, count: 36, health: 100}
|
| 428 |
# Persistent unarmed enemy `fact` marker (anti auto-DRAW).
|
| 429 |
# Enemy actors do not honour spawn_point, so a single marker
|
| 430 |
# (no spawn_point) places under either seed.
|
tests/test_mid_concede_vs_hold.py
CHANGED
|
@@ -8,20 +8,34 @@ properties (spawn_point contract for hard, fail_condition shape,
|
|
| 8 |
benchmark anchors), and that the win/fail predicate tree is in the
|
| 9 |
right band (after_ticks ≤ within_ticks ≤ reachable-tick).
|
| 10 |
|
| 11 |
-
Recalibration note
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
"""
|
| 26 |
|
| 27 |
from __future__ import annotations
|
|
@@ -135,38 +149,45 @@ def _stall(rs, C):
|
|
| 135 |
|
| 136 |
|
| 137 |
def _cons_west(rs, C):
|
| 138 |
-
"""Intended policy:
|
| 139 |
-
(the light-push side)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
u = rs.get("units_summary", []) or []
|
| 141 |
cmds = [
|
| 142 |
-
C.
|
| 143 |
for x in u
|
| 144 |
-
if x["cell_x"] > 35
|
| 145 |
]
|
| 146 |
return cmds or [C.observe()]
|
| 147 |
|
| 148 |
|
| 149 |
def _cons_east(rs, C):
|
| 150 |
-
"""Wrong-side commit: everything
|
|
|
|
|
|
|
|
|
|
| 151 |
u = rs.get("units_summary", []) or []
|
| 152 |
cmds = [
|
| 153 |
-
C.
|
| 154 |
for x in u
|
| 155 |
-
if x["cell_x"] < 65
|
| 156 |
]
|
| 157 |
return cmds or [C.observe()]
|
| 158 |
|
| 159 |
|
| 160 |
def _split_defend(rs, C):
|
| 161 |
-
"""Split-defend: half the flex squad to each base,
|
|
|
|
| 162 |
u = rs.get("units_summary", []) or []
|
| 163 |
flex = sorted(
|
| 164 |
[x for x in u if 38 <= x["cell_x"] <= 65], key=lambda x: x["id"]
|
| 165 |
)
|
| 166 |
cmds = []
|
| 167 |
for i, x in enumerate(flex):
|
| 168 |
-
tgt = (
|
| 169 |
-
cmds.append(C.
|
| 170 |
return cmds or [C.observe()]
|
| 171 |
|
| 172 |
|
|
@@ -176,24 +197,19 @@ class _PanicTC:
|
|
| 176 |
|
| 177 |
|
| 178 |
def _panic_reinforce_factory():
|
| 179 |
-
"""Reinforce-both-back-and-forth: flip the
|
| 180 |
-
(no commitment, no rest). Pure wasted
|
|
|
|
|
|
|
| 181 |
tc = _PanicTC()
|
| 182 |
|
| 183 |
def f(rs, C):
|
| 184 |
tc.n += 1
|
| 185 |
u = rs.get("units_summary", []) or []
|
| 186 |
-
|
| 187 |
-
x
|
| 188 |
-
for x in u
|
| 189 |
-
if 38 <= x["cell_x"] <= 65
|
| 190 |
-
or abs(x["cell_x"] - 26) < 5
|
| 191 |
-
or abs(x["cell_x"] - 76) < 5
|
| 192 |
-
]
|
| 193 |
-
tgt = (26, 20) if tc.n % 2 == 0 else (76, 20)
|
| 194 |
return [
|
| 195 |
-
C.
|
| 196 |
-
for x in
|
| 197 |
] or [C.observe()]
|
| 198 |
|
| 199 |
return f
|
|
|
|
| 8 |
benchmark anchors), and that the win/fail predicate tree is in the
|
| 9 |
right band (after_ticks ≤ within_ticks ≤ reachable-tick).
|
| 10 |
|
| 11 |
+
Recalibration note (engine MOVEMENT fixes — `attack_unit` on an
|
| 12 |
+
out-of-sight target paths normally instead of teleporting, and a
|
| 13 |
+
moving unit both fires and takes fire en route, respecting stance):
|
| 14 |
+
the fixes shifted combat bench-wide and broke this pack's bar two
|
| 15 |
+
ways. (1) Agent units left at `stance:3` AttackAnything now
|
| 16 |
+
auto-advance to intercept any visible enemy — so a STALL policy
|
| 17 |
+
self-played the defence (the flex squad and both garrisons hunted
|
| 18 |
+
the pushes unaided) and won for free. (2) A fully-consolidated
|
| 19 |
+
force that attack-moves into a push is now strong enough to hold
|
| 20 |
+
EITHER base — so a wrong-side consolidation onto the (formerly
|
| 21 |
+
unsavable) heavy EAST push WON, collapsing the concede-vs-hold
|
| 22 |
+
discrimination.
|
| 23 |
+
|
| 24 |
+
The pack was re-tuned: every agent mobile unit set to `stance:1`
|
| 25 |
+
ReturnFire (fires back when shot, never advances/initiates — so the
|
| 26 |
+
agent must EXPLICITLY attack-move the consolidated force, making the
|
| 27 |
+
capability load-bearing again); the WEST light push raised 16->20
|
| 28 |
+
and the EAST heavy push raised to 72 (split into two 36-count actor
|
| 29 |
+
entries — the schema caps a single count at 50) so the conceded
|
| 30 |
+
base is genuinely unsavable even by a full consolidation. Earlier
|
| 31 |
+
recalibration still in effect: `enemy_units_killed` termination
|
| 32 |
+
dropped (the win is a survival-band check), a `not proc:1` fail
|
| 33 |
+
clause, a persistent unarmed enemy `fact` marker (anti auto-DRAW),
|
| 34 |
+
the hard survival floor at tick 2400 and its attrition cap at 18.
|
| 35 |
+
|
| 36 |
+
The capability stays load-bearing — stall / split / oscillate /
|
| 37 |
+
wrong-side consolidate all LOSE; only consolidate-on-the-light-side
|
| 38 |
+
AND attack-move into the push WINS.
|
| 39 |
"""
|
| 40 |
|
| 41 |
from __future__ import annotations
|
|
|
|
| 149 |
|
| 150 |
|
| 151 |
def _cons_west(rs, C):
|
| 152 |
+
"""Intended policy: ATTACK-MOVE every mobile unit into the WEST
|
| 153 |
+
push (the light-push side) so the consolidated force actively
|
| 154 |
+
clears the raiders. The agent's units are stance:1 ReturnFire —
|
| 155 |
+
they neither advance nor initiate on their own, so the explicit
|
| 156 |
+
attack-move order is what makes this policy effective. A plain
|
| 157 |
+
`move_units` to a fixed rally point leaves the arrived units
|
| 158 |
+
idle and lets the rifles chip the base buildings down."""
|
| 159 |
u = rs.get("units_summary", []) or []
|
| 160 |
cmds = [
|
| 161 |
+
C.attack_move([str(x["id"])], target_x=20, target_y=20)
|
| 162 |
for x in u
|
|
|
|
| 163 |
]
|
| 164 |
return cmds or [C.observe()]
|
| 165 |
|
| 166 |
|
| 167 |
def _cons_east(rs, C):
|
| 168 |
+
"""Wrong-side commit: attack-move everything into the EAST push
|
| 169 |
+
(the heavy-push side). The 72-rifle heavy push razes the EAST
|
| 170 |
+
base before the force can clear it, and the un-garrisoned WEST
|
| 171 |
+
base falls too — every refinery is lost."""
|
| 172 |
u = rs.get("units_summary", []) or []
|
| 173 |
cmds = [
|
| 174 |
+
C.attack_move([str(x["id"])], target_x=84, target_y=20)
|
| 175 |
for x in u
|
|
|
|
| 176 |
]
|
| 177 |
return cmds or [C.observe()]
|
| 178 |
|
| 179 |
|
| 180 |
def _split_defend(rs, C):
|
| 181 |
+
"""Split-defend: half the flex squad attack-moves to each base,
|
| 182 |
+
garrisons stay. Neither base gets enough reinforcement."""
|
| 183 |
u = rs.get("units_summary", []) or []
|
| 184 |
flex = sorted(
|
| 185 |
[x for x in u if 38 <= x["cell_x"] <= 65], key=lambda x: x["id"]
|
| 186 |
)
|
| 187 |
cmds = []
|
| 188 |
for i, x in enumerate(flex):
|
| 189 |
+
tgt = (20, 20) if i % 2 == 0 else (84, 20)
|
| 190 |
+
cmds.append(C.attack_move([str(x["id"])], target_x=tgt[0], target_y=tgt[1]))
|
| 191 |
return cmds or [C.observe()]
|
| 192 |
|
| 193 |
|
|
|
|
| 197 |
|
| 198 |
|
| 199 |
def _panic_reinforce_factory():
|
| 200 |
+
"""Reinforce-both-back-and-forth: flip the attack-move target of
|
| 201 |
+
every unit every turn (no commitment, no rest). Pure wasted
|
| 202 |
+
travel — the force never settles long enough to clear either
|
| 203 |
+
push, so both bases fall."""
|
| 204 |
tc = _PanicTC()
|
| 205 |
|
| 206 |
def f(rs, C):
|
| 207 |
tc.n += 1
|
| 208 |
u = rs.get("units_summary", []) or []
|
| 209 |
+
tgt = (20, 20) if tc.n % 2 == 0 else (84, 20)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
return [
|
| 211 |
+
C.attack_move([str(x["id"])], target_x=tgt[0], target_y=tgt[1])
|
| 212 |
+
for x in u
|
| 213 |
] or [C.observe()]
|
| 214 |
|
| 215 |
return f
|