Autopilot · write-up
Level 2 connector ownership and physical retreat recovery
This work follows the unchanged merged-build validation in LEVEL2_MERGED_VALIDATION_20261002.MD. The earlier lip fix was present after the merge. It covered a particular first retreat waypoint; it did not solve the navigation/arbitration problem generally. All gameplay changes below are in C. Python runs and audits the resident pilot. No native ABI or mission-state layout changes are required.
Confirmed causes
- Corridor pacing replaced the retained connector's X goal with its preferred lane. The screen band also prevented a necessary downward/backscroll leg. A tiny projected first anchor could conceal the retreat later in that route.
- The occupied-start guard expected one movement to leave terrain entirely. An escape requiring several monotonically improving movements was rejected. Captured player world Y and the current camera can also differ by one update; comparing different phases produced a false worsening of wall overlap.
- An authored forward backbone point could already be behind the ship but far away horizontally. The controller kept chasing that old point instead of reconnecting to the next forward destination.
- A four-pixel span waypoint and the actual ship anchor are not interchangeable. A small projection may be skipped, but a real retreat row must be reached. A lateral leg accepted by the rounded graph can still clip a physical corner.
- The local planner can select a safe hold or retreat that never advances the connector. A route proposal is therefore not proof of the final movement. Repeatedly restoring the lane or eroding the backscroll window can make the mandatory crossing unreachable.
- Applying horizontal-first throughout a lateral crossing suppresses all world-Y corrections. The camera continues scrolling, so a ship aligned at the start drifts into the upper wall. A lateral leg must retain both-axis feedback; vertical-first is needed only until a required retreat reaches its row.
Implemented rules
- Retain the complete connector and inspect its remaining retreat legs. When a terrain detour is needed, corridor lane and screen-band preferences yield.
- Skip passed forward backbone anchors only in the existing paced Level 2 passages. Preserve maze waypoint order and Level 3's intentional backtracking.
- Follow ordinary Level 2 corridor doglegs with a four-pixel horizontal tolerance and a vertical-first retreat. Complete the actual retreat row before crossing.
- During a lateral leg, allow world-Y corrections as well as horizontal travel. Do not suppress them for the whole leg while the camera is advancing.
- Recover using the shared ship/camera forecast and physical wall queries. A blocked origin may move only along a wall-safe escape trajectory. A lateral connector may first align its physical row if rounded navigation misaligns it.
- Certify a recovery against both terrain and the current body/projectile hazard forecast before replacing a planner move. Ordinary forward dodges remain under planner control; the Level 2 hazard arbitration still runs afterwards.
- Do not reuse a retained maneuver while the mission requires a terrain detour. The corrected connector must be reconsidered from the new observation.
The main implementation is in xenon_autopilot_level2.c,
xenon_autopilot_mission.c and xenon_autopilot_driver.c. The earlier
world-Y 4096..4208 special-case exemption is removed. There are no new canonical
frame-number conditions, special replay identities, or waypoint blobs.
Validation and rejected intermediate versions
Both Hatari Release and the replay DLL are built with
xenon_tools/hatari_dev.ps1. Runs use validate_campaign.py native-run, a visible
emulator, fast-forward, both AVIs, 150-frame checkpoints and XAP_LEVEL2_TRACE_FILE.
The final targeted mission/driver/session suite passes 26 tests, including a
24-frame C maneuver forecast with camera scrolling during a lateral crossing.
The broader suite passes 148 of 152 tests. Its four failures were also reproduced
with the unchanged merged baseline: early pickup priority and three tests
expecting the superseded upper-pocket eye strategy.
Generated replay descriptions pass generate_replay_descriptions.py --check.
Intermediate recordings are diagnostic evidence, not final success claims:
Recording stem under E:/xenon_runs/<stem>.validation/ |
Problem and conclusion |
|---|---|
level2-route-progress-full-20261002-r181 |
The early corridor had no shield decrease, but a permissive projected-anchor skip discarded the real (212,3252) retreat row and stalled near (211,3243). Restrict projection skipping; retain real retreat rows. |
level2-route-all-corners-20261002-r182-3 |
A post-shop checked route could still start sideways before finishing its backward leg and repeat wall rollback. The route must complete its retreat row. |
level2-route-final-full-20261002-r184 |
Cleared the known early/post-shop traps and reached Level 3, but lost a life at 9932. The provisional recovery could replace a clear forward planner dodge. It was rejected. |
level2-route-recovery-full-20261002-r185 |
No life loss; cleared early/post-shop traps, then lost the lower maze crossing's backscroll window. Stationary stall reported at 15107, beginning near 14208; recording stopped at 17212. |
level2-maze-route-20261002-r187-1 |
Stationary stall at 15148 near (284,1102) despite the partial maze connector changes. |
level2-maze-route-20261002-r187-2 |
Stationary stall at 15497 near (261,1163). Rounded row arrival and a safe nonprogressing maneuver were insufficient. |
The r187 report incorrectly said route_passed=true: the first version of the
helper omitted the monitor's stalled incident kind. The helper now rejects all
stall kinds and optionally requires an explicit world-space exit row. That old
report does not establish a successful route.
Reusable checkpoint validation
xenon_tools/validate_level2_route_checkpoints.py launches each checkpoint through the
campaign validator, closes its owned emulator after AVI finalization, and reports
raw observed progress and incidents. It never evaluates a Python autopilot.
python xenon_tools/validate_level2_route_checkpoints.py <checkpoint1.sav> <checkpoint2.sav> `
--name level2-route-review --output-root E:/xenon_runs --frames 1200 --target-y 1000
Choose checkpoints by recorded player coordinates and encounter state, not by assuming frame numbers have the same meaning across runs. Route completion, shield damage, lives, equipment and boss rewards are separate acceptance facts.
Final validation
All seven final runs used the same C sources and the official Release builds of Hatari and the DLL. Each folder contains the event recording, original and sprite AVIs, Level 2 trace, manifest, incident log and checkpoints. AVI audits confirmed continuous VBL indices and successful first/last-frame decoding. All owned Hatari instances were closed after recording finalization.
For the table below, each recording is
E:/xenon_runs/<stem>.validation/<stem>.x2events. Coordinates are observed world
coordinates. A bounded route check establishes escape/progress through that
passage; it does not establish no-damage completion of the whole level.
| Recording stem | Tested start → final position | Result |
|---|---|---|
level2-route-all-corners-final-20261002-r190-1 |
Post-shop trap (97,2397) → (222,1975) |
No stall, damage or life loss. 96px forward progress by 12641. |
level2-route-all-corners-final-20261002-r190-2 |
Approach corner (220,3243) → (66,2744) |
No stall, damage or life loss. 96px forward progress by 10410. |
level2-route-all-corners-final-20261002-r190-3 |
Merged-build trap (95,4163) → (156,3813) |
No stall, damage or life loss. 96px forward progress by 9302. |
level2-route-all-corners-final-20261002-r190-4 |
Original lip trap (138,4137) → (100,3815) |
No stall or life loss; 4-point hits at 9117 and 9233. 96px forward progress by 9065. |
level2-maze-row-tracking-20261002-r189-1 |
Before maze (282,1295) → spider approach (14,192) |
Exit row passed; no stall or life loss. Hits at 13985 (4 points) and 14447 (8 points). |
level2-maze-row-tracking-20261002-r189-2 |
Alternate maze start (227,1335) → spider approach (14,178) |
Exit row passed; no stall or life loss. Hits at 14697 and 15132 (8 points each). The save already had two lives; neither was lost. |
Whole Level 2
Recording:
E:/xenon_runs/level2-route-full-confirmation-20261002-r191.validation/level2-route-full-confirmation-20261002-r191.x2events.
Started from
D:/src/hatari/xenon_tools/run_logs/native-level2-full-20260916.validation/checkpoints/L2-f8136-start.sav.
Reached Level 3 and stopped at 16837 (final recorded frame 16838), with no monitor
stall or life loss. The three-eye boss cleared at 11349; the spider cleared at
15386. The spider check is restricted to captures after the first shop, because
the earlier arena also uses a boss controller and must not count as the spider.
The player started with shield 39 and three lives, and ended Level 2 with shield 27 and three lives. There were nine damage incidents totaling 52 shield points; health recovery explains why this exceeds the final net shield decrease. This is a successful navigation/completion check, not a clean gameplay result.
Every frame below refers to the r191 recording named above. Sources are recorded collision callbacks, rather than guessed from a nearby object.
| Frame | Damage | Recorded source / problem |
|---|---|---|
| 9082 | 4 | Directional projectile #17583 hit the ship in the first corridor. |
| 9088 | 4 | Directional projectile #17611 hit the ship shortly afterwards. |
| 9578 | 4 | Directional projectile #19651 hit the ship in the following passage. |
| 9579 | 4 | Directional projectile #19645 caused a second immediate hit. |
| 10059 | 8 | Scripted sine-motion body #21528 (0x0e1e) contacted the ship before boss entry. |
| 13314 | 12 | Homing enemies #31013 and #31007 caused body/point-test damage. |
| 13315 | 8 | Contact with #31007 was still reported after its procedure changed to the destroy/unlink thunk. |
| 13921 | 4 | Directional projectile #33570 hit the ship before the maze crossing. |
| 15249 | 4 | Directional projectile #40078 hit the ship during the spider encounter. |
Pickup audit:
- POWERUP
#22583was collected at 10321; captured Double Shot power increased from 0 to 1. SIDE SHOT#32737was collected at 13750, replacing Rear Shot as intended. No life-related attachment loss occurred. - HEALTH POWER 1
#17396was missed at 9077 (closest observed gap 4px). ZAPPER#31265was missed at 13388 (closest gap 22.47px). Another health pickup was collected at 13685. - Of the spider's 20 cash drops, the passive audit estimates 17 collected and three missed. Across the whole level it estimates 54 cash pickups collected and 31 missed. These are disappearance/proximity estimates; there is no explicit collection event in the capture.
The C shop report records shop 1 at 11410–12376: cash 2100 → 1600, no attachment
change, with shield recovery. Shop 2 at 15497–16470: cash 4400 → 400; Laser was
added at 16091. The report is
work/l2-source-comparison/r191-shops.md; full collision/pickup/AVI evidence is
work/l2-source-comparison/r191-audit.json.
Limits and next investigation
The six different trapped starts and the full run establish that the tested routes now make progress. They do not establish a universal absence of stalls or combat regressions. One earlier provisional version escaped the original lip without damage; the final version's r190-4 took two hits. The unchanged merged full run stalled before later encounters, so it cannot establish a whole-level damage baseline. A saved baseline post-shop run finished without damage; the new full run's later hits require separate combat comparison.
The next diagnostic should start with the 20-point homing contact at 13314–13315, then the early corridor's directional shots. Compare forecasts and the final arbitration trail against recorded collision sources; do not restore the rejected blanket override or alter spider tactics to repair corridor navigation. The current fix contains no new boss phase rules.