Xenon 2

Autopilot · write-up

Level 5 final ship: damage sequence and firing lanes

xenondoc/LEVEL5_FINAL_BOSS_REVA.md · 26 KB · updated 2026-10-01

September 30 corrections and current C implementation

The rebuilt resident C pilot defeated the full 18-mount encounter and collected all 20 cash drops in E:\xenon_runs\level5-final-progress-loot-r101.validation\level5-final-progress-loot-r101.x2events. All mounts were removed at frame 83090, the core at 83198, and every coin was collected by 83304. The shop opened at 83308. Shield fell 27→25 from one directional projectile, #628635, at 82680; there was no lost life. Both original and sprite AVIs were finalized and checkpoints were recorded every 150 frames. This is a low-damage victory, not a zero-damage claim.

The previous full-entry recording, level5-final-verified-r100.validation, removed the core at 85029 but collected only five of twenty coins. From activation at 82110, the new fight takes 1,088 rather than 2,919 game frames, approximately 63% less time. Both runs start from the same snapshot and lose two shield. The new cash audit confirms ten large coins worth 100 each and ten small coins worth 50 each: 1,500 collected, compared with 250 previously. Captured cash increases from 400 to 1,900 before the shop transition; no coin expires.

The finalized original AVI contains 5,024 indexed frames, and the sprite AVI contains 5,018. Both decode their first and last frame, and each RIFF header's declared size matches its file size. The original frame at 83304 shows the boss and its coins gone, with the player at the bottom center and 25 shield.

Causes of the reported pauses and cash loss

The frame references below describe r100, not the shorter r101 replay:

  • Near 82673 and 83413, the lane search rewarded damage only to its selected flank target. Damage to a reachable opposite-side arm or cannon had no value. It also accepted a retained safe path with eventual damage even when its next action was an idle wait far from the firing row. These two rules suppressed useful short vertical moves between incoming rounds. The search now rewards all mounted-part damage, weighting cannon damage twice arm/joint damage, and reconsiders idle retained paths off the firing row.
  • Near 84350, predicted future minions had no combat target/health. They were treated as indestructible hazards even when forward weapons could destroy them during a descent. ReVa confirms that $50076 initializes minion health from $4F052 to four. Each known future allocation now has a trial-only four-HP combat target; its bounds appear at allocation, and ordinary bullet simulation can kill it before contact. Its health is reset between rollouts. It creates no fabricated observed object or Python bridge.
  • Cannon #625730, previously left alive until 84950, can take side-shot damage from the opposite flank. In r101, it and #625729 both take damage during 82762–82766. The remaining right cannon dies at 83090. The arm #625734 dies at 82595, and cannon #625726 at 82946. These are measured HP changes, rather than a claim based solely on the intended goal.
  • After the core died, its controller disappeared and generic level progression resumed, sending the player away from the falling cash. The encounter now retains a collection phase until its cash is collected or gone. It holds near screen X=162, Y=172–176 below the convergence point and makes short horizontal corrections for coins already falling toward that row. It does not chase orbiting coins or let those opportunistic scores replace this lane.

The remaining incident in r101 is 82680, a two-point hit from directional round #628635. Its recorded rectangle is X=20..26, screen Y=172..178; the player's is X=4..25, Y=166..186. There are no minion hits or lost lives in this full-entry replay. Other levels were not replayed for these encounter-local changes.

The earlier experiment notes below are history, not the current tactic. The resident pilot now uses the separate xenon_autopilot_level5_final.c controller and an encounter-local lane search. It removes the exposed rear joint, chooses a cannon on its current flank, and works on the other mounted parts after the cannons. It retains the current cannon while fighting on that flank; an escape out of the flank clears that target, so the next attack chooses the nearest exposed cannon row. A separate identity that persisted through every escape proved harmful: it kept trying to return to a lower gun while minions accumulated. The earlier 32px row-switching rule also left wounded lower guns behind. It crosses above the hull at world Y=64 when already alongside it, or below at Y>=398 when already behind the stern. All 18 mounts must die before the core can take damage; the root controller's health must then reach zero.

Verified original-code details that the earlier forecast missed:

  • $5025E minions are screen relative: they do not apply $CD8 camera compensation. They turn one octant every eight updates; an exactly opposite target turns in the negative direction. They do not expire at age 80 or at the viewport edge. Their speed doubles after $D90 reaches zero. The C forecast now implements these rules and the two animation collision boxes. The animation table starting at $5017E uses two updates per sprite, including after a direction change; the old one-update forecast advanced it too quickly.
  • The root opens a tube by advancing its signed phase at offset $28 by two. Phase +40/-40 allocates a minion; +60/-60 closes the tube. Once an opening is observed, its next allocation is known up to 19 frames ahead. The upper allocation uses root+(119,101), the lower root+(119,245). The newborn moves upward in that same object pass: captures first show age two, five pixels above its allocation point. Its forecast has a four-HP combat target even before the real object is observed, but cannot collide or be hit before its allocation frame. At phase zero the random next opening is deliberately not guessed. Recorded turn ticks confirm targeting reads the player after movement.
  • $4FF7E restores $CEA=416 before the camera update. The next frame can backscroll farther than the 16px window visible in a post-camera capture.
  • $507DA aims from the cannon anchor, then allocates its round at +(8,8). $4180 moves the new shot immediately and discards it outside X=0..319 or Y=0..191. An offscreen cannon must not create a phantom shot that enters later. The first carry is predicted from the observed byte accumulator; the random reload after that carry is observed again on the next frame.
  • The final cannon's round sprite $5B4AC has collision bounds (-3,-3)..(3,3). The old generic anticipated-shot envelope was too wide.
  • Side rounds remain in screen coordinates. A moving camera changes their eventual world-space height. The attack search leads the firing row by the side round's flight time, using the forecast camera intent.
  • $6906 selects the ship pose and $3D58 builds its collision box before movement. The neutral sprite $208EA has bounds (-10,-10)..(11,10), not the legacy (-10,-15)..(11,15) envelope. That legacy envelope includes movement padding even when holding still. The encounter now combines the original pre-move and post-move pose boxes without extra padding in this fight. The five original poses are (-6,-10,18,25), (-7,-10,17,20), (-10,-10,21,20), (-9,-10,17,20), (-7,-10,14,20) as X/Y/width/height.

The lane search checks complete changing UP/hold/DOWN paths, with physical wall clearance and minion destruction. Safe paths rank damage to all mounted parts, with a 2:1 weight for cannons versus arms/joints, then accumulated distance to the nominal target's firing row. Opposite-side damage counts when the actual weapon simulation reaches it. Rewarding unrelated minion damage made the pilot repeatedly retreat from its boss objective. Ranking only the endpoint also allowed the attack to be postponed to the end of every new horizon. Minion destruction still matters when it makes a path safe; it is not a competing mission objective. Other levels retain their previous planner and hull envelopes. Above the solid hull it can also dodge horizontally toward the open center, then return outside before descending. Purely vertical options could pin the ship against the ceiling beside the final joint.

The exposed core is approached along the nearest exterior lane to world Y=400, then through an installed forward firing lane. A rounded navigation waypoint at X=32 caused a stationary stall beside the left hull: steering toward it made the banked ship's DOWN forecast collide with the wall. This approach keeps the clear exterior X instead. A physically clear short descent near the stern (for example X=257,Y=390 to Y=400) takes precedence over another trip around the hull. Transfers do not retain stationary safe suffixes.

The resident session had a separate forecast bug: its cached fire schedule ended at 32 frames even though the final-ship search evaluates 56-frame maneuvers. The cache now covers the configured decision/homing horizons. This changes predicted firing, not the live fire-button pulse. A whole-session regression test checks that the prepared scene still contains fire after frame 32.

The remaining 2-point hit in r97 at frame 82591 was from directional round #628143. At 82588 its safety sweep began at a fractional Y=112.757..., leaving a tiny gap against a hull ending at Y=112. This suggests an integer collision boundary mismatch: the native linear body does not retain the initial fractional anchor phase. Rounding all safety sweeps outward was tested independently in r99, but changed the escape paths and increased total shield loss from 2 to 22. It was removed rather than retaining a global change with worse live results. Exact initial projectile phase remains a prediction limitation to investigate separately. The current mission-state layout is ABI 97, mirrored by the replay DLL's ctypes inspection structure; live control remains resident C.

Recent comparison recordings (each has both AVIs and 150-frame checkpoints):

Recording folder under E:\xenon_runs Result
level5-final-near-row-r68.validation Two middle cannons destroyed; one 2-point hit at 82411. Stalled on later rows with 7 parts left at 85250.
level5-final-attack-urgency-r69.validation Accumulated firing-row distance alone still stalled; 8 parts remained at 86716. Same 2-point hit at 82411.
level5-final-sprite-hulls-r70.validation Original pose boxes opened firing windows and permitted backscroll. 6 parts remained; minion damage at 82573, 84831–84832 and 85546.
level5-final-source-priority-r71.validation Removed 14 of the 15 initially live mounts. One minion hit at 84100 (27→23 shield), no lost life. Last right cannon remained at 27 HP; not a victory.
level5-final-full-encounter-r80.validation Started with 15 mounts; all removed at 84865, core destroyed at 84920, shop at 85037. Shield stayed 27, no lost life. This proves the mid-encounter checkpoint, not the full 18-mount entry.
level5-final-upper-dodge-r90.validation Started with one 2HP joint; removed it at 83961 without damage. Then stalled at X=24 beside the hull while approaching the core.
level5-final-core-approach-r91.validation Core destroyed at 84307, shop at 84420. One minion collision at 84111–84112 cost six shield during descent.
level5-final-full-entry-r92.validation Full 18-mount entry at 82110; all mounts removed at 86235, core destroyed at 86457, shop at 86574. No lost life, but shield fell 27→7. Victory, not a low-damage validation.
level5-final-stable-flank-r93.validation Restricting lateral choices below the hull lost a life at 83769. That restriction was reverted.
level5-final-dodge-commit-r94.validation Retaining short escape suffixes won the focused five-mount checkpoint, but shield fell 27→7. That escape-retention experiment was removed.
level5-final-full-fire-r95.validation Full 18-mount entry; mounts destroyed at 88862, two minion collisions cost 12 shield. Bounded recording ended before core destruction.
level5-final-core-finish-r96.validation Continued r95; core destroyed at 89086, shop at 89201, shield remained 15.
level5-final-committed-cannon-r97.validation Full 18-mount entry; mounts destroyed at 84950, core at 85029, shop at 85133. One directional hit at 82591, shield 27→25, no lost life.
level5-final-pixel-safety-r98.validation Persistent cannon identity plus outward rounding lost a life at 84044 with seven mounts still alive. Persistent identity was removed.
level5-final-rounded-sweep-r99.validation Outward rounding alone defeated the core at 85141 and reached the shop at 85253, but shield fell 27→5. Rounding was removed.
level5-final-verified-r100.validation Rebuilt retained implementation: full 18-mount entry, mounts removed at 84950, core at 85029, shop at 85133. Shield 27→25, one hit at 82591, no lost life.
level5-final-progress-loot-r101.validation Full 18-mount entry, mounts removed at 83090, core at 83198, all 20 coins (1,500) collected by 83304, shop at 83308. Shield 27→25, one hit at 82680, no lost life.

Use python xenon_tools/analyze_level5_final_boss.py RECORDING --report-json FILE to report mounted-part HP changes, hits, remaining core-owner health, and each cash drop's collection/expiry without running a Python pilot. $4EA0 converges cash near screen (160,100), orbits for 34 updates, then drops eight pixels per update. Both collection and expiry switch to unlink procedure $107C; expiry occurs at screen Y>=200 before the pickup check. The audit distinguishes these branches by position, and ignores stale cash sprite pointers on reused projectile slots. Reconcile its collected value with the captured cash counter before the shop initializes its display. A controller-disappearance flag is only a completion candidate; confirm the final destruction sequence in the AVI. Build both the Hatari executable and the replay DLL with hatari_dev.ps1 before comparison.

The current EXE and replay DLL were both rebuilt with the official script after the mounted-damage, future-minion health and cash-collection fixes. The targeted native body, candidate, planner, world scene, workspace, fire schedule, session, player, maneuver, map, scene-fire, world-body, combat and final-mission tests passed (106 tests). In particular they cover the newborn minion's first move, post-movement targeting, two-update animation, current-flank cannon retention, the short stern descent, firing beyond frame 32, resettable combat health for future minions, and holding the cash drop lane before resuming progression. Other levels have not been replayed as part of this final-boss validation.

To repeat the full encounter after building both targets:

$env:XAP_LEVEL5_TRACE_FILE='E:/xenon_runs/level5-final-repeat.trace'
$env:XAP_LEVEL5_PLAN_TRACE='E:/xenon_runs/level5-final-repeat.plan'
python xenon_tools/validate_campaign.py native-run level5-final-repeat `
  --output-root E:/xenon_runs `
  --resume E:/xenon_runs/level5-final-full-fire-r95.validation/start.sav `
  --port 6903 --frames 6000 --checkpoint-interval 150 --stop-at-shop

Use a fresh output name for each run. The native-run manifest confirms driver_backend=hatari-c and per_frame_controller_calls=false; Python records the resident pilot and does not choose its input. Unlike the older r47 checkpoint below, this start snapshot precedes activation of all 18 mounts.

This analysis uses ReVa program /level5dump and resident-C recordings E:\xenon_runs\level5-final-staged-entry-r46.validation\level5-final-staged-entry-r46.x2events, E:\xenon_runs\level5-final-side-encounter-r47.validation\level5-final-side-encounter-r47.x2events, and E:\xenon_runs\level5-final-short-step-r50.validation\level5-final-short-step-r50.x2events. The reproducible pre-encounter checkpoint is E:\xenon_runs\level5-final-side-encounter-r47.validation\start.sav at game frame 82211. Each validation also has paired original and sprite AVIs. AVI coordinates are twice the game coordinates; the positions below are game coordinates from captured object collision rectangles.

Damage sequence confirmed in the original code

The final ship starts with 18 destructible mounted parts: eight cannons (update_proc=$507DA), four arms ($5032A), and six joints ($4FF62). They all use hit handler $503D2. That handler subtracts damage from object health at offset $32 and decrements the remaining-part counter at RAM $0D90 when a part dies. It does not require a particular left/right destruction order. At frame 82211 the counter was 15; at frame 82361 it was 14, with six cannons, four arms, and four joints still active.

The center core uses update handler $4FF6E and hit handler $504A6. $4FF6E sets its shot collision rectangle only when $0D90 == 0. Before then the captured X bounds are both 1000, so shooting its visible sprite does not damage the boss. Once enabled, $504A6 subtracts damage from the root controller's health at offset $32; reducing that health to zero triggers the completion sequence. The root controller's update handler is $4FF7E.

Geometry at frame 82400

At scroll Y≈280, the lower cannon hitboxes are X=122–133 and X=204–215, at screen Y≈16–32. The middle cannon pair is X=113–124 and X=212–223; the upper pair returns to X≈122–133 and X≈204–215. The lower arms are X=91–98 and X=237–244, near screen Y=9–38. Lower joints are X=110–117 and X=222–229, slightly above the viewport in this frame. The core sprite is near X=160–176, but its shot rectangle remains disabled.

Correction: those X-coordinate alignments are insufficient. In the visible r46 run the boss wings intercepted the forward bullets and lasers fired from below. After the exposed rear joint died, the lower arms and cannons took no damage despite sustained forward fire. Whether each blocking pixel belongs to a terrain mask or an object hitbox still needs a dedicated render/collision trace, but a below-the-wings forward attack is disproved by the live result. The side weapons fired from the exterior do damage mounted parts; r47 destroyed the left lower arm and r50 destroyed several arms and joints from the left lane. Do not use the theoretical centered forward lanes as an attack plan.

Historical map error: the offline map reported row 26 (world Y=416–431) as solid through column 13 (X=0–223) and again from column 16 (X=256) rightward. Only the X=224–255 opening crosses that divider. For the captured 18-pixel ship hull, a left-edge X around 230 fits; a point route at X=240 does not leave enough space for the hull. Row 22 has another central solid band at world Y=352–367. A center station above row 26 must approach through the right opening and remain below row 22. Those old connectors were based on terrain which the boss activation replaces. The current adapter captures the live arena tile map from RAM $5D21A at activation; its body occupies rows 6..23 and its stern crossing is open. The destruction sequence clears the arena again. Replay imports the same captured arena state. Do not navigate using the stale offline divider during this encounter.

Threats and unsuccessful side-lane experiments

Each mounted cannon uses update $507DA; its fire accumulator increases by 15 at $4F057, so one cannon fires roughly every 13–18 game frames after its random reload. The fired projectile uses one of eight aimed directions at about six pixels per frame. With six active cannons, shots repeatedly cross the narrow left flank. The root $4FF7E creates a homing minion about every 59–64 frames in these recordings. $5025E adjusts its compass heading by at most one 45° step every eight frames. One observed minion (r50 #626344) traveled from screen (63,-17) at frame 82270 to (35,86) at 82292 and hit the ship at 82293 when the pilot climbed into its path.

The left exterior lane is approximately ship X=14–30. Side fire from there can reach mounted parts, and X≈23 at screen Y=176 also permitted backscroll in r39. Holding X≈33 near the same edge caused wall replay to suppress DOWN and backscroll in an earlier run. Thus backscroll is possible, but the ship must stay in the clear exterior lane.

These probes do not defeat the boss:

  • r47 stalled with 13 mounted parts left and fell from 27 to 1 shield. At frame 82328 the candidate rejected a climb because a horizontal cannon shot at world (83,299), moving left six pixels per frame, would cross the firing row about seven frames later. The retreat was locally safe but the controller never returned for sustained cannon damage.
  • r48 tried a wait-then-climb proposal and moved into minion #626344 at frame 82293. The one-frame decision already reported a future collision near 82290; this is a planning failure rather than proof the minion was invisible.
  • r49 waited for the minion to pass and kept 27 shield for hundreds of frames, but made no mounted-part progress, then took damage near 82998.
  • r50 tried short upward steps and destroyed lower joints and arms, but all six cannons remained alive. Repeated aimed shots reduced shield to zero by 82854; a life was lost at 82871. When the pilot was driven to the top, the desired lower-cannon station lay below the camera's current reachable window. The navigation goal was clamped upward as the camera advanced, so the controller never re-established the lower firing row.

The next implementation should explicitly keep track of a cannon row until that cannon dies, backscroll if the row drops below the camera, and treat the periodic minion crossing as an encounter phase. Before choosing a firing station, verify in a live run that side projectiles reach the intended cannon without the nearer arm or joint absorbing them. A useful validation must show cannon HP falling, then the remaining-part counter reaching zero, and finally core HP falling; a nominal route or safe-looking rollout is not enough. This was true for these historical probes; r80 later demonstrated a damage-free victory from a checkpoint with 15 mounted parts remaining.

Two-row lure experiments on September 29

The following visible native-C runs all started from r47's start.sav at frame 82211 and recorded both AVIs. The C executable and replay DLL were built from the same source before each distinct run:

Run Result
E:\xenon_runs\level5-final-lure-r51.validation\level5-final-lure-r51.x2events A lower bait row and side-shot-row goal preserved 27 shield through frame 82912, but no cannon took damage. At frame 82550 the mission proposed UP toward the cannon; the search accepted a retained stationary action after one rollout. Shield then fell to 9 by 83414.
E:\xenon_runs\level5-final-lure-r52.validation\level5-final-lure-r52.x2events Disabling retained-plan reuse for this encounter made the ship climb. It damaged cannons, including lower left #625729 from 30 to 9 HP, but lost all 27 shield and a life by 82655.
E:\xenon_runs\level5-final-lure-r53.validation\level5-final-lure-r53.x2events A 20-frame horizon preserved all 27 shield through 82764 but made no cannon progress. The search repeatedly chose safe holding/retreating actions.
E:\xenon_runs\level5-final-lure-r55.validation\level5-final-lure-r55.x2events A 10-frame horizon and shot-triggered return to the lower row destroyed one cannon, #625729, at 82634. The ship nevertheless lost a life at 82662 after repeated directional-projectile hits.
E:\xenon_runs\level5-final-lure-r56.validation\level5-final-lure-r56.x2events A first attempt to choose the upper dodge row from a simple eight-frame straight-shot check repeated r55's loss; its check called the lower path clear while the full maneuver search chose UP.
E:\xenon_runs\level5-final-aligned-r57.validation\level5-final-aligned-r57.x2events Side-shot height correction without the switching rule lost 14 shield and damaged no cannon by 82711. Two hits were from minion bodies, not directional shots.

The r54 run accidentally retained r53's 20-frame side-attack horizon and was stopped as a duplicate; its AVIs are valid but it adds no independent result. The failed row-switching code was removed from the active source after r56, and the unproven side-shot-height change was removed after r57.

At r55 frame 82426, the tactic switched its goal downward, but the native search selected UP because the descending rollout encountered a projectile. The ship then passed above the live cannon row, and later descended through shots aimed at several different Y positions. This shows why a timer or a single straight-shot crossing test cannot safely control the encounter. xap_search_submit() accepts a collision-free retained plan before it evaluates the new mission. When it does search, its general rank puts screen exposure, side preference, and pacing ahead of predicted damage and distance to the firing station; fixed waiting proposals also include unrelated screen Y positions. These rules explain the safe idle in r51/r53 and the escape away from the selected row in r52/r55. The projectile forecast did identify danger in several of those frames; this is primarily an action-search problem, not evidence that the straight projectiles turned or were invisible. The next C implementation needs a boss-local time-expanded lane search: evaluate complete UP/hold/DOWN sequences over the same prepared hazard scene, require clearance along each sequence, reward actual side-shot damage and cannon kills, and keep the lowest live cannon as the target until it dies. Include camera backscroll and minion paths. Choose the first input of a safe sequence and replan each frame. This replaces the generic search's early acceptance of a retained hold and its unrelated fixed-Y waiting stations for this encounter only. Do not port the unsuccessful two-row state machine back into the active driver.