Compare commits
2 Commits
auto/re-un
...
auto/re-es
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c3e3dfe47 | |||
| f4d59c5783 |
@@ -58,3 +58,26 @@ class cannot hide behind `e106` passing.
|
||||
handedness, node-instance recursion) is not re-breaking a correct assembly — compare
|
||||
the viewer's placement against `assemble_ship`'s output directly before blaming the
|
||||
format layer.
|
||||
|
||||
---
|
||||
|
||||
## Viewer: `include_external` is already on — that hypothesis is dead
|
||||
|
||||
**Checked 2026-08-11.** The item above names "first that it passes
|
||||
`include_external = true` (`iso_loader.rs:4012`)" as the cheap first step. It
|
||||
does: `ShipBrowser::show_external` defaults to `true`
|
||||
(`iso_loader.rs:643`), the checkbox reads it (`ui.rs:1593`) and it is threaded
|
||||
through `RequestShipRender` → `build_ship_model` → `assemble_ship` unchanged
|
||||
(`ui.rs:1689`, `iso_loader.rs:4012`). So a ship rendered by the viewer is the
|
||||
full external assembly, not the bare hull.
|
||||
|
||||
The viewer also does not have a transform stack of its own to blame: it bakes
|
||||
`ScenePart::apply` straight into the vertices and rotates normals by the same
|
||||
`p.m` (`iso_loader.rs:4030-4062`), so its placement is `assemble_ship`'s output
|
||||
by construction. What remains unexcluded, in order of cheapness: the mirror
|
||||
handling (`det < 0` reverses triangle winding only — a reflected part keeps its
|
||||
reflected geometry), `Xbg7Model::models_named` resolving the wrong sub-model when
|
||||
a resource name repeats, and the exhaust cones. **Next step is a visual**: the
|
||||
diagnosis has run out of things it can settle by reading, so the viewer needs to
|
||||
be run against a known-good class (`e106`) and its render compared with
|
||||
`ship_render`'s.
|
||||
|
||||
161
docs/re/captures/escort-decay-ab.csv
Normal file
161
docs/re/captures/escort-decay-ab.csv
Normal file
@@ -0,0 +1,161 @@
|
||||
run,turret_rule,t_s,acropolis_hull,e007_alive,e007_killed_cum
|
||||
mission01,off,0,25000.0,0,0
|
||||
mission01,off,10,25000.0,5,0
|
||||
mission01,off,20,25000.0,6,0
|
||||
mission01,off,30,25000.0,6,0
|
||||
mission01,off,40,25000.0,6,0
|
||||
mission01,off,50,25000.0,8,0
|
||||
mission01,off,60,25000.0,11,0
|
||||
mission01,off,70,25000.0,11,3
|
||||
mission01,off,80,25000.0,8,3
|
||||
mission01,off,90,25000.0,8,3
|
||||
mission01,off,100,25000.0,10,3
|
||||
mission01,off,110,25000.0,14,3
|
||||
mission01,off,120,25000.0,16,3
|
||||
mission01,off,130,25000.0,17,3
|
||||
mission01,off,140,25000.0,20,3
|
||||
mission01,off,150,25000.0,23,3
|
||||
mission01,off,160,25000.0,26,4
|
||||
mission01,off,170,24509.5,33,4
|
||||
mission01,off,180,24509.5,37,4
|
||||
mission01,off,190,24419.5,38,5
|
||||
mission01,off,200,24212.5,41,5
|
||||
mission01,off,210,23447.5,40,5
|
||||
mission01,off,220,23447.5,40,5
|
||||
mission01,off,220,23447.5,40,5
|
||||
mission01,off,230,23447.5,41,5
|
||||
mission01,off,230,23447.5,41,5
|
||||
mission01,off,240,22912.0,43,5
|
||||
mission01,off,240,22867.0,43,5
|
||||
mission01,off,250,22667.0,42,6
|
||||
mission01,off,250,22647.0,42,6
|
||||
mission01,off,260,22334.5,44,6
|
||||
mission01,off,260,22177.9,44,6
|
||||
mission01,off,270,21101.2,46,6
|
||||
mission01,off,270,21101.2,45,6
|
||||
mission01,off,280,20292.6,46,6
|
||||
mission01,off,280,20279.1,46,6
|
||||
mission01,off,290,19598.5,58,6
|
||||
mission01,off,290,19598.5,58,6
|
||||
mission01,off,300,18725.2,58,6
|
||||
mission01,off,300,18658.5,47,6
|
||||
mission01,off,310,18231.9,47,6
|
||||
mission01,off,310,18231.9,47,6
|
||||
mission01,off,320,17425.2,47,6
|
||||
mission01,off,330,17046.9,49,6
|
||||
mission01,off,340,16044.8,49,7
|
||||
mission01,off,350,15461.5,50,7
|
||||
mission01,off,360,14956.3,51,7
|
||||
mission01,off,370,14425.5,51,8
|
||||
mission01,off,380,13910.5,51,8
|
||||
mission01,off,390,13418.9,49,11
|
||||
mission01,off,400,13218.9,45,12
|
||||
mission01,off,410,12517.9,44,12
|
||||
mission01,off,420,12161.3,44,13
|
||||
mission01,off,430,11601.3,44,13
|
||||
mission02,on,0,25000.0,0,0
|
||||
mission02,on,10,25000.0,5,0
|
||||
mission02,on,20,25000.0,6,0
|
||||
mission02,on,30,25000.0,5,0
|
||||
mission02,on,40,25000.0,5,1
|
||||
mission02,on,50,25000.0,7,1
|
||||
mission02,on,60,25000.0,10,1
|
||||
mission02,on,70,25000.0,7,4
|
||||
mission02,on,80,25000.0,7,4
|
||||
mission02,on,90,25000.0,7,4
|
||||
mission02,on,100,25000.0,7,4
|
||||
mission02,on,110,25000.0,9,4
|
||||
mission02,on,120,25000.0,14,4
|
||||
mission02,on,130,25000.0,16,4
|
||||
mission02,on,140,25000.0,19,4
|
||||
mission02,on,150,25000.0,19,4
|
||||
mission02,on,160,25000.0,23,5
|
||||
mission02,on,170,24730.0,28,5
|
||||
mission02,on,180,24730.0,32,5
|
||||
mission02,on,190,24730.0,36,5
|
||||
mission02,on,200,24640.0,43,6
|
||||
mission02,on,210,24190.0,46,6
|
||||
mission02,on,220,24055.0,47,6
|
||||
mission02,on,220,24055.0,47,6
|
||||
mission02,on,230,24055.0,49,6
|
||||
mission02,on,230,24055.0,49,6
|
||||
mission02,on,240,23474.5,52,6
|
||||
mission02,on,240,23474.5,52,6
|
||||
mission02,on,250,23474.5,53,6
|
||||
mission02,on,250,23474.5,53,6
|
||||
mission02,on,260,23474.5,54,6
|
||||
mission02,on,260,23474.5,54,6
|
||||
mission02,on,270,22697.0,54,6
|
||||
mission02,on,270,22697.0,54,6
|
||||
mission02,on,280,22091.2,56,6
|
||||
mission02,on,280,22091.2,55,7
|
||||
mission02,on,290,21577.9,55,7
|
||||
mission02,on,290,21555.4,55,7
|
||||
mission02,on,300,20799.1,56,7
|
||||
mission02,on,310,19781.1,55,7
|
||||
mission02,on,320,18925.8,56,8
|
||||
mission02,on,330,18592.5,56,8
|
||||
mission02,on,340,17432.5,57,8
|
||||
mission02,on,350,16932.6,59,8
|
||||
mission02,on,360,16171.6,58,9
|
||||
mission02,on,370,15017.6,58,11
|
||||
mission02,on,380,14217.2,59,11
|
||||
mission02,on,390,13322.2,59,12
|
||||
mission02,on,400,12808.9,58,13
|
||||
mission02,on,410,12090.6,61,16
|
||||
mission02,on,420,11440.6,66,19
|
||||
mission02,on,430,11127.3,55,19
|
||||
mission03,on,0,25000.0,0,0
|
||||
mission03,on,10,25000.0,3,0
|
||||
mission03,on,20,25000.0,8,0
|
||||
mission03,on,30,25000.0,5,0
|
||||
mission03,on,40,25000.0,6,0
|
||||
mission03,on,50,25000.0,8,0
|
||||
mission03,on,60,25000.0,9,0
|
||||
mission03,on,70,25000.0,16,0
|
||||
mission03,on,80,25000.0,10,0
|
||||
mission03,on,90,25000.0,11,0
|
||||
mission03,on,100,25000.0,10,2
|
||||
mission03,on,110,25000.0,15,2
|
||||
mission03,on,120,25000.0,12,2
|
||||
mission03,on,130,25000.0,10,4
|
||||
mission03,on,140,25000.0,11,6
|
||||
mission03,on,150,25000.0,14,6
|
||||
mission03,on,160,25000.0,17,7
|
||||
mission03,on,170,24685.0,24,7
|
||||
mission03,on,180,24640.0,23,7
|
||||
mission03,on,190,24640.0,27,7
|
||||
mission03,on,200,24460.0,26,8
|
||||
mission03,on,210,24100.0,28,8
|
||||
mission03,on,220,24100.0,29,9
|
||||
mission03,on,230,24100.0,28,11
|
||||
mission03,on,230,24100.0,28,11
|
||||
mission03,on,240,23810.0,35,11
|
||||
mission03,on,240,23810.0,36,11
|
||||
mission03,on,250,23630.0,36,12
|
||||
mission03,on,250,23630.0,36,12
|
||||
mission03,on,260,23630.0,38,13
|
||||
mission03,on,260,23630.0,38,13
|
||||
mission03,on,270,22998.0,39,13
|
||||
mission03,on,270,22908.0,39,13
|
||||
mission03,on,280,22197.5,40,13
|
||||
mission03,on,280,22188.5,40,13
|
||||
mission03,on,290,21602.4,38,13
|
||||
mission03,on,290,21602.4,38,13
|
||||
mission03,on,300,21042.4,39,13
|
||||
mission03,on,300,20975.8,39,13
|
||||
mission03,on,310,20529.1,41,15
|
||||
mission03,on,310,20529.1,41,15
|
||||
mission03,on,320,19812.5,38,17
|
||||
mission03,on,320,19767.5,38,17
|
||||
mission03,on,330,19434.2,39,18
|
||||
mission03,on,340,18785.9,37,19
|
||||
mission03,on,350,18339.2,37,19
|
||||
mission03,on,360,17528.1,38,19
|
||||
mission03,on,370,16901.5,37,19
|
||||
mission03,on,380,16260.7,35,19
|
||||
mission03,on,390,15708.2,37,19
|
||||
mission03,on,400,15346.2,37,19
|
||||
mission03,on,410,14412.1,36,20
|
||||
mission03,on,420,13951.9,37,22
|
||||
mission03,on,430,13211.9,35,23
|
||||
|
BIN
docs/re/captures/mission01-t485.png
Normal file
BIN
docs/re/captures/mission01-t485.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 MiB |
BIN
docs/re/captures/mission03-turretrule-t430.png
Normal file
BIN
docs/re/captures/mission03-turretrule-t430.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
181
docs/re/mission-outcome-stage02.md
Normal file
181
docs/re/mission-outcome-stage02.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Why Stage 02 is never won — the escort sinks at ~11 minutes (2026-08-10)
|
||||
|
||||
**Status: ✅ measured, one 500 s run.** The standing open item since 2026-07-29 was
|
||||
"no mission completed". This is the first session whose deliverable was the
|
||||
*ending* rather than a measurement, and it settles why: **the mission is lost
|
||||
before it can be won, and the pilot's survival policy is what guarantees it.**
|
||||
|
||||
Run: `tools/re-capture/mission_run.sh 500 mission01` — boot → Stage 02 in flight →
|
||||
`pilot.py` (escort-weighted targeting, target commitment, guided missiles) for
|
||||
500 s, with every entity's hull sampled at 2 Hz and a screenshot every 30 s.
|
||||
Artifacts at `/sylph-home/re/mission01/` (9 MB `mission.jsonl`, not committed).
|
||||
|
||||
## The escort's decay is linear, and it ends the mission
|
||||
|
||||
| t (s) | ACROPOLIS hull | % |
|
||||
|---|---|---|
|
||||
| 0–160 | 25000 | 100 % |
|
||||
| 180 | 24510 | 98.0 |
|
||||
| 280 | 20279 | 81.1 |
|
||||
| 380 | 13799 | 55.2 |
|
||||
| 480 | 8541 | 34.2 |
|
||||
| 485 (end) | 8182 | 32.7 |
|
||||
|
||||
Untouched until **t ≈ 170 s**, then **≈53 HP/s** with no let-up — so the asset
|
||||
reaches zero at **t ≈ 640 s**, and the whole-run average rate puts it at 722 s.
|
||||
Either way the escort is dead at **10–12 minutes**, and "the ACROPOLIS is sunk"
|
||||
is a defeat condition ([mission-escort-state](mission-escort-state.md)).
|
||||
|
||||
This also retires a suspicion: the 240 s time-box of earlier runs was *not*
|
||||
hiding a win, and the ~500 s ceiling of a single blocking tool call is **not**
|
||||
the binding constraint. A longer session would simply watch the loss arrive.
|
||||
|
||||
## What is actually killing it — and the conflict that follows
|
||||
|
||||
Attributing damage by co-presence (which hostiles are within 3000 units of the
|
||||
asset in the sample where its hull drops, damage split evenly among the classes
|
||||
present — suggestive, not per-shot proof), only **two** classes are ever near it:
|
||||
|
||||
| class | samples present | attributed damage |
|
||||
|---|---|---|
|
||||
| `UN_e007_ADAN_Turret` | 200 | 8483 |
|
||||
| `UN_e010_ADAN_Attacker_S` | 194 | 8334 |
|
||||
|
||||
Roughly half the damage comes from **turrets** — and `pilot.py` treats turrets as
|
||||
**keep-out zones at 2500 units, never as targets**. That rule is not arbitrary: a
|
||||
turret is what shot down every pilot before 2026-07-30, and it is why the craft
|
||||
now survives. But it means **the policy that keeps the pilot alive also
|
||||
guarantees the escort dies.** Survival and the objective are in direct conflict,
|
||||
and the pilot currently resolves it entirely in favour of survival.
|
||||
|
||||
The HUD at t≈485 s says the same thing from the game's side:
|
||||
|
||||
- `YOU KILLED WARSHIPS` **0000** — not one warship in 500 s, across every run ever;
|
||||
- `YOU KILLED WARPLANES` **0009** — fighters only;
|
||||
- `REMAINING OB` **004 → 008** — objectives are being *added* by waves faster than
|
||||
any are cleared, so the pilot is not touching the objective set at all;
|
||||
- SHIELD and ARMOR bars full, hull **1500/1500**, 120 missiles spent.
|
||||
|
||||

|
||||
|
||||
## The conclusion that matters
|
||||
|
||||
The pilot optimises the wrong thing. It maximises survival and fighter kills;
|
||||
the mission scores **objectives** and **the escort**, and the fighter population
|
||||
(134 → 92) is close to irrelevant to both. An untouched 1500/1500 hull at the
|
||||
moment the escort passes 33 % is not a good run — it is **unspent risk budget**.
|
||||
|
||||
Concretely, for the next attempt, in priority order:
|
||||
(**Step 1 below was run on 2026-08-11 and did not hold — see the follow-up A/B at
|
||||
the end of this file before acting on it.**)
|
||||
|
||||
1. **Turrets near the asset must become targets**, not keep-out zones — accepting
|
||||
hull damage is the only way to cut ~50 % of the incoming escort damage. The
|
||||
keep-out rule should be scoped to turrets that are *not* threatening the
|
||||
asset, rather than applied globally.
|
||||
2. **Engage warships.** `WARSHIPS 0000` forever means the objective class has
|
||||
never been attacked; `REMAINING OB` rising is the scoreboard saying so.
|
||||
3. Re-check whether the escort damage rate actually falls once turrets die —
|
||||
that is the experiment that tells us whether (1) is sufficient or whether the
|
||||
bombers need dedicated intercept too.
|
||||
|
||||
## Method note, learned the hard way
|
||||
|
||||
A harness-tracked **background** task does *not* protect the display: the same
|
||||
run launched in the background lost Xvfb 11 s in, at the turn boundary
|
||||
(`skip_intro` exit 3, "DISPLAY LOST"). The comment in `launch_mission.sh` saying
|
||||
the script may be run as a tracked background task is **wrong**; the
|
||||
one-blocking-foreground-call rule still stands, which caps a single attempt at
|
||||
the tool's 600 s timeout. And do not pipe a long run through `tail` — the first
|
||||
attempt printed nothing because `timeout` killed the pipeline before it flushed;
|
||||
the on-disk artifacts are what survived.
|
||||
|
||||
---
|
||||
|
||||
# Follow-up A/B (2026-08-11): making turrets targets does **not** save the escort
|
||||
|
||||
**Status: ✅ measured (3 runs), and it falsifies the causal claim above.** The
|
||||
section above ends by naming step 1 — "turrets near the asset must become
|
||||
targets" — as the fix worth ~50 % of the escort damage. That was an *inference
|
||||
from an attribution*, never a measurement. It has now been run, and it does not
|
||||
hold.
|
||||
|
||||
## First, a correction to the run labelling
|
||||
|
||||
`pilot.py` gained the `SYLPH_KILL_TURRETS` gate at 21:20:28 on 2026-08-10;
|
||||
`/sylph-home/re/mission02` started at **21:22:25**, i.e. *after* it, with the
|
||||
gate **on**. So `mission02` was never a second baseline — it is a treatment run
|
||||
that the previous session produced but never reported. The arms are:
|
||||
|
||||
| run | turret rule | evidence (`pilot.log`) |
|
||||
|---|---|---|
|
||||
| `mission01` | **off** (baseline) | `tgt=e007` on **0** of 3968 lines |
|
||||
| `mission02` | **on** | `tgt=e007` on 1588 of 3844 lines |
|
||||
| `mission03` | **on** (this session) | `tgt=e007` on 1218 of 3514 lines |
|
||||
|
||||
`mission01` predates the edit, so the *only* difference between the arms is the
|
||||
gate. Everything else — build, save slot, nav route, loadout — is identical.
|
||||
|
||||
## The result, all three runs truncated to a common t = 428 s
|
||||
|
||||
| run | rule | ACROPOLIS @428 s | % | decay rate | turrets seen | turrets killed | attack onset |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| `mission01` | off | 11735 | 46.9 % | 50.3 HP/s | 92 | 30 | 166 s |
|
||||
| `mission02` | on | 11127 | 44.5 % | 52.7 HP/s | 98 | 34 | 167 s |
|
||||
| `mission03` | on | 13257 | 53.0 % | 44.4 HP/s | 88 | 41 | 166 s |
|
||||
|
||||
**The two runs of the *same* arm differ by 8.5 percentage points (2130 HP) —
|
||||
more than either differs from the baseline.** The baseline sits *between* the two
|
||||
treatment runs on escort hull and on decay rate. So the effect of the turret rule
|
||||
is **not resolvable at n=1 per arm**, and the honest statement is: it was not
|
||||
demonstrated. Extrapolating each run's own rate, the escort still reaches zero at
|
||||
**t ≈ 590–670 s** in every arm — the mission is lost in all three.
|
||||
|
||||
The rule does do the mechanical thing it was written to do (turret kills 30 → 34,
|
||||
41; 1218–1588 frames spent with a turret as the committed target). It just does
|
||||
not convert into escort hull.
|
||||
|
||||

|
||||
|
||||
## What *is* reproducible across all three runs
|
||||
|
||||
These are the numbers to build on, because they repeat to within a percent:
|
||||
|
||||
- **The assault on the ACROPOLIS is scripted, not emergent.** Onset at
|
||||
**166 / 167 / 166 s** — ±1 s across three runs with completely different pilot
|
||||
behaviour in between. Nothing the pilot does moves it.
|
||||
- **The damage split is a property of the scenario, not of our targeting.**
|
||||
`e007` turrets vs `e010` bombers is **50.6/49.4, 50.0/50.0, 51.0/49.0** — and
|
||||
the baseline, in which we never fired at a turret at all, splits the same way.
|
||||
A co-presence attribution that is invariant to whether we attack one of the two
|
||||
classes is measuring the wave script, not our contribution.
|
||||
- **Turrets die anyway**: 30 of 92 are already dead by t=428 s in the baseline,
|
||||
from friendly fire. The turret rule adds ~4–11 kills on top of that, which is
|
||||
why its escort-hull effect is small enough to be buried in run-to-run noise.
|
||||
- `WARSHIPS 0000` and `REMAINING OB` rising (004 → 008) in **every** run,
|
||||
including this one.
|
||||
|
||||
Per-run series: [`captures/escort-decay-ab.csv`](captures/escort-decay-ab.csv)
|
||||
(10 s grid, hull + turret alive/killed counts for all three runs).
|
||||
|
||||
## The methodological finding, which is the transferable one
|
||||
|
||||
**A single 430 s flight cannot resolve an escort-hull effect smaller than ~9
|
||||
percentage points.** Every pilot conclusion drawn from one run — including step 1
|
||||
above, and including anything drawn from `mission03` alone — is inside the noise.
|
||||
Each run costs ~10 minutes of wall clock and one blocking foreground call, so a
|
||||
properly powered A/B is ~6 runs ≈ 1 hour. **Any future pilot tuning must budget
|
||||
that, or not be believed.**
|
||||
|
||||
## What this means for the reason we wanted a win
|
||||
|
||||
Story progress was wanted only because unit definitions instantiate per stage, so
|
||||
unit-field coverage is stuck at 21/110 ([unit struct](structures/unit-struct-runtime.md)).
|
||||
This result says the pilot route to that coverage is expensive and unproven: the
|
||||
escort dies on a script at ~10–11 minutes, our best lever moved it by less than
|
||||
the noise, and the objective class (`WARSHIPS`) has never been touched in any run
|
||||
ever. **Before spending another hour on pilot tuning, the cheaper lever to price
|
||||
is the save file** — if the profile's save data can be read and understood, stage
|
||||
unlock state is a much shorter path to the same coverage, and the save format is
|
||||
itself something the reimplementation needs. Marked `NEEDS-HUMAN` as a direction
|
||||
choice; no save-file modification has been attempted.
|
||||
@@ -19,7 +19,9 @@ alive(){ ps -o pid=,stat= -C xenia_canary 2>/dev/null | awk '$2 !~ /^Z/ {print $
|
||||
# that actually bought was the opposite: a process nothing owns is a process
|
||||
# nothing keeps alive, and both were being reaped a couple of minutes in — the
|
||||
# long-standing "Xvfb and the emulator die on their own every few minutes" note.
|
||||
# Run this whole script as ONE tracked background task and leave Xvfb, openbox
|
||||
# MEASURED WRONG 2026-08-10: a harness-tracked BACKGROUND task does not protect
|
||||
# them either — the display was lost 11 s in, at the turn boundary. Run this
|
||||
# whole script as ONE BLOCKING FOREGROUND call and leave Xvfb, openbox
|
||||
# and xenia as its children: they then live exactly as long as the session does.
|
||||
# `nohup` still shields them from a stray HUP; the exit-status wrapper means a
|
||||
# death is reported with the server's own account instead of being inferred.
|
||||
|
||||
60
tools/re-capture/mission_run.sh
Executable file
60
tools/re-capture/mission_run.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
# Attempt to COMPLETE a mission and record how it ends.
|
||||
#
|
||||
# Every previous flight session was time-boxed to 240 s to measure something
|
||||
# (escort hull, lethality, ship placement) and none ever reached a mission
|
||||
# outcome — "no mission completed" has been the standing open item. Unit
|
||||
# definitions are instantiated per stage, so story progress is the only thing
|
||||
# that grows unit coverage past 21/110, and that needs a WIN, not a survival.
|
||||
#
|
||||
# So this run is deliberately long and its only deliverable is the ENDING:
|
||||
# screenshots throughout, every entity's hull sampled, and the pilot log kept
|
||||
# whole (never tail-piped — a frozen log tail is what mission-end looks like
|
||||
# from outside, and tailing throws away the transition).
|
||||
#
|
||||
# Runs as ONE tracked background task with Xvfb/openbox/xenia as plain nohup
|
||||
# children — see docs/re/session-lifetime notes; do NOT setsid anything.
|
||||
#
|
||||
# Usage: mission_run.sh [flight_seconds] [tag]
|
||||
set -u
|
||||
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
|
||||
export PYTHONPATH=/sylph-home/.local/lib/python3.12/site-packages
|
||||
SD="$(cd "$(dirname "$0")" && pwd)"
|
||||
SECS="${1:-900}"
|
||||
TAG="${2:-mission}"
|
||||
SHOTS=/sylph-home/re/shots
|
||||
OUT="/sylph-home/re/$TAG"
|
||||
mkdir -p "$SHOTS" "$OUT"
|
||||
|
||||
"$SD/launch_mission.sh" fly || { echo "BOOT FAILED"; exit 1; }
|
||||
python3 "$SD/entities2.py" self 0x130 "$OUT/cfg.json" || { echo "BIND FAILED"; exit 1; }
|
||||
|
||||
echo "=== initial entity table ==="
|
||||
python3 "$SD/mission_state.py" scan "$OUT/cfg.json"
|
||||
|
||||
# A screenshot every 30 s for the WHOLE run: the outcome card (MISSION COMPLETE
|
||||
# / GAME OVER) is on screen only briefly, so sampling must not stop early.
|
||||
( n=$(( SECS / 30 + 4 ))
|
||||
for i in $(seq 1 "$n"); do
|
||||
printf '%s SHOT %03d\n' "$(date +%s)" "$i" >> "$OUT/shots.log"
|
||||
screenshot "$SHOTS/$TAG-$(printf %03d "$i").png" >/dev/null 2>&1
|
||||
sleep 30
|
||||
done ) &
|
||||
SHOTTER=$!
|
||||
|
||||
date +%s > "$OUT/t0"
|
||||
python3 "$SD/mission_state.py" watch "$OUT/cfg.json" "$SECS" 2 "$OUT/mission.jsonl" \
|
||||
> "$OUT/mission.log" 2>&1 &
|
||||
WATCHER=$!
|
||||
|
||||
SYLPH_KILL_TURRETS="${SYLPH_KILL_TURRETS:-0}" python3 "$SD/pilot.py" "$OUT/cfg.json" "$SECS" > "$OUT/pilot.log" 2>&1
|
||||
PILOT_RC=$?
|
||||
wait $WATCHER 2>/dev/null
|
||||
kill $SHOTTER 2>/dev/null
|
||||
screenshot "$SHOTS/$TAG-end.png" >/dev/null 2>&1
|
||||
cp -f "$SHOTS/$TAG-end.png" "$OUT/end.png" 2>/dev/null
|
||||
|
||||
echo "PILOT_RC=$PILOT_RC"
|
||||
echo "--- last 5 pilot lines ---"; tail -5 "$OUT/pilot.log"
|
||||
echo "--- shots: $(ls "$SHOTS/$TAG-"*.png 2>/dev/null | wc -l) ---"
|
||||
echo "MISSION RUN DONE ($TAG, ${SECS}s)"
|
||||
@@ -100,6 +100,18 @@ MISSILE_PERIOD = 2.0 # s between launches; 300 rounds is not unlimited
|
||||
# configuration here — override with $SYLPH_ASSET for another stage.
|
||||
ASSET_NAME = os.environ.get("SYLPH_ASSET", "Acropolis")
|
||||
|
||||
# Turrets are keep-out zones everywhere else in this file, and that rule is what
|
||||
# stopped the pilot being shot down. But the 2026-08-10 outcome run measured the
|
||||
# cost of it: of the damage that sinks the ACROPOLIS, roughly half comes from
|
||||
# e007 turrets sitting 1.5-3 km off it (the other half from e010 bombers), and a
|
||||
# turret is 100 HP -- one missile, or ~7 nose-gun hits. Several were already at
|
||||
# 45-95 HP from stray fire and were never finished off, because nothing ever
|
||||
# targets them. So the rule that keeps us alive is also what loses the escort.
|
||||
# Gated rather than simply changed, so the measured baseline stays reproducible:
|
||||
# SYLPH_KILL_TURRETS=1 lets DEFEND -- and only DEFEND -- treat a turret that is
|
||||
# near the asset as a target. ENGAGE still avoids them.
|
||||
KILL_TURRETS = os.environ.get("SYLPH_KILL_TURRETS") == "1"
|
||||
|
||||
|
||||
class Pilot:
|
||||
KP, KD = 2.2, 0.45
|
||||
@@ -230,7 +242,9 @@ class Pilot:
|
||||
"""
|
||||
out = []
|
||||
for off, nm, p, v, r, hard in hos:
|
||||
if hard:
|
||||
# A turret near the asset IS a killable objective (100 HP) when
|
||||
# SYLPH_KILL_TURRETS is set; a capital hull never is.
|
||||
if hard and not (KILL_TURRETS and "Turret" in nm):
|
||||
continue
|
||||
rel = a_p - p
|
||||
d = float(np.linalg.norm(rel))
|
||||
@@ -520,9 +534,12 @@ class Pilot:
|
||||
self.set_throttle(0)
|
||||
fire = True
|
||||
|
||||
# a turret inside its keep-out radius outranks the target
|
||||
# A turret inside its keep-out radius outranks the target -- except the
|
||||
# one we are deliberately attacking, or the keep-out would steer us off
|
||||
# the very thing we chose to kill and neither goal would be served.
|
||||
tgt_off = tgt[0] if tgt else None
|
||||
for off, nm, p, v, r, hard in hos:
|
||||
if not hard:
|
||||
if not hard or (KILL_TURRETS and off == tgt_off):
|
||||
continue
|
||||
d = float(np.linalg.norm(p - me_p))
|
||||
if d < self.TURRET_KEEPOUT:
|
||||
|
||||
Reference in New Issue
Block a user