yxc20098 commited on
Commit
7c6d052
ยท
1 Parent(s): 78e5db2

def-surprise-flank-react: tailor map + scheduled-events surprise rush

Browse files

Replaces the generic rush-hour-arena with a custom
def-surprise-flank-react-arena (128x40 cordon 2) materialised via the
mapgen 'arena' generator. Three water bands on the north edge form a
visual 'natural chokepoint' โ€” only narrow opening x=14..26 at y=4..7
โ€” that visually corroborates the intel ('enemy from NORTH'); the
south half is wide open. The terrain is the second leg of the
misdirection so a minimap-reader sees what the brief tells them.

Rewrites the engagement to use scheduled_events: spawn_actors instead
of t=0 enemy placement, exercising the actual 'react to a reveal that
develops' capability (rather than 'observe a pre-placed enemy'). The
four pre-placed e1 defenders move from the north lane (y=14..16) to
distributed positions around the construction yard at (20,20) so
they're not pre-committed to the intel axis โ€” the model must explicitly
commit them.

Per tier:
- easy: single surprise SOUTH wave (4 e1) at tick 800.
- medium: two SOUTH waves (4 e1 at t=800, 3 e1 at t=1400) โ€” a late
commit gets the first wave but the second catches defenders
depleted.
- hard: agent base longitude varies per seed (x=20 vs x=40 via
spawn_point 0/1) and scheduled spawns inject south rushes at BOTH
x columns each wave; only the matching-longitude spawn is the
immediate threat (the other walks the full map width before
arrival, outside the 4500-tick budget). units_lost_lte:2 keeps
the late-commit margin tight.

Validation (4 policies x 3 levels x 4 seeds = 48 runs):
- intended detect-and-redeploy WINS all 12 level/seed cells.
- stall, stay_north, pure_build all LOSE all 36 cells (real
reachable LOSS, fact razed by tick 1788-2433, never DRAW).
- existing tests/test_def_surprise_flank_react.py: 19 passed.

data/maps/def-surprise-flank-react-arena.oramap ADDED
Binary file (937 Bytes). View file
 
openra_bench/scenarios/packs/def-surprise-flank-react.yaml CHANGED
@@ -1,102 +1,93 @@
1
  # def-surprise-flank-react.yaml
2
  #
3
- # REASONING capability โ€” Group C surprise-axis reaction. The model is
4
- # told (in the brief, the "intel") that the enemy will attack from the
5
- # NORTH, and the world is pre-rigged to MATCH that intel: 2 pillboxes
6
- # are already standing NORTH of the base at (20,14)/(22,14), and four
7
- # rifle defenders are already lined up NORTH at yโ‰ˆ14 next to the
8
- # pre-built pillboxes โ€” the canonical pre-positioned "prepared
9
- # defence". BUT the enemy rush actually arrives from the SOUTH, walking
10
- # around the entire NORTH pillbox line. The pre-built defenders + the
11
- # pre-built pillbox triple do NOTHING โ€” they sit in their northern
12
- # lane while the rushers stride past them on the southern lane and
13
- # raze the construction yard.
14
- #
15
- # The intended capability โ€” and the SOLE difference between WIN and
16
- # LOSS โ€” is detecting the actual attack axis from the observation
17
- # (rusher units visible to the south of the base, not the north) and
18
- # RE-POSITIONING the four pre-placed defenders SOUTH to intercept the
19
- # rush before it reaches the fact. The 2 NORTH pillboxes are dead
20
- # wood for this engagement. The brief explicitly hints "intel said
21
- # NORTH but be ready for anything" โ€” the model must NOT trust the
22
- # stated direction blindly and must commit to what it ACTUALLY
23
- # observes.
24
  #
25
  # Real-world anchors:
26
- # - Adversarial robustness on a directional axis: a stated cue is
27
- # misdirection; the agent must react to the ground-truth signal,
28
- # not the stated one.
29
  # - Opponent-modelling under deception: detect that the announced
30
  # plan and the observed plan differ; replan around the truth.
31
  # - Military reactive defence / flank discipline: a fortified flank
32
  # that the enemy has bypassed must be ABANDONED in favour of the
33
  # actual contact axis; "fighting the plan, not the enemy" is the
34
- # classical military failure mode this trains against.
35
  # - CICERO deception handling: when announced behaviour contradicts
36
- # observed behaviour, the agent should treat the announcement as
37
- # a signal to NOT trust the plan and to commit to the observation.
38
  #
39
  # DISCRIMINATIONS (the scripted bar โ€” every brute / lazy play LOSES,
40
  # only the detect-and-redeploy play WINS):
41
  #
42
- # โ€ข stall (only `observe`): defenders idle NORTH of the base; the
43
- # SOUTH rusher band marches straight at the fact (the agent
44
- # centroid is dominated by the NORTH defenders so the band
45
- # targets a north-of-fact point and PASSES THROUGH the fact on
46
- # the way). Fact razed โ†’ `not has_building:fact` LOSS.
47
  #
48
- # โ€ข stay-NORTH-with-pre-built-defence (do not move defenders): the
49
- # pre-built NORTH pillbox line is far enough north of the fact
50
- # that the south-coming rusher band reaches the fact without
51
- # ever entering pbox range. Defenders never engage because the
52
- # rush is on the OTHER side of the base. Fact razed โ†’ LOSS.
53
- # (This is the canonical "fighting the announced plan" trap.)
54
  #
55
- # โ€ข pure-build-no-react: the model uses the small starting cash to
56
- # queue (or attempt to queue) more buildings but never moves the
57
- # defenders. With no tent in the pre-placed base the infantry
58
- # queue is empty, and a single pbox at 600cr placed somewhere
59
- # near the existing NORTH line does NOT intercept the SOUTH
60
- # rush either. Same outcome as stay-NORTH โ†’ LOSS.
61
  #
62
- # โ€ข intended detect-and-redeploy SOUTH: observe (rushers are to
63
- # the south), `move_units` all four defenders south to a yโ‰ˆ26
64
- # intercept line, then `attack_move` them onto the spotted
65
- # rusher cluster. The 4 defenders trade ~4-vs-4 against the
66
- # rifle infantry rush on favourable focus-fire terms (concentrated
67
- # on the rusher centroid, no splash, e1-vs-e1 even), keep โ‰ฅ3
68
- # alive, kill โ‰ฅ4 of the rushers, fact survives โ†’ WIN.
69
  #
70
  # ENGINE FACTS used (CLAUDE.md / SCENARIO_QUALITY.md):
71
- # - The `rusher` bot drives every unit at the agent centroid every
72
- # ~8 ticks (openra-sim/src/scripted_bot.rs::Rusher). With 4
73
- # defenders NORTH at y=14 and the fact at y=20, the centroid is
74
- # near y=17 โ€” south-rusher-band targets yโ‰ˆ17 and walks straight
75
- # through y=20 (the fact) to get there โ†’ fact razed in the
76
- # stay-NORTH path. When the defenders relocate SOUTH, the
77
- # centroid drops to yโ‰ˆ22..24 and the band engages defenders
78
- # SOUTH of the fact (intercepted before fact contact).
79
- # - Enemy actors with NO spawn_point ALWAYS place regardless of the
80
- # agent's spawn group (per CLAUDE.md) โ€” so on hard we tag the
81
- # N/S agent groups and place TWO opposite-direction rusher
82
- # bands; the seed-driven agent spawn dictates which intel-axis
83
- # pair is the "surprise" one, but the bands themselves are
84
- # symmetric.
 
 
85
  # - Inert enemy `fact` marker far east (anti-DRAW): without a
86
  # persistent enemy actor the engine auto-`done`s on enemy-elim
87
  # before the win/fail evaluator runs.
88
  # - `enemy_unit_spotted` interrupt โ†’ re-decide the instant the
89
- # rusher band becomes visible (faster detection of the actual
90
- # axis).
91
  # - `own_unit_destroyed` interrupt โ†’ re-decide when a defender
92
  # falls.
93
  # - Tick budget: ~90 ticks/turn. `max_turns 50` โ†’ reachable tick
94
- # 93 + 90ยท49 = 4503 โ‰ฅ 4501 (the after_ticks fail) so a
95
- # non-winner is a real reachable timeout LOSS, never a draw.
 
 
96
  # - No tent in the pre-placed base + cash 800 means the model
97
  # CAN'T cheese the win by mass-training fresh defenders โ€” the
98
- # intended play is RE-POSITIONING the 4 starting defenders,
99
- # which is the controlled capability.
 
 
 
 
 
100
  #
101
  # Distinct from siblings (Group C):
102
  # * def-position-expected-direction โ€” intel is TRUE; commit defence
@@ -108,13 +99,12 @@
108
  # * defense-rush-survive โ€” opening rush vs sustained throughput;
109
  # no directional misdirection.
110
  # * THIS pack โ€” intel is FALSE (or just unreliable). The pre-built
111
- # defence + pre-positioned defenders are ON THE WRONG AXIS. The
112
- # model must DETECT that the announced and observed axes differ
113
- # and RE-POSITION accordingly.
114
  #
115
  # Validate (no model / no network):
116
- # python3 -m openra_bench.scenarios.validate \
117
- # openra_bench/scenarios/packs/def-surprise-flank-react.yaml
118
 
119
  meta:
120
  id: def-surprise-flank-react
@@ -122,18 +112,16 @@ meta:
122
  capability: reasoning
123
  real_world_meaning: >
124
  The mission brief states the enemy will attack from the NORTH and
125
- the world is pre-rigged accordingly: two pillboxes are already
126
- standing NORTH of the base, four rifle defenders are pre-positioned
127
- on the NORTH lane next to them โ€” the canonical prepared defence.
128
- BUT the rusher band actually arrives from the SOUTH and walks
129
- around the entire northern fortification. The decision is whether
130
- the agent trusts the announced direction blindly (and watches its
131
- construction yard get razed while its pre-built defence sits idle)
132
- or detects the actual attack axis from the observation, abandons
133
- the wrong-axis prepared defence, and re-positions its defenders
134
- SOUTH to intercept the real rush before it reaches the fact. The
135
- pre-built NORTH pillboxes do nothing for this engagement; the
136
- capability is the reactive re-positioning, not the build.
137
  robotics_analogue: >
138
  Misdirection-aware defence / military adversarial robustness on
139
  a directional axis: a fortified flank that the enemy has bypassed
@@ -153,29 +141,49 @@ meta:
153
  - 'CICERO deception handling'
154
  author: openra-bench
155
 
156
- # rush-hour-arena (128ร—40, playable xโˆˆ[2..126], yโˆˆ[2..38]). Agent base
157
- # at (20, 20): fact + 4 pre-placed e1 defenders + 2 pre-built pbox.
158
- # The 2 pbox and the 4 defenders are NORTH of the base (yโ‰ˆ14..16) โ€”
159
- # the prepared "expected" defence per the intel. The rusher band
160
- # spawns SOUTH (yโ‰ˆ32..34) and walks around the entire NORTH defence
161
- # line on its way to the fact at (20, 20).
162
- base_map: rush-hour-arena
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
  # Pack-wide budget โ€” a small float (800cr). Not enough to spam a
165
- # second pillbox line on the south, and without a `tent` the infantry
166
- # queue is empty so the cash can't be turned into fresh defenders.
167
- # The intended play is RE-POSITIONING the four pre-placed defenders;
168
- # the cash exists so the model has the OPTION to attempt a build
169
- # (which is part of the discrimination โ€” a pure-build-no-react policy
170
- # also LOSES).
171
  starting_cash: 800
172
 
173
  base:
174
  agent:
175
  faction: allies
176
  # Concentrated charge at the agent centroid every ~8 ticks โ€” the
177
- # canonical rush-defence opponent. The bot's SPAWN POSITION is the
178
- # surprise: the brief says NORTH, the bot is placed SOUTH.
 
179
  enemy:
180
  faction: soviet
181
  bot_type: rusher
@@ -188,7 +196,7 @@ base:
188
  - attack_move
189
  - stop
190
  planning: true
191
- # React the INSTANT a rusher unit is spotted (the south-axis
192
  # detection event) or a defender is lost โ€” the re-positioning
193
  # decision is reactive, not on fixed 90-tick boundaries.
194
  interrupts:
@@ -200,83 +208,74 @@ base:
200
 
201
  levels:
202
  # โ”€โ”€ EASY โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
203
- # The bare surprise-axis detect-and-react skill. One light SOUTH
204
- # rush (4 e1). The model is TOLD "intel said NORTH but be ready
205
- # for anything" in the brief โ€” i.e. the prior is unreliable.
206
- # Pre-built NORTH defence: 2 pillboxes at (20,14)/(22,14) and 4
207
- # defenders along y=14. The intended play is moving all 4
208
- # defenders south to yโ‰ˆ26 and attack-moving onto the south rush.
209
- # max_turns 50 โ†’ reachable tick 93 + 90ยท49 = 4503 โ‰ฅ 4501, so the
210
- # after_ticks fail bites comfortably before the turn cap.
211
  easy:
212
- # Original (pre-verbosity-sweep) description preserved for
213
- # contributors. The trimmed version below removes scripted-policy
214
- # spoilers and cell-coord dumps; load-bearing intent kept.
215
- #
216
- # INTEL said the enemy will attack from the NORTH โ€” but be ready for
217
- # anything. You start with a construction yard at (20, 20), two
218
- # pillboxes already standing NORTH of the base at (20, 14) and (22,
219
- # 14), and four rifle defenders lined up next to them on the NORTH
220
- # lane (y=14..16). Available cash 800. Identify the ACTUAL attack
221
- # direction from your observation, then act: destroy at least 4 enemy
222
- # units AND keep โ‰ฅ3 of your own units alive AND keep the construction
223
- # yard standing before tick 4500. Stalling, leaving the defenders on
224
- # the NORTH lane next to the pre-built pillboxes, and building extra
225
- # structures without re-positioning the defenders all LOSE โ€” the
226
- # construction yard is razed because the pre-built defence and the
227
- # pre-positioned defenders are on the WRONG axis.
228
  description: >
229
- Intel said the enemy would come from the NORTH โ€” your pillboxes and
230
- four defenders are already lined up there. But the actual rush comes
231
- from elsewhere. Identify the live axis from observation, redeploy
232
- the defenders, and kill at least four with the yard intact. Two
233
- units alive, within about 50 turns. Trusting the intel layout loses
234
- the yard.
 
 
235
  overrides:
236
  actors:
237
  # Agent base โ€” construction yard at the centre. Pre-placed at
238
  # `health: 50` (HP %): post the engine movement+combat fixes a
239
- # 4-rifle south rush that reaches an UNDEFENDED fact razes it
240
- # comfortably inside the 4500-tick budget (~turn 45) โ€” the
241
- # damaged starting HP keeps the lazy/stall outcome a real
242
- # reachable LOSS rather than a budget-cap DRAW (the 4 idle
243
- # HoldFire defenders would otherwise soak the rush long
244
- # enough for a full-HP fact to survive the cap). The intended
245
- # detect-and-redeploy play intercepts the rush SOUTH of the
246
- # base and never lets it touch the fact, so the starting HP
247
- # is irrelevant to the WIN path.
248
  - {type: fact, owner: agent, position: [20, 20], health: 50}
249
  # Pre-built NORTH pillbox line โ€” the "prepared" defence per
250
- # the intel. These are far enough NORTH of the fact (y=14
251
- # vs fact y=20) that the south-coming rusher band reaches
252
- # the fact without ever entering pbox range โ€” they sit
253
- # silent the entire engagement unless the agent leaves the
254
- # defenders next to them (in which case the defenders also
255
- # sit silent and the fact dies anyway).
256
  - {type: pbox, owner: agent, position: [20, 14]}
257
  - {type: pbox, owner: agent, position: [22, 14]}
258
- # Four pre-placed e1 defenders lined up on the NORTH lane,
259
- # stance:2 (defend โ€” auto-fires at hostiles in range,
260
- # doesn't chase). stance:2 keeps them parked on the wrong
261
- # axis if the agent never issues a move; if the agent
262
- # explicitly `move_units` them south, they relocate.
263
- - {type: e1, owner: agent, position: [18, 14], stance: 0}
264
- - {type: e1, owner: agent, position: [20, 16], stance: 0}
265
- - {type: e1, owner: agent, position: [22, 16], stance: 0}
266
- - {type: e1, owner: agent, position: [24, 14], stance: 0}
267
- # SOUTH rusher band โ€” 4ร— e1 at y=32. The rusher bot drives
268
- # them at the agent centroid (~y=17 with 4 defenders
269
- # NORTH); they walk straight NORTH through y=20 (the fact)
270
- # on the way โ†’ fact razed unless intercepted SOUTH of it.
271
- # 4 e1 vs 4 defenders engaged head-on with focus-fire
272
- # trades cleanly (~1 defender lost on the engagement),
273
- # the kill bar (4) is exactly met when the rush is wiped.
274
- - {type: e1, owner: enemy, position: [20, 36], stance: 3, count: 4}
275
  # Inert enemy `fact` marker far east โ€” anti-DRAW per
276
  # CLAUDE.md; without a persistent enemy actor the engine
277
  # auto-`done`s on enemy-elim before the win/fail evaluator
278
  # runs.
279
  - {type: fact, owner: enemy, position: [120, 20]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
  win_condition:
281
  all_of:
282
  - building_count_gte: {type: fact, n: 1}
@@ -291,56 +290,48 @@ levels:
291
  max_turns: 50
292
 
293
  # โ”€โ”€ MEDIUM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
294
- # Same surprise-axis discrimination, larger rush. The intel /
295
- # pre-built layout is identical to easy; the rush is 5ร— e1 from
296
- # the SOUTH (one more than easy). The 4-defender screen still
297
- # wins on FAVOURABLE terms โ€” committing south so the defenders
298
- # engage the rush before it reaches the fact, focus-fire on the
299
- # nearest rusher to the fact, take โ‰ค1 loss; a late commit or a
300
- # wait-at-fact passive defence either burns the fact OR over-
301
- # trades the defender screen (busts `own_units_gte:3`).
302
- # max_turns 50 โ†’ reachable tick 4503 โ‰ฅ 4501 (same as easy).
303
  medium:
304
- # Original (pre-verbosity-sweep) description preserved for
305
- # contributors. The trimmed version below removes scripted-policy
306
- # spoilers and cell-coord dumps; load-bearing intent kept.
307
- #
308
- # INTEL said the enemy will attack from the NORTH โ€” but be ready for
309
- # anything. You start with a construction yard at (20, 20), two
310
- # pillboxes already standing NORTH of the base at (20, 14) and (22,
311
- # 14), and four rifle defenders lined up next to them on the NORTH
312
- # lane (y=14..16). Available cash 800. A stronger surprise rush โ€” 5
313
- # rifle infantry in a tight, fast cluster โ€” is incoming. Identify the
314
- # ACTUAL attack direction from your observation, then act: destroy at
315
- # least 4 enemy units AND keep โ‰ฅ3 of your own units alive AND keep the
316
- # construction yard standing before tick 4500. Stalling, leaving the
317
- # defenders on the NORTH lane next to the pre-built pillboxes, and
318
- # building extra structures without re-positioning the defenders all
319
- # LOSE โ€” the construction yard is razed and the 4-defender screen is
320
- # overrun if it commits late or never.
321
  description: >
322
- Same north-leaning pre-built defence, same $800 โ€” but the surprise
323
- rush is now five rifles in a tight cluster. Spot the actual
324
- approach, peel the four-defender screen off the north strip, and
325
- intercept on the real axis. Four kills, two alive, yard intact,
326
- within about 50 turns.
 
327
  overrides:
328
  actors:
329
- - {type: fact, owner: agent, position: [20, 20]}
330
  - {type: pbox, owner: agent, position: [20, 14]}
331
  - {type: pbox, owner: agent, position: [22, 14]}
332
- - {type: e1, owner: agent, position: [18, 14], stance: 0}
333
- - {type: e1, owner: agent, position: [20, 16], stance: 0}
334
- - {type: e1, owner: agent, position: [22, 16], stance: 0}
335
- - {type: e1, owner: agent, position: [24, 14], stance: 0}
336
- # Stronger surprise rush โ€” a tighter, faster cluster of
337
- # rifle infantry from the SOUTH (5 e1 at y=32). The same
338
- # 4-defender screen has to commit FIRST (move toward the
339
- # spotted rush, not wait for it to arrive at the fact); a
340
- # late commit lets the rush close to the fact and trades
341
- # the screen 2-for-2 โ†’ busts `own_units_gte:3`.
342
- - {type: e1, owner: enemy, position: [20, 36], stance: 3, count: 5}
343
  - {type: fact, owner: enemy, position: [120, 20]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344
  win_condition:
345
  all_of:
346
  - building_count_gte: {type: fact, n: 1}
@@ -355,107 +346,71 @@ levels:
355
  max_turns: 50
356
 
357
  # โ”€โ”€ HARD โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
358
- # +1 controlled variable: TWO spawn_point groups round-robin the
359
- # AGENT's base LONGITUDE per seed (WEST-staged base at x=20 OR
360
- # EAST-staged base at x=40). The intel always says "the enemy
361
- # will attack from the NORTH" and the pre-built defence is always
362
- # on the NORTH lane of each spawn (same as easy/medium). The
363
- # actual rush always arrives from the SOUTH (the surprise axis).
364
- # The capability test is the same detect-and-redeploy doctrine
365
- # as easy/medium โ€” but the fact's x-position varies per seed, so
366
- # a memorised "move defenders to (20,26)" opening does NOT
367
- # generalise; only the doctrine ("intercept south of THIS spawn's
368
- # fact, x-aligned with this spawn's base") does. An attrition cap
369
- # (units_lost_lte:2) tightens the margin vs medium โ€” a late
370
- # commit or a sloppy intercept overshoots the cap and loses,
371
- # even if the fact survives.
372
  hard:
373
- # Original (pre-verbosity-sweep) description preserved for
374
- # contributors. The trimmed version below removes scripted-policy
375
- # spoilers and cell-coord dumps; load-bearing intent kept.
376
- #
377
- # INTEL said the enemy will attack from the NORTH โ€” but be ready for
378
- # anything. Your construction yard sits at one of two LONGITUDES (WEST
379
- # at x=20 OR EAST at x=40, chosen by seed), with two pillboxes and
380
- # four rifle defenders pre-positioned in the NORTH lane of your spawn
381
- # (the intel direction). The actual rush arrives from the SOUTH (the
382
- # surprise axis) on both seeds, x-aligned with YOUR base's column.
383
- # Identify the ACTUAL attack direction from your observation, then re-
384
- # position your defenders SOUTH of YOUR construction yard and
385
- # intercept BEFORE the rush reaches it. Destroy at least 4 enemy units
386
- # AND keep โ‰ฅ2 of your own alive AND lose at most 2 defenders AND keep
387
- # the construction yard standing before tick 4500. Stalling, sticking
388
- # with the pre-built NORTH defence layout, and pure-building all LOSE.
389
  description: >
390
- Pre-built north defences as before, but your yard spawns either WEST
391
- (x=20) or EAST (x=40) by seed. The actual rush always comes from the
392
- SOUTH, x-aligned with your yard. Spot it, re-position the defenders
393
- south of YOUR yard, intercept before it lands. Four kills, two
394
- alive, loss cap two, yard intact, within about 50 turns.
 
395
  overrides:
396
  actors:
397
- # Hard tier: the intel direction is FIXED ("rushers will come
398
- # from the NORTH" โ€” the brief, same as easy/medium) and the
399
- # actual rush direction is FIXED (SOUTH โ€” the surprise axis).
400
- # The seed-driven variable is the AGENT base LONGITUDE (x
401
- # coord) โ€” two spawn_point groups round-robin a WEST-staged
402
- # base (x=20) and an EAST-staged base (x=40). The capability
403
- # test is the same on both spawns: identify that the actual
404
- # rush comes from the SOUTH (not the announced NORTH) and
405
- # re-position the defenders SOUTH of THIS spawn's fact to
406
- # intercept the rush before it reaches it. A single
407
- # memorised intercept cell ("move defenders to (20,26)")
408
- # does NOT generalise because the fact's x-position varies
409
- # per seed; the doctrine ("intercept south of THIS spawn's
410
- # fact") does. An attrition cap (units_lost_lte:2) tightens
411
- # the margin โ€” a late commit overshoots the cap.
412
- #
413
  # โ”€โ”€ spawn_point 0 โ€” WEST-staged base (x=20) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
414
- # Same geometry as easy/medium: fact at (20,20), pre-built
415
- # defence on the NORTH lane y=14, defenders at y=14/16
416
- # (stance:0 HoldFire so they don't engage passively).
417
  - {type: fact, owner: agent, position: [20, 20], spawn_point: 0}
418
  - {type: pbox, owner: agent, position: [20, 14], spawn_point: 0}
419
  - {type: pbox, owner: agent, position: [22, 14], spawn_point: 0}
420
- - {type: e1, owner: agent, position: [18, 14], stance: 0, spawn_point: 0}
421
- - {type: e1, owner: agent, position: [20, 16], stance: 0, spawn_point: 0}
422
- - {type: e1, owner: agent, position: [22, 16], stance: 0, spawn_point: 0}
423
- - {type: e1, owner: agent, position: [24, 14], stance: 0, spawn_point: 0}
424
  # โ”€โ”€ spawn_point 1 โ€” EAST-staged base (x=40) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
425
- # Same geometry translated east by 20 cells.
426
  - {type: fact, owner: agent, position: [40, 20], spawn_point: 1}
427
  - {type: pbox, owner: agent, position: [40, 14], spawn_point: 1}
428
  - {type: pbox, owner: agent, position: [42, 14], spawn_point: 1}
429
- - {type: e1, owner: agent, position: [38, 14], stance: 0, spawn_point: 1}
430
- - {type: e1, owner: agent, position: [40, 16], stance: 0, spawn_point: 1}
431
- - {type: e1, owner: agent, position: [42, 16], stance: 0, spawn_point: 1}
432
- - {type: e1, owner: agent, position: [44, 14], stance: 0, spawn_point: 1}
433
- # โ”€โ”€ Enemy rush bands โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
434
- # CLAUDE.md: enemy actors WITHOUT spawn_point ALWAYS place
435
- # regardless of the agent's spawn group, so we declare BOTH a
436
- # NORTH and a SOUTH band. For spawn_point 0 (NORTH base) the
437
- # MATCHING band is the NORTH one (y=4) โ€” same side as the
438
- # base, opposite the pre-built defence. For spawn_point 1
439
- # (SOUTH base) the MATCHING band is the SOUTH one (y=36).
440
- # The `rusher` bot charges the agent CENTROID โ€” which sits
441
- # near the base on each spawn (the pre-built defenders are
442
- # only 2-4 cells off so the centroid stays close to the
443
- # base) โ€” so the matching-side band has the SHORTER path
444
- # and is the immediate threat; the opposite-side band has
445
- # to walk across the whole map to arrive.
446
- - {type: e1, owner: enemy, position: [20, 36], stance: 3, count: 5}
447
- - {type: e1, owner: enemy, position: [40, 36], stance: 3, count: 5}
448
- # Note: enemies don't honour spawn_point โ€” both bands always
449
- # place. The matching-side band (same latitude as the agent
450
- # base) is the immediate threat and the trigger for the
451
- # detect-and-redeploy decision. The opposite-side band has
452
- # to walk the full map width (28+ cells) before becoming a
453
- # serious threat โ€” well outside the 4500-tick budget.
454
  # Inert enemy `fact` marker far east (anti-DRAW). Placed
455
  # off-axis (y=2) so the bands don't include it in their
456
- # centroid target and it doesn't accidentally end the
457
- # episode early via enemy-elim auto-done.
458
  - {type: fact, owner: enemy, position: [120, 2]}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
459
  win_condition:
460
  all_of:
461
  - building_count_gte: {type: fact, n: 1}
 
1
  # def-surprise-flank-react.yaml
2
  #
3
+ # REASONING capability โ€” Group C surprise-axis reaction. The brief
4
+ # (intel) tells the model the enemy will attack from the NORTH and the
5
+ # map LOOKS like the intel is right: a band of rocky terrain along the
6
+ # north edge forms a natural chokepoint, with a single narrow opening
7
+ # in the centre. Two pre-built pillboxes (the canonical "prepared
8
+ # defence") sit just south of that chokepoint, watching the only
9
+ # obvious northern lane. BUT the actual rush arrives MID-EPISODE from
10
+ # the SOUTH (the open, undefended flank) via `scheduled_events:
11
+ # spawn_actors` โ€” the world's terrain implies one approach, the intel
12
+ # announces the same approach, and the enemy comes from the OPPOSITE
13
+ # direction.
 
 
 
 
 
 
 
 
 
 
14
  #
15
  # Real-world anchors:
16
+ # - Adversarial robustness on a directional axis: a stated cue +
17
+ # terrain-implied cue are misdirection; the agent must react to
18
+ # the ground-truth signal (where the rusher band actually spawns).
19
  # - Opponent-modelling under deception: detect that the announced
20
  # plan and the observed plan differ; replan around the truth.
21
  # - Military reactive defence / flank discipline: a fortified flank
22
  # that the enemy has bypassed must be ABANDONED in favour of the
23
  # actual contact axis; "fighting the plan, not the enemy" is the
24
+ # classical failure mode this trains against.
25
  # - CICERO deception handling: when announced behaviour contradicts
26
+ # observed behaviour, treat the announcement as a signal NOT to
27
+ # trust the plan and commit to the observation.
28
  #
29
  # DISCRIMINATIONS (the scripted bar โ€” every brute / lazy play LOSES,
30
  # only the detect-and-redeploy play WINS):
31
  #
32
+ # โ€ข stall (only `observe`): defenders sit idle wherever they
33
+ # spawned; the SOUTH rusher band spawns at tick 800 and walks
34
+ # straight at the fact โ†’ fact razed โ†’ LOSS.
 
 
35
  #
36
+ # โ€ข stay-NORTH-with-pre-built-defence (move defenders to the
37
+ # intel/pbox lane): the defenders re-park on the NORTH lane next
38
+ # to the pillboxes. When the south rush spawns mid-episode, the
39
+ # defenders are 12+ cells off-axis and the rusher band reaches
40
+ # the fact unopposed โ†’ LOSS.
 
41
  #
42
+ # โ€ข pure-build-no-react: queue/place a third pbox on the NORTH
43
+ # lane (where the intel + terrain say the rush will come). The
44
+ # pre-built defenders + the new pbox never face south and the
45
+ # rusher band razes the fact โ†’ LOSS.
 
 
46
  #
47
+ # โ€ข intended detect-and-redeploy: hold a mobile-reserve posture
48
+ # (do not pre-commit defenders to the intel lane), observe the
49
+ # spawned rusher band the instant `enemy_unit_spotted` fires,
50
+ # then `attack_unit` all four defenders onto the south rusher
51
+ # cluster. Keep โ‰ฅ2 alive, kill โ‰ฅ4, fact survives โ†’ WIN.
 
 
52
  #
53
  # ENGINE FACTS used (CLAUDE.md / SCENARIO_QUALITY.md):
54
+ # - `scheduled_events: spawn_actors` injects fresh-id actors at a
55
+ # scheduled tick (Wave-9 feature; test_scheduled_events). The
56
+ # surprise SOUTH rush is INJECTED at tick 800 (easy) or in two
57
+ # waves (medium); a t=0 placement cannot exercise "react to a
58
+ # reveal that develops". Spawned actors get fresh ids so the
59
+ # `enemy_unit_spotted` interrupt fires on their appearance.
60
+ # - The `rusher` bot drives every unit at the agent centroid
61
+ # every ~8 ticks. With defenders distributed near the fact at
62
+ # (20,20), the centroid sits on the fact and the south rusher
63
+ # band walks straight at it.
64
+ # - Enemy actors with NO spawn_point ALWAYS place regardless of
65
+ # the agent's spawn group (per CLAUDE.md). On hard we tag
66
+ # agent N/S spawn groups so the seed-driven AGENT longitude
67
+ # varies (and the matching surprise direction with it); the
68
+ # scheduled south rush also varies its x-target per spawn via
69
+ # the scheduled-events block in `overrides:`.
70
  # - Inert enemy `fact` marker far east (anti-DRAW): without a
71
  # persistent enemy actor the engine auto-`done`s on enemy-elim
72
  # before the win/fail evaluator runs.
73
  # - `enemy_unit_spotted` interrupt โ†’ re-decide the instant the
74
+ # scheduled rusher band materialises (the detection event).
 
75
  # - `own_unit_destroyed` interrupt โ†’ re-decide when a defender
76
  # falls.
77
  # - Tick budget: ~90 ticks/turn. `max_turns 50` โ†’ reachable tick
78
+ # 93 + 90ยท49 = 4503 โ‰ฅ 4501 (the after_ticks fail) so a non-
79
+ # winner is a real reachable timeout LOSS, never a draw. The
80
+ # scheduled spawn at tick 800 leaves >3700 ticks for the
81
+ # detect-and-redeploy intercept.
82
  # - No tent in the pre-placed base + cash 800 means the model
83
  # CAN'T cheese the win by mass-training fresh defenders โ€” the
84
+ # intended play is RE-POSITIONING the 4 starting defenders.
85
+ # - Tailored terrain (`def-surprise-flank-react-arena`): two
86
+ # northern rocky bands form a visual chokepoint that "matches"
87
+ # the intel; the south is fully open. The terrain reinforces
88
+ # the misdirection โ€” a model reading the minimap should see
89
+ # "north is the fortifiable lane" and the brief agrees, but
90
+ # the south spawn proves the cue is false.
91
  #
92
  # Distinct from siblings (Group C):
93
  # * def-position-expected-direction โ€” intel is TRUE; commit defence
 
99
  # * defense-rush-survive โ€” opening rush vs sustained throughput;
100
  # no directional misdirection.
101
  # * THIS pack โ€” intel is FALSE (or just unreliable). The pre-built
102
+ # pillbox line is on the WRONG axis. The model must DETECT (via
103
+ # the mid-episode scheduled spawn) that the announced and
104
+ # observed axes differ and RE-POSITION accordingly.
105
  #
106
  # Validate (no model / no network):
107
+ # python3 -m pytest tests/test_def_surprise_flank_react.py -q
 
108
 
109
  meta:
110
  id: def-surprise-flank-react
 
112
  capability: reasoning
113
  real_world_meaning: >
114
  The mission brief states the enemy will attack from the NORTH and
115
+ the terrain reinforces it: a rocky band on the north edge forms a
116
+ natural chokepoint, with two pillboxes pre-built to cover it. The
117
+ four pre-placed defenders are distributed around the construction
118
+ yard โ€” not yet committed to either lane. MID-EPISODE a rusher band
119
+ actually arrives from the SOUTH (the open flank) via a scheduled
120
+ spawn. The decision is whether the agent trusts the announced
121
+ direction + terrain cue and watches its construction yard get
122
+ razed, or detects the actual attack axis from the spawn event,
123
+ leaves the wrong-axis pillboxes silent, and commits the defenders
124
+ SOUTH to intercept the real rush before it reaches the fact.
 
 
125
  robotics_analogue: >
126
  Misdirection-aware defence / military adversarial robustness on
127
  a directional axis: a fortified flank that the enemy has bypassed
 
141
  - 'CICERO deception handling'
142
  author: openra-bench
143
 
144
+ # Tailored arena: 128ร—40 with a northern rocky band forming a visual
145
+ # chokepoint at xโ‰ˆ14..26 (the only narrow northern opening โ€” the
146
+ # pre-built pillboxes at (20,14)/(22,14) sit just south of it and
147
+ # "cover" the intel-direction approach). The southern half is fully
148
+ # open โ€” the surprise-rush spawn at y=36 has clear path to the fact.
149
+ # The terrain is the second leg of the misdirection: terrain implies
150
+ # the same axis the intel announces.
151
+ base_map:
152
+ generator: arena
153
+ name: def-surprise-flank-react-arena
154
+ width: 128
155
+ height: 40
156
+ cordon: 2
157
+ title: Surprise Flank Arena 128x40
158
+ obstacles:
159
+ # Northern rocky band โ€” west of the central chokepoint.
160
+ - {x: 4, y: 4, w: 10, h: 4}
161
+ # Northern rocky band โ€” east of the central chokepoint.
162
+ - {x: 26, y: 4, w: 12, h: 4}
163
+ # Northern rocky band โ€” far-east (continues the natural-fortress
164
+ # impression across the full north edge).
165
+ - {x: 50, y: 4, w: 76, h: 4}
166
+ # NOTE: the only north opening is x=14..26 at y=4..7; the pre-
167
+ # built pillboxes at (20,14)/(22,14) cover this opening, so
168
+ # the world LOOKS like the prepared defence is sensible. The
169
+ # south is deliberately left wide open.
170
 
171
  # Pack-wide budget โ€” a small float (800cr). Not enough to spam a
172
+ # second pillbox line on the south, and without a `tent` the
173
+ # infantry queue is empty so the cash can't be turned into fresh
174
+ # defenders. The intended play is RE-POSITIONING the four pre-
175
+ # placed defenders; the cash exists so the model has the OPTION to
176
+ # attempt a build (which is part of the discrimination โ€” a pure-
177
+ # build-no-react policy also LOSES).
178
  starting_cash: 800
179
 
180
  base:
181
  agent:
182
  faction: allies
183
  # Concentrated charge at the agent centroid every ~8 ticks โ€” the
184
+ # canonical rush-defence opponent. The bot's SPAWN POSITION is
185
+ # the surprise (the brief says NORTH, the bot is spawned SOUTH
186
+ # via scheduled_events).
187
  enemy:
188
  faction: soviet
189
  bot_type: rusher
 
196
  - attack_move
197
  - stop
198
  planning: true
199
+ # React the INSTANT the scheduled south rush materialises (the
200
  # detection event) or a defender is lost โ€” the re-positioning
201
  # decision is reactive, not on fixed 90-tick boundaries.
202
  interrupts:
 
208
 
209
  levels:
210
  # โ”€โ”€ EASY โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
211
+ # The bare surprise-axis detect-and-react skill. The intel says
212
+ # NORTH, the terrain (a north-edge natural chokepoint with the
213
+ # pillboxes already covering it) AGREES, but a single light SOUTH
214
+ # rush (4ร— e1) materialises at tick 800 via scheduled spawn. The
215
+ # 4 defenders start DISTRIBUTED around the construction yard
216
+ # (NOT pre-committed to the intel lane); the intended play is to
217
+ # commit them SOUTH the instant the rush is spotted.
218
+ # max_turns 50 โ†’ reachable tick 93 + 90ยท49 = 4503 โ‰ฅ 4501.
219
  easy:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
  description: >
221
+ Intel said the enemy would come from the NORTH and the terrain
222
+ agrees โ€” a north-edge chokepoint with two pillboxes already in
223
+ place. Four defenders are staged around the construction yard
224
+ (not yet committed to either lane). Mid-episode the actual rush
225
+ arrives from elsewhere. Identify the live axis from observation,
226
+ commit the defenders, and kill at least four with the yard
227
+ intact. Two units alive, within about 50 turns. Pre-committing
228
+ to the intel lane loses the yard.
229
  overrides:
230
  actors:
231
  # Agent base โ€” construction yard at the centre. Pre-placed at
232
  # `health: 50` (HP %): post the engine movement+combat fixes a
233
+ # 4-rifle south rush that reaches an UNDEFENDED fact razes
234
+ # it comfortably inside the 4500-tick budget โ€” the damaged
235
+ # starting HP keeps the lazy/stall outcome a real reachable
236
+ # LOSS rather than a budget-cap DRAW. The intended detect-
237
+ # and-redeploy play intercepts SOUTH of the base and never
238
+ # lets it touch the fact.
 
 
 
239
  - {type: fact, owner: agent, position: [20, 20], health: 50}
240
  # Pre-built NORTH pillbox line โ€” the "prepared" defence per
241
+ # the intel + terrain. Sits just south of the only northern
242
+ # chokepoint opening (x=14..26 at y=4..7); the world LOOKS
243
+ # like the prepared defence is sensible. Far enough north
244
+ # of the fact (y=14 vs fact y=20) that the south-coming
245
+ # rusher band reaches the fact without ever entering pbox
246
+ # range โ€” they sit silent the entire engagement.
247
  - {type: pbox, owner: agent, position: [20, 14]}
248
  - {type: pbox, owner: agent, position: [22, 14]}
249
+ # Four pre-placed e1 defenders DISTRIBUTED around the fact
250
+ # (not pre-committed to the intel lane). stance:0 HoldFire
251
+ # so they don't engage passively โ€” the model must issue
252
+ # explicit orders. With defenders centred near the fact
253
+ # the rusher centroid sits on the fact and the south rush
254
+ # walks straight at it.
255
+ - {type: e1, owner: agent, position: [18, 20], stance: 0}
256
+ - {type: e1, owner: agent, position: [22, 20], stance: 0}
257
+ - {type: e1, owner: agent, position: [20, 18], stance: 0}
258
+ - {type: e1, owner: agent, position: [20, 22], stance: 0}
 
 
 
 
 
 
 
259
  # Inert enemy `fact` marker far east โ€” anti-DRAW per
260
  # CLAUDE.md; without a persistent enemy actor the engine
261
  # auto-`done`s on enemy-elim before the win/fail evaluator
262
  # runs.
263
  - {type: fact, owner: enemy, position: [120, 20]}
264
+ # Mid-episode surprise SOUTH rush โ€” 4ร— e1 at (20, 36),
265
+ # spawned at tick 800. Enough lead time for the model to
266
+ # observe the spawn (the `enemy_unit_spotted` interrupt
267
+ # fires), commit defenders south, and intercept before the
268
+ # rush reaches the fact at (20, 20). The 16-cell south-
269
+ # to-fact gap takes ~30 ticks at infantry speed, but the
270
+ # rusher bot re-targets every 8 ticks toward the agent
271
+ # centroid (which sits ON the fact when defenders are
272
+ # central) โ€” so the rush is decisive only if the defenders
273
+ # never commit south.
274
+ scheduled_events:
275
+ - tick: 800
276
+ type: spawn_actors
277
+ actors:
278
+ - {type: e1, owner: enemy, position: [20, 36], stance: 3, count: 4}
279
  win_condition:
280
  all_of:
281
  - building_count_gte: {type: fact, n: 1}
 
290
  max_turns: 50
291
 
292
  # โ”€โ”€ MEDIUM โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
293
+ # +1 controlled variable vs easy: TWO reinforcement waves on the
294
+ # south flank (tick 800 โ†’ 4ร—e1, tick 1400 โ†’ 3ร—e1). A late commit
295
+ # gets the defenders south in time for the first wave but the
296
+ # second wave catches them depleted; a wait-at-fact passive
297
+ # defence either burns the fact OR over-trades the defender
298
+ # screen (busts `own_units_gte:2`). The intended play commits
299
+ # SOUTH on the FIRST spawn and intercepts both waves on the
300
+ # south lane before either reaches the fact.
 
301
  medium:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  description: >
303
+ Same north-leaning intel and pre-built defence, same $800 โ€” but
304
+ the surprise rush now arrives in TWO waves on the live axis.
305
+ Spot the actual approach on the first wave, commit the defenders
306
+ forward, and hold the line across both waves. Four kills, two
307
+ alive, yard intact, within about 50 turns. A late commit or a
308
+ passive wait-at-fact stance loses to the second wave.
309
  overrides:
310
  actors:
311
+ - {type: fact, owner: agent, position: [20, 20], health: 50}
312
  - {type: pbox, owner: agent, position: [20, 14]}
313
  - {type: pbox, owner: agent, position: [22, 14]}
314
+ - {type: e1, owner: agent, position: [18, 20], stance: 0}
315
+ - {type: e1, owner: agent, position: [22, 20], stance: 0}
316
+ - {type: e1, owner: agent, position: [20, 18], stance: 0}
317
+ - {type: e1, owner: agent, position: [20, 22], stance: 0}
 
 
 
 
 
 
 
318
  - {type: fact, owner: enemy, position: [120, 20]}
319
+ # TWO scheduled SOUTH waves โ€” first at tick 800 (4ร—e1, the
320
+ # detection event), second at tick 1400 (3ร—e1, the follow-
321
+ # up). The defenders must be committed south on the FIRST
322
+ # spawn to engage the first wave AND still be positioned to
323
+ # absorb the second; a late commit lets the first wave reach
324
+ # the fact while the second wave catches the defenders out
325
+ # of position.
326
+ scheduled_events:
327
+ - tick: 800
328
+ type: spawn_actors
329
+ actors:
330
+ - {type: e1, owner: enemy, position: [20, 36], stance: 3, count: 4}
331
+ - tick: 1400
332
+ type: spawn_actors
333
+ actors:
334
+ - {type: e1, owner: enemy, position: [20, 36], stance: 3, count: 3}
335
  win_condition:
336
  all_of:
337
  - building_count_gte: {type: fact, n: 1}
 
346
  max_turns: 50
347
 
348
  # โ”€โ”€ HARD โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
349
+ # +1 controlled variable vs medium: TWO spawn_point groups round-
350
+ # robin the AGENT base LONGITUDE per seed (WEST-staged base at
351
+ # x=20 OR EAST-staged base at x=40). The intel always says "the
352
+ # enemy will attack from the NORTH" and the pre-built defence is
353
+ # always on the NORTH lane of each spawn (same as easy/medium).
354
+ # The actual rush always arrives from the SOUTH (the surprise
355
+ # axis) โ€” but on each seed, x-aligned with YOUR base's column
356
+ # (so the literal "flank direction" relative to the base varies
357
+ # by seed: WEST spawn โ‡’ rush at x=20, EAST spawn โ‡’ rush at
358
+ # x=40). A single memorised intercept cell ("move defenders to
359
+ # (20,26)") does NOT generalise; only the doctrine ("intercept
360
+ # south of THIS spawn's fact") does. An attrition cap
361
+ # (units_lost_lte:2) tightens the margin โ€” a late commit
362
+ # overshoots the cap.
363
  hard:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  description: >
365
+ Pre-built north defences as before, but your yard spawns either
366
+ WEST (x=20) or EAST (x=40) by seed. The actual rush always comes
367
+ from the SOUTH, x-aligned with your yard, in two scheduled waves.
368
+ Spot it, commit the defenders south of YOUR yard, intercept
369
+ before it lands. Four kills, two alive, loss cap two, yard
370
+ intact, within about 50 turns.
371
  overrides:
372
  actors:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
  # โ”€โ”€ spawn_point 0 โ€” WEST-staged base (x=20) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
 
 
374
  - {type: fact, owner: agent, position: [20, 20], spawn_point: 0}
375
  - {type: pbox, owner: agent, position: [20, 14], spawn_point: 0}
376
  - {type: pbox, owner: agent, position: [22, 14], spawn_point: 0}
377
+ - {type: e1, owner: agent, position: [18, 20], stance: 0, spawn_point: 0}
378
+ - {type: e1, owner: agent, position: [22, 20], stance: 0, spawn_point: 0}
379
+ - {type: e1, owner: agent, position: [20, 18], stance: 0, spawn_point: 0}
380
+ - {type: e1, owner: agent, position: [20, 22], stance: 0, spawn_point: 0}
381
  # โ”€โ”€ spawn_point 1 โ€” EAST-staged base (x=40) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
382
  - {type: fact, owner: agent, position: [40, 20], spawn_point: 1}
383
  - {type: pbox, owner: agent, position: [40, 14], spawn_point: 1}
384
  - {type: pbox, owner: agent, position: [42, 14], spawn_point: 1}
385
+ - {type: e1, owner: agent, position: [38, 20], stance: 0, spawn_point: 1}
386
+ - {type: e1, owner: agent, position: [42, 20], stance: 0, spawn_point: 1}
387
+ - {type: e1, owner: agent, position: [40, 18], stance: 0, spawn_point: 1}
388
+ - {type: e1, owner: agent, position: [40, 22], stance: 0, spawn_point: 1}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  # Inert enemy `fact` marker far east (anti-DRAW). Placed
390
  # off-axis (y=2) so the bands don't include it in their
391
+ # centroid target.
 
392
  - {type: fact, owner: enemy, position: [120, 2]}
393
+ # TWO scheduled SOUTH waves โ€” placed at BOTH possible base
394
+ # longitudes so whichever agent spawn fires, the matching
395
+ # rush spawn is x-aligned. The opposite-longitude spawn
396
+ # would normally be wasted distance (the rusher bot drives
397
+ # at the agent centroid, which sits on the live base) โ€” but
398
+ # because enemies don't honour spawn_point, both spawns
399
+ # fire. The far-from-base spawn has to walk the full map
400
+ # width before becoming a serious threat โ€” well outside the
401
+ # 4500-tick budget โ€” so only the matching-longitude spawn
402
+ # is the immediate threat.
403
+ scheduled_events:
404
+ - tick: 800
405
+ type: spawn_actors
406
+ actors:
407
+ - {type: e1, owner: enemy, position: [20, 36], stance: 3, count: 4}
408
+ - {type: e1, owner: enemy, position: [40, 36], stance: 3, count: 4}
409
+ - tick: 1400
410
+ type: spawn_actors
411
+ actors:
412
+ - {type: e1, owner: enemy, position: [20, 36], stance: 3, count: 3}
413
+ - {type: e1, owner: enemy, position: [40, 36], stance: 3, count: 3}
414
  win_condition:
415
  all_of:
416
  - building_count_gte: {type: fact, n: 1}