pilot: escort-weighted targeting (DEFEND), plus the capital-ship keep-out it needed

While the asset is losing hull, target what is pressing IT — ranked by distance
to the asset minus credit for closing on it — instead of what is nearest to us.
Trigger and ranking both read the live hull (pos+0x154), so nothing is inferred.
DEFEND engaged 1.9 s after the asset's first hit and held 54% of a 330 s run.

It did NOT measurably save the asset: over the window two runs share, the
policies are equal to within noise (t=239: 23218 vs 23038). Two reasons, both
recorded rather than papered over: the runs are not comparable past that window
(spawn timing differs and the hostile count GREW 134->166 in one, fell 147->118
in the other), and the real bottleneck is lethality — the guns are on for 12% of
combat frames because the target is outside the 9 deg cone the rest of the time.

Also corrects a single-run claim in the previous commit: the asset is NOT
reliably safe for the first ~170 s. A second run had first damage at t=70 s. The
stage does not replay identically; only 'the loss is slow' survives.

Fixes a fatal bug the new mode exposed: DEFEND flies at the asset, which sits
inside the friendly formation, and the first escort run went hull 1500 -> DEAD in
one tick at 2026 units/s, 0.6 s from a friendly destroyer that avoidance thought
it would clear by 365 units — the ship's radius is 2000. Keep-out applied only to
hostile turrets. Every entity above BIG_RADIUS now gets its own radius + 800 of
physical keep-out with braking inside it, whatever its faction.
This commit is contained in:
claude-re
2026-07-30 17:49:38 +00:00
parent c277e42c92
commit 402985adbf
3 changed files with 524 additions and 13 deletions

View File

@@ -33,11 +33,13 @@ five distinct HP values**:
| `UN_e105_ADAN_Cruiser` | 3800 | 30000 | 30000.0 |
| **`UN_f101_TCAF_Acropolis`** | 1400 | **25000** | **25000.0** |
Of the 18 capital ships present at t=0, 13 sat exactly at their `HP`; the other 5
sat *slightly below* it (9800/10000, 29933.3/30000, 9725/10000, 9950/10000,
9750/10000) — the battle is already in progress when the player launches, so those
five had already been shot at. **Nothing read above its `HP`, and nothing read an
unrelated number**, which is what a coincidental offset would produce.
Measured directly by `mission_state.py scan` at the start of three separate runs:
**146/150, 147/150 and 147/150 entities** hold exactly their definition's `HP` at
`pos+0x154`. The handful that do not sit *slightly below* it (9800/10000,
29933.3/30000, 9725/10000, …) — the battle is already in progress when the player
launches, so those ships have already been shot at. **Nothing read above its `HP`,
and nothing read an unrelated number**, which is what a coincidental offset would
produce.
The value behaves like a live counter, not a copy of the definition:
@@ -65,13 +67,14 @@ t= 210.1s 24149.5
t= 239.1s 23038.2 -1961.8 total, ≈ -600 HP/min once it starts
```
Two useful consequences:
1. **The attack on the asset starts late** (~t≈170 s here) and then runs at roughly
600 HP/min, i.e. ~40 min to sink from full. The earlier `GAME OVER` run therefore
did not lose because the loss is fast — it lost because nothing was defending.
2. **The asset is not in danger for the first ~23 minutes**, so an escort policy has
time to spend on offence early and should tighten later.
**⚠️ Onset is NOT a fixed schedule — corrected by a later run.** From this run alone
it looked like the asset is safe for the first ~170 s. A second run put the first
damage at **t = 70 s**, and its hostile population *grew* (134 → 166 ADAN) where this
one's shrank (147 → 118). So the stage is not replaying identically, and "the asset
is untouched early" is a property of one run, not of Stage 02. What survives the
second run is the weaker, still useful claim: **the loss is slow** — a few hundred to
~1400 HP/min against 25000, so tens of minutes to sink. The earlier `GAME OVER`
therefore was not a fast loss; it was an undefended one.
## Also captured
@@ -83,6 +86,41 @@ Two useful consequences:
alive, so that counter is **objectives, not hostiles** — its RAM address is still
unknown (❔ open).
## Escort-weighted targeting — implemented, and what it did NOT fix
`pilot.py` gained a **DEFEND** mode (2026-07-30): while the asset is losing hull,
target the hostiles pressing *it* — ranked by distance to the asset minus credit for
closing on it — instead of the ones nearest to us. Trigger and ranking both read the
live hull, so nothing is inferred.
It works mechanically: DEFEND engaged **1.9 s after the asset's first hit** in one run
(t=167.0), and held for 54 % of a 330 s run. **But it did not measurably save the
asset.** Over the window the two policies share, they are the same to within noise:
| t (s) | nearest-fighter | escort-weighted |
|---|---|---|
| 120 | 25000.0 | 24910.0 |
| 180 | 24779.5 | 24460.0 |
| 239 | 23038.2 | 23218.0 |
Two honest reasons it cannot yet be scored better than "no worse":
1. **The runs are not comparable past that window** — different spawn timing and, in
the escort-weighted run, a hostile population that *grew* 134 → 166 while the
baseline's fell 147 → 118.
2. **Lethality is the real bottleneck, not target choice.** The guns are on for only
**12 % of combat frames** (320 of 2630); the rest of the time the target is outside
the 9° firing cone while the loop manoeuvres. Choosing a better target does little
when most passes do not shoot.
**One bug found and fixed by the first escort run** (worth keeping as a pattern): the
new mode flies *at* the asset, which sits inside the friendly formation, and the run
ended `hull 1500 -> DEAD` in a single tick at 2026 units/s, 0.6 s from a friendly
destroyer the avoidance expected to clear by 365 units — against a hull of radius
2000. Keep-out had been applied only to hostile turrets. Every entity above
`BIG_RADIUS` now gets a physical keep-out of **its own radius + 800**, with braking
inside it, whatever its faction; the next run survived its full 330 s untouched.
## Reimplementation notes
- Defeat conditions for an escort stage are readable as: protected-asset