Xenon 2

Autopilot · write-up

Three native campaign attempts — 2026-10-04

xenondoc/FULL_NATIVE_CAMPAIGN_THREE_20261004.MD · 15 KB · updated 2026-10-05

Result: 0/3 completed the game. All three started at the beginning, passed Levels 1–3 and the first Level 4 boss, then stopped at the same persistent post-shop Level 4 navigation stall. There were no lost lives. Each attempt took 32 shield points of damage: 28 in Level 2 and 4 in Level 3. Level 4's final boss and Level 5 were not reached by these campaigns.

These are repeatability tests of the same initial RAM state, not three different random seeds or loadouts. The damage, pickup misses, shop decisions and reward outcomes match across all three. The few extra terminal frames differ because the passive monitor and graceful recording shutdown run outside the emulator.

No gameplay tactics were changed during this comparison. The new scripts launch the existing resident C pilot and audit captured events; they do not run the old Python pilot.

Recordings and build

Run Recording Last captured frame Stop reason
R1 campaign-three-full-20261004-r306-1.x2events 37091 Persistent Level 4 stall
R2 campaign-three-full-20261004-r306-2.x2events 37094 Persistent Level 4 stall
R3 campaign-three-full-20261004-r306-3.x2events 37096 Persistent Level 4 stall

Each recording's directory contains both -original.avi and -sprite.avi, their VBL indexes, start/final RAM and saves, periodic checkpoints, incident logs, manifest and working-tree patch. All six AVIs passed the chunk and video-index audit, including their multi-RIFF structure. Indexed frames per AVI: R1 127542, R2 127557, R3 127565. Shutdown finalized recordings without cleanup errors; no Hatari processes from this batch remain.

The first run checked/built Hatari Release and the native DLL through xenon_tools/hatari_dev.ps1, invoked by validate_campaign.py native-run --build. All three ran visible, fast-forward, without cheats, using assets/xenonplay.sav, paired AVIs, a configured checkpoint interval of 150, and memory tracing off. The manifests identify resident-c-autopilot, hatari-c and per_frame_controller_calls=false.

  • Source base: e5adb087814f94ded0cba543a7b70b850cc65a88, plus the existing uncommitted Level 5/rendering work saved in each run's patch.
  • Hatari SHA256, identical in all three: a0e73013b544dc8c23ba91e06240f2e3653354dcf5acaaeaf952e814558a96ea.
  • Starting save SHA256, identical in all three: 651d0f8931aa3ce5b5a9cd89aacda5fa7dc6f6aef1ef6792d58dcf1d32a90439.
  • Current freshly built replay DLL SHA256: a22a1e4a6118ec0e980f76460dafc0ed1a332f812d3a99e901ddbd4db1bffcf4.

Build identity, batch manifest, AVI audit.

Incidents common to all three recordings

The following frame numbers apply to R1, R2 and R3 above. Coordinates are the ship's captured world position after the update; hit records also retain the exact collision-time position and bounds.

Frame Level Shield loss Ship world position One-sentence description
8011 2 4 (219,4012) Moving left from the right wall crossed right-moving directional projectile #15196, handler $4180.
8231 2 4 (191,3776) A left/up correction intersected right-moving directional projectile #16031, handler $4180.
12191 2 4 (267,1561) Homing enemy #28377 reached the bottom-right holding position before the ship began its up/left escape.
12192 2 8 (267,1555) The same #28377 inflicted a second collision during its unlink transition, bringing this encounter's loss to 12.
12923 2 8 (116,876) The ship moved right across diving swarm member #31572 after its rapid descent and turn, handler $50346.
21557 3 4 (76,1688) Holding DOWN at the bottom edge left the ship beside the falling projectile #64210; its upward escape started on the hit frame.
34076 onward 4 0 (229,1980) The ship stopped advancing in the right pocket after the first shop; the monitor reported the stall at 34975.

The terminal persistent-stall stop occurred about frame 37090. The monitor was configured to continue after initial damage/stall reports; these are not runs that stopped at their first shield hit.

What the movement evidence establishes

The two Level 2 directional projectiles moved +6 X per update, with the screen Y change attributable to scrolling. The Level 3 projectile remained at X=64 and descended regularly. There is no observed erratic direction change to explain these hits. In both Level 2 cases the ship moved toward an incoming shot during a correction; terrain rollback appeared earlier in the approach. This suggests checking the chosen maneuver, hull timing and escape availability before changing the projectile motion model. It does not prove the C forecast was correct.

At 12180–12190, the ship stayed at screen (276,176) while #28377 approached from X=216 to X=264. Escape began at 12191, already too late. The second hit is a real recorded game collision, not a duplicate report: shield fell 39→35→27, and the same identity changed from $50798 to $E54 between the two collision records. Any repair must cover this contact lifecycle as well as the approach.

At 12917 the ship was safely left of the diving enemy, at screen (62,176). It then moved right to X=116 while the enemy descended at X=106 and turned to X=112. The harmful transition is the early rightward release across this enemy's path, not the three-eye or spider boss logic.

Captured movement around every hit.

Level 4 navigation diagnosis

This stall is in the second-part bend, not either boss fight. The original AVI shows the ship below the right rock lip, with open space on the left.

R1 original AVI at frame 34975

At 34975 the player is at screen (229,176), scroll 1804, world Y=1980. The backward scroll limit is 1811: only seven pixels of backward camera movement remain. The wall replay flag is zero. There is no continuing damage, meaningful movement or score increase during the stall. A proposed retreat must respect that actual camera allowance; simply asking for an arbitrarily lower waypoint does not establish that the ship can reach it.

Two relevant code facts:

  1. route_second_bend() in src/autopilot/xenon_autopilot_level4.c owns world Y (1824,2016], aims for X=96, but returns immediately when player X>128. Consequently it never acquires this right-side arrival at X=229.
  2. xap_mission_advance() in src/autopilot/xenon_autopilot_mission.c tries the preferred centre point using travel and physical graphs. If both connectors fail, it clears the route and supplies a same-X cruise goal. The Level 3 branch already searches a reachable forward region; Level 4 does not. Repeatedly asking to climb at the same X can leave the pocket without any progress-producing connector.

A current-DLL C-session reconstruction from the synchronized R1 frame-34856 checkpoint found an empty route and the fallback goal X=[225,233], world Y=[1915,1939]. This supports the connector-failure mechanism. It is recomputed evidence, not the exact retained live decision: its action was DOWN while the recorded packet input was FIRE-only. The recording does not contain the live mission/arbitration state needed to attribute that difference to a specific override.

C resume probe.

Pickup and cash comparison

Both Level 2 bosses and the reached Level 3/4 bosses were defeated. No life or installed weapon was lost in gameplay; equipment replacements/upgrades match the recorded pickups and shop changes. The following misses repeat in all runs.

Pickup Level Visible lifetime Closest frame / rectangle gap Assessment
Zapper 2 11856–11929 11895 / 29.41 px Audit the safe intercept during the homing encounter; its benefit depends on which enemies are present at collection time.
Side Shot #100911 4 30037–30123 30092 / 12.21 px Valuable missed upgrade: the installed Side Shot was power 0, below maximum.
Health Power 1 3 16277–16347 16302 / 18 px Shield was 31, so this could help if the detour is safe.
Health Power 2 3 22019–22103 22028 / 25.63 px Shield was 35; consider a safe short recovery detour.
Health Power 2 3 24901–24970 24965 / 30 px Shield remained 35; the safe-intercept question needs a local check.

Three further missed health pickups at frames 4280–4356, 7785–7845 and 28648–28761 had their closest approaches with shield 39; they are not equivalent to missed useful equipment. Rear Shot skips that preserve Side Shot are allowed. The full audit preserves every pickup's identity, retirement evidence and distance; collection without an explicit event is labelled likely_collected, not silently asserted.

Large simultaneous cash drops observed around boss completion:

Level / drop frame Observed cash items collected Observed value collected / available
L1 / 5835 17/18 1300/1350
L2 three-eye / 9933 10/10 750/750
L2 spider / 14125 20/20 1500/1500
L3 / 18502 9/9 650/650
L3 / 25963 20/20 1500/1500
L4 first boss / 32682 9/9 700/700

The L1 miss was small cash #11908, which expired at frame 5886 near screen (166,203). Reward-burst grouping is based on captured simultaneous lifetimes. An item collected on its allocation update may have no active capture, so these are observed totals, not a claim about the exact number originally generated.

Across reached gameplay, 196/316 observed cash lifetimes were collected, worth 13950/22250. Most missing cash was outside boss rewards. This does not mean all of those detours were safe, and the totals are not a replacement for the game's shop-credit ledger.

All seven shops match. Significant equipment changes were Double Shot at 6503, Laser at 14827, Laser power 0→1 at 19109, Drone at 26643 and Drone power 0→1 at 33336. Full loadouts, cash changes and decisions are in R1 shops, R2 shops and R3 shops.

Proposed fixes, in priority order

  1. Unblock the Level 4 bend locally. Acquire the left passage while its connector is still reachable after the shop, including right-side arrivals. Use live terrain and camera limits to choose the staging connector; retain its lateral/backward leg until reached. Do not just remove the X guard and assume a direct diagonal to X=96 is safe. Also make a failed navigation connector explicit instead of treating a same-pocket cruise goal as progress. The encounter route belongs in Level 4; a generic progress/failure contract should apply to navigation elsewhere only after targeted regression checks.
  2. Repair the Level 2 homing/diving escapes. These account for 20/32 damage per run. Reproduce the last safe hold and the release into contact, then compare the C forecast's exact frame origin, consumed input, banking hull and enemy lifecycle against captured RAM. Correct a demonstrated shared-model defect generically; otherwise change only the homing staging and lower-maze release rules. Keep an escape lane before the bottom/right clamp, and do not release a held crossing while the diver still sweeps through it. Preserve both currently clean Level 2 boss fights.
  3. Resolve straight-projectile contacts in the shared safety path. Use the three source identities above as fixtures. Verify that the next real update, including wall rollback and changing hull, is checked before accepting a route correction or edge hold. If the prediction is accurate and all exits are blocked, fix the encounter's earlier source attack/staging locally. A stronger global avoidance weight or blanket downward hold is not yet a justified repair.
  4. Retain safe useful pickup intercepts. Start with the missed Level 4 Side Shot upgrade, then the Level 2 Zapper and damaged-shield Level 3 pickups. Recheck the whole intercept and its egress, not only the pickup's current position. Do not chase full-shield health or replace Side Shot with Rear Shot.
  5. Collect the last L1 reward coin. Keep the post-boss sweep until the remaining reachable reward is collected or expired, using its trajectory. Existing Level 2/3/4 reward sweeps worked in these runs and should remain unchanged while repairing this local miss.

For each repair, first replay several checkpoints before the failed transition, including both entry sides/loadouts where relevant, then run the complete level. After the Level 4 blocker is removed, repeat campaigns to establish coverage of its final boss and Level 5. Use matching Hatari/DLL builds and actual live C decisions; a cold replay reconstruction is diagnostic, not a substitute for a live acceptance run.

Diagnostic limitation found during the audit

validate_game_model.py passed input (989 checks), ship (989), camera (1001) and damage (3) over frames 12000–13000. Its 24 reported homing failures are invalid first-capture comparisons: it assumes any raw record at least 196 bytes has a trailing 98-byte pre-update snapshot. A normal first capture can instead be 98 bytes of object data plus 128 bytes of script capture, without pre-state. For example, #27845's 226-byte record at 12014 has post-age 27 but no pre-age; its next 324-byte record has actual pre-age 27 and post-age 28.

This checker must use the actual capture layout/presence before those failures can justify a gameplay prediction change. The independent damage records and movement evidence above remain valid.

Reusing the validation

From D:\src\hatari, with a fresh output prefix:

python xenon_tools\validate_campaign_repeats.py campaign-new-name --build

The script calls validate_campaign.py native-run sequentially, sets Level 2/5 trace files, records both AVIs, keeps incidents/checkpoints and verifies the native binary did not change between attempts. Completion, game-over or a persistent stall terminates an attempt; initial damage does not.

To regenerate the passive comparison:

python xenon_tools\report_campaign_comparison.py E:\xenon_runs\campaign-three-full-20261004-r306-1.validation\campaign-three-full-20261004-r306-1.x2events E:\xenon_runs\campaign-three-full-20261004-r306-2.validation\campaign-three-full-20261004-r306-2.x2events E:\xenon_runs\campaign-three-full-20261004-r306-3.validation\campaign-three-full-20261004-r306-3.x2events --output xenondoc\campaign-three-full-20261004

Comparison JSON, R1 audit, R2 audit, R3 audit.