Xenon 2

Autopilot · write-up

Xenon 2 Autopilot

xenondoc/AUTOPILOT.MD · 143 KB · updated 2026-09-17

2026-09-05 architecture update: the separate experimental controller is available with --planner maneuver. See AUTOPILOT_MANEUVER.MD for its architecture, limits, validation and comparison commands. The default --planner legacy runs the original scorer/beam described below. Historical recordings without planner metadata continue to replay with the legacy controller.

This is the implementation guide for the current Python autopilot, world model, route planner, recorder, and live/replay inspector. It describes the code as of 2026-09-02. Historical failures and the lessons learned from them are kept separately in AUTOPILOT_AIERRORS.MD.

For a visible, fully recorded regression run from Level1 (or a selected save), use CAMPAIGN_VALIDATION.MD. The driver reports important events during play, indexes periodic/transition checkpoints, audits acknowledged shop transactions, and supplies RAM terrain seeds for offline profiling. It does not change tactical scoring. Double Shot remains in the Level1 second-shop recipe; the new reports verify actual transactions instead of intended purchases.

The Python system is still an exploration implementation: after the observations and tactics stabilize, the final controller can be moved into dependency-free C. The important architectural boundary is already in place: Hatari supplies a self-contained canonical game frame, while all policy and analysis lives outside the emulator.

Handoff entry point (2026-09-02)

Start here rather than reading the chronological tank experiments as a list of currently verified successes. TANKS.MD consolidates the tank model, failed approaches, open issues, checkpoints, and commands. PROFILING.MD describes the offline timing/function-profile workflow. The detailed sections below remain the implementation reference; individual run reports are historical evidence, not a fresh regression pass of every level.

Levels 1--4 have been traversed with level-specific route/boss/shop policies. This does not imply every segment is damage-free or regressed after every recent change. Level 5 has reached the tank and destroyed its side emplacements and four mounted cannons. Before the shield-cheat exploration, the latest core validation was run201: HP160 to57 in600 canonical frames, shield19 to3, no complete tank kill. Homing pressure and return-to-firing-lane control remain open normal-play problems. The subsequent opt-in cheat run202 killed the core and reached the shop at93325 using two shield refills. Run204 preserved Side Shot, bought one Power Up, left the shop at94305 and explored222 further gameplay frames without damage. Its ending checkpoint at94527 is historical; protected shop continuation210 and post-shop exploration215 are described below. Full tank basenames and commands are in TANKS.MD. They inherit cheated progress, not a normal-play clear.

Shop policy correction: finite recipes are now the first purchase pass, followed by spend_remaining. The controller buys further useful compatible upgrades until none is affordable, including duplicate auxiliary Lasers/Cannons up to the four-slot capacity. It preserves protected mounts, skips the previously excluded items, and reports purchase_policy/purchased_counts in shop diagnostics. Cash can survive a mid-level shop exit but is cleared at level initialization; the policy does not bank it. See shop plan for disassembly evidence and the exact remaining-budget rules.

Operator recipes and evidence

All commands below run from D:\src\hatari. Use the documented build/launch wrapper, not an executable with an inherited unknown DLL path. Build changes in src/xenonControl.c require restarting Hatari; Python edits do not.

powershell -ExecutionPolicy Bypass -File xenon_tools\hatari_dev.ps1 build -BuildDirectory build2
powershell -ExecutionPolicy Bypass -File xenon_tools\hatari_dev.ps1 run -BuildDirectory build2 -ControlPort 6902 -Snapshot assets\xenonplay.sav
python xenon_tools\record_run.py handoff-test.x2events --mode autopilot --port 6902 --snapshot assets\xenonplay.sav --frames 600 --trace --avi
python xenon_tools\record_run.py human-test.x2events --mode human --port 6902 --snapshot assets\xenonplay.sav --avi
python xenon_tools\replay_ui.py xenon_tools\run_logs\handoff-test.x2events

record_run.py is the preferred live entry point. It pauses/restores, starts native event logging, transfers the single socket to autoplay.py, and on Ctrl+C/key/automatic stop finalizes both AVIs, saves Atari state and controller state, and releases input. Without an automatic stop, press a key in its terminal. Do not kill the process just to stop a recording. For an unattended run, use one of --frames N, --stop-at-frame GAME_FRAME, --stop-at-shop, or --stop-at-level N (these stop conditions are mutually exclusive).

Outputs share the requested basename under xenon_tools/run_logs: .x2events is the authoritative object/draw/memory event stream, .jsonl is optional live decision telemetry, .sav plus its identity and .sav.autoplay.json sidecars is a continuation checkpoint, and -original.avi / -sprite.avi plus their frame indices are the paired visual evidence. Keep all companion files. A late checkpoint is not interchangeable with the start of a recording. Snapshot loading restores object identities when a sidecar is present; absent sidecars reset the namespace. Do not use recorded IDs as permanent enemy keys.

Use --trace --avi for the final live validation. Normal-speed stepping is the acceptance baseline; do not add --full-speed while diagnosing tactics. All commands use canonical Atari frames, not extrapolated renderer frames. Replay toolbar game-frame counters are different from file-record ordinals.

Ownership and models to inspect first

src/xenonControl.c captures synchronized RAM/objects and services the socket; xenon_client.py decodes it. WorldState/TrackedObject classify by level, procedures, live status/fields, draw presence and semantic tables, not one sprite per enemy. GameMemoryState includes input timing, scroll bounds, attachments, shop state and projectile damage bonus. xenon_symbols.py names the verified addresses; level-specific code must be analyzed against that level's loaded RAM.

PersistentWorldMap combines resident terrain with authoritative live changes to destructible cells and a one-time RAM sync on level entry/restore. Its 4px configuration-space grid uses the ship collision bounds, 8-connected paths, clearance, and cached searches. Missing sprite draws alone do not prove a static wall disappeared. The upper scene, lower persistent map and navigator must agree about a destroyed gate before another path heuristic is added.

The executable driver is main() in autoplay.py; _legacy_main() is historical unused code and must not be copied as the current control flow. ReactiveController.choose owns perception-dependent tactics, target/weapon selection and survival arbitration. MovementGoal is a stable world-space region; Decision contains the proposed action. movement_authority identifies which layer finally overrode it. A correct target or cyan/blue route is not evidence that the applied joystick follows it: inspect applied_input, next_input, movement_authority, collision predictions and committed actions together.

The shared game_model.py implements disassembly-derived ship/camera transitions; specialized projectile/formation predictors add their own state. World-space predictions must evolve the prospective ship and camera, especially for homing missiles. Attachment drawings do not enlarge the player hull. Weapon collision lanes, spawn delays, travel time, viewport culling and damage callbacks determine reachability, not merely seeing a sprite in the expanded inspector view.

Current tactical families

Family Policy and switch evidence
Pickups Semantic equipment/cash priority, exact released motion where known, reachable intercept before expiry. Replacement cost can reject Homing Missile when Side Shot is important.
Scripted swarms Predict script/phase, target earliest surviving member, retain productive lane for leader-follow motion; use a different lane/sweep policy for broad horizontal formations. Avoid collision before tracking target.
Worms/formations Predict linked segments, avoid the whole chain, not just head. No aiming at an unreachable segment across a wall.
Wall shooters/gates Attack from a reachable equipped firing lane before advancing; confirmed destruction updates terrain, then follow a committed egress. Surviving shooters remain hazards even if not route blockers.
Dead-end retreat Retained mission/turn point, corridor-centred world route and collision-tested connector. At the bottom DOWN produces backscroll only while RAM bounds permit it. Stop forcing exhausted backscroll.
Level 1 Known fork/dead-end policy, wall/projectile suppression, front-cannon shell clearance before passing, eye boss aiming and shop transition.
Level 2 Generated offline route/mission policy, homing staging, three-eye boss target order, later boss and guide-derived equipment recipes.
Level 3 Destructible-aware offline maze segments and firing stations, gate egress, radial/linked formations, boss weapon alignment.
Level 4 Offline route, directional wall shooters, chained-head satellite/left-refuge tactics; later large-eye phases then head/tongue timing.
Level 5 Destructible laser/tile-pair barriers, launcher suppression, one route contract through the diagonal area, tank phase/station policy in TANKS.MD.
Shop/interstitial Separate ShopController driven by RAM acknowledgment, mount conflicts/budget and per-visit recipes, then spending remaining cash on useful compatible upgrades. Level-5 shop1 preserves attachments (no generic Side-to-Homing sale). Four auxiliary mounts may contain duplicate weapons. Loading/fire-wait predicates prevent stale shop control after transitions.

Level-1 vertical boss shells (2026-09-02)

XENON_UPDATE_PROC_BOSS_SHELL ($4F8DC) keeps a fixed X and oscillates in world Y. ReVa's disassembly in /mydumpat0 names this entry BossShell_VerticalOscillatorAndProximityBurst. Signed words +$28 (step), +$2A (travel) and +$30 (phase) drive the vertical predictor. Camera compensation is not world velocity. The existing recorded object bytes contain these fields; no new protocol or Hatari build is required.

The burst tests only vertical proximity, -30 <= shellY - shipY <= 10, then applies a random test. Moving beside it does not prevent the burst. A zero step means it already burst and was unlinked from the damageable list; replay labels that object boss_shell_spent rather than pursuing it as a live target.

boss_shell_clearance owns the reachable shell before ordinary map advance. It reuses the configuration-space firing-station planner, restricted to the front cannon, finite backscroll range and a below-shell approach. A wall may require first moving DOWN to a wider row and then aligning X. Retain the selected shell while its firing station remains reachable; do not switch to another shell because an animation changes its bounds. Near the station, use the exact bullet anchor interval, not the wider navigation-waypoint tolerance. Current bounds still determine that interval. Survival can override movement temporarily. Shells in other corridors without a safe front station are not invented firing objectives, and a destruction/spent transition releases the mission.

Synthetic regressions in test_autoplay.py cover vertical reflection, spent classification, precedence over navigation, routing below a divider, rejecting an unsafe forward detour, and precise final gun alignment. Live validation full-game-03-shell-check-0902-04.validation starts from the original L1-f25974-section-clear.sav: the reported #11697 enters the destruction routine at frame25995; the 350-frame run ends with shield39, zero hits and no lost lives. Its canonical events and both AVI streams are in the same folder. The final exact-alignment/reachability check is full-game-03-shell-check-0902-05.validation: shells #11618, #11697, and #11619 enter destruction at25994,25995,26020 respectively; none entered the spent/burst state first. The run stops at26174 with no lost lives, but a later directional projectile causes four shield damage at26049. This is a bounded shell-clearance regression, not proof of a damage-free area or campaign.

Level-5 post-shop exploration (2026-09-02)

The protected continuation begins at level5-shop-protection-0902-210.sav. Runs211/212 traversed the entry and first train without damage, but the right entry cannon was wrongly excluded from forward preposition because it did not block the route. LEVEL5_POST_SHOP_ENTRY_EMPLACEMENTS now admits the two verified world anchors (112,2064) and (256,2048) as explicit suppression objectives while the ship remains below them. Existing weapon-viewport and configuration-space path checks still apply; remote optional sources keep their previous policy. This grants permission to approach a firing station, not permission to ignore survival.

Train members remain individually damageable enemy/level5_path_train targets. ReVa verified their independent delayed motion scripts; they are not predecessor- linked worms. Capture now includes512 bytes for these long scripts, reusing the existing world-space interpreter and UI prediction path. Other observations stay at128 script bytes. See SCRIPT.MD for addresses, radial firing behavior and the exact-motion validation. No new navigation/search subsystem was added.

Run214 (level5-post-shop-entry-fix-0902-214) destroyed the left entry source at 94994 and the right at95058 (first frames after their damaging updater disappeared). Its900-frame run advanced scroll2222 to1396, with shield39->37, one directional projectile hit, no life lost, and both original/sprite AVIs recorded. Train forecasts matched actual world positions at1/8/32/64 frames. Average decision time was60ms; the slowest isolated decision was1.28s, not sustained one-frame-per-second playback. The older run213 had three two-point hits and an incomplete128-byte train capture; do not use its prediction paths as validation of the corrected model.

Run215 continued through the third train and following swarms to scroll782, shield37->29, no life lost. It exposed two remaining policy/model omissions: the generic enemy tactic resumed navigation between train firing opportunities, and post-train $515DC wall cannons were generic enemies with scroll added twice to their aiming anchors. Their collision rectangles were already correct.

level5_train_hold first requires a currently reachable forward weapon lane: offscreen allocation alone must not freeze the camera before a train is shootable. It then retains the ship's world-space firing lane through individual member deaths and temporarily unavailable targets. A lower-only row band counters forward camera drift; it does not chase the members upward. The mission ends when its live members disappear; new scripts acquire a new station. Ordinary route progress cannot replace the hold, but concrete collision avoidance can. The existing UI movement-goal overlay displays the lane, and the policy sidecar preserves it across continuation checkpoints. There is no new pathfinder or scoring weight. Exact prediction covers motion of existing train members, not yet their future eight-way radial volleys.

enemy/$515DC is now wall_shooter/level5_horizontal_wall_cannon, only in Level5. ReVa /level5dump verifies the world anchor, the screen collision box, horizontal shot directions and the $F32->$63A8 health/tile-restoration callback. It reuses the exact multi-mount firing-station planner previously used for Level4 and publishes its path, hull reserve and target together as the Level5 route contract. The older generic firing path did not publish that contract, allowing ordinary navigation to replace the displayed firing goal. Approach from below now owns movement ahead of routine pressure relief; survival still has veto. Its health at object+$32 is exposed from the raw bytes already recorded; no new protocol field or C rebuild is required for this cannon correction.

Validation status (do not treat the above as a solved post-shop section):

  • Run216 cleared the first two train groups at full shield, but acquired the third hold before it was shootable. Acquisition now requires a real viewport/mount intersection. Run216 ended39->21; it is a failed whole-section acceptance run.
  • Run217 from212 cleared the last train but took8 shield damage, then exposed the cannon station being abandoned for a navigation dead-end retreat. A reachable suppression station now precedes that retreat; its final X tolerance is clipped to the selected weapon's hit interval. No other level changes its priority.
  • Run218 stopped on a missing helper import; it is not gameplay evidence. The helper was corrected and covered by a direct regression before run219.
  • Run219 (level5-post-train-station-0902-219.x2events) records the station failure with paired AVIs. Frames96919--97217, shield31 unchanged, source at world (256,1104) still10HP. The ship oscillates near (218,1274); route requests station (232,1272) using the offset Cannon, but static collision prediction rejects the last rightward step. At97009 all directional-projectile collision predictions are clear; RIGHT/UP-RIGHT have static collision at1, while DOWN has8. Thus it is not projectile avoidance or target switching. The retained path/station and executable ship transition disagreed: the reused station planner used the17x20 enemy-hit rectangle, not the29x24 wall hull. That input is now corrected specifically for the new Level5 cannon. The impossible below-wall station is rejected. Run220 nevertheless failed: its alternative station required retreat beyond the camera limit. Station searches now bound the whole path by scroll_backward_limit + SHIP_BOTTOM_SCREEN_Y.
  • The post-train left opening is now an explicit prerequisite, using the existing destructible_gate_egress route contract. After trains clear, ship world Y in (1188,1376] requests a full-wall-hull route to X100..108/Y1180..1184. This is above the open row75/76 connector. The current map must prove the route; the controller retains its reserve and path, and cannon targeting cannot replace it. Immediate survival still has final authority. Once the complete hull is above the wall, ordinary firing-station acquisition resumes. No object IDs or new movement executor are involved; replay shows the existing cyan route and egress tactic.
  • Run221 (level5-post-train-left-crossing-0902-221.x2events) validates from the stopped220 checkpoint, not an artificially improved starting state. Frames 96919--97197: crossed the left opening at96967 (world106,1187), destroyed the right cannon and a subsequent cannon, then continued into the next swarm. Shield31 throughout, zero hits or lost lives. Both AVIs and the final checkpoint are saved; automatic stop released input. This validates the local crossing, not damage-free completion of the earlier train encounters or the whole level.

Run219 remains useful as a regression fixture for rejecting the false station. For a fresh post-shop comparison use210, or212 for the last train and later cannon. Use221 to continue beyond the now-cleared connector and cannons. All validation prefixes have both AVIs beside events.

Recent tank-only fixes retain Level-5 homing escape prefixes (cleanup previously discarded them every frame), correct next-transition contact timing, and relatch productive cannon stations after a cross-column dodge. These are tested model/ ownership corrections, not proof the remaining tank survival problem is solved. The user's proposed small vertical Side Shot interception maneuver remains an unimplemented tactical experiment; current search predicts interceptions but does not have a dedicated proactive staging policy for it.

Explicit exploration cheat

Protocol v15 adds command 23 SET_PLAYER_SHIELD, one big-endian word in 0..39; there is no general arbitrary-memory write command. Add --cheat-shield to record_run.py --mode autopilot or autoplay.py to refill a living ship when shield is <=12, to39. Optional threshold/value flags configure those numbers. Normal play is unchanged by default; no invulnerability or enemy HP modification is applied. JSONL shield_cheat marks enablement and each refill request. A cheated checkpoint/recording must be named and described as such, not counted as no-damage acceptance. record_run.py automatically enables JSONL for cheat runs and rejects combining cheat with --require-no-damage. A large amount of damage within one game frame can still kill the ship before the next refill; this is not invulnerability. The direct diagnostic client command is python xenon_tools\xenon_client.py --port 6902 set-player-shield 39.

Regression and handoff checklist

python -m unittest discover -s xenon_tools -p "test_xenon_client.py"
python -m unittest discover -s xenon_tools -p "test_profile_autoplay.py"
python -m unittest discover -s xenon_tools -p "test_autoplay.py"

First state a measurable failure, checkpoint, frame range and expected action. Use analyze_hits.py for recorded damage, analyze_run.py for reconstructed decision details, validate_game_model.py for observed transition mismatches, and profile_autoplay.py for time attribution. Synthetic tests should express the model/ownership invariant without requiring large gitignored recordings. Live runs remain necessary to prove damage and progression. For an unresolved block, provide the event filename, last checkpoint, world coordinates and actual movement owner; ask for advice instead of layering another scoring exception.

The current check passes558 tests in test_autoplay.py; the associated protocol, shop, replay-UI, profiling and shield-helper suites pass63 tests in total. An existing regression uncovered by the full pass placed newly added projectile damage telemetry before shop in the positional GameMemoryState constructor. Moving it to the end fixed silent level-detection loss without changing live keyword-based decoding. A new regression protects that constructor contract.

Shop strategy

Shop control is a separate semantic state machine. Hatari exports the exact shop mode, cursor, cash, level, confirmation state, buy-page state, twenty grid records, and installed attachment metadata on canonical Atari frames. The controller pulses one menu input and waits for the corresponding RAM change before sending another. Because the shop reads input in a blocking 68000 routine, Hatari applies claimed input at $11646, recreates the fire edge at $A01, and carries one direction across the intervening redraw until $1167A consumes it. This keeps canonical-frame publication from cancelling an action halfway through the selector call.

Before leaving Sell mode it predicts the Buy grid and reserves cash for higher-priority purchases. It sells equipment only when a confirmed affordable replacement needs the same mount. Rear Shot and Side Shot are mutually exclusive. Level and the game's scroll threshold at $CD0 select one of two shop recipes per Atari level: level-1 shop 1 prescribes no replacement; shop 2 replaces Rear with Double and Side; level-2 shop 1 completes Side; and shop 2 replaces Side with Lasers and Rear. Minimum equipment is funded before desired grades, optional Power-up, or critical-health repair. Once that pass finishes, spend remaining cash on useful compatible upgrades. MORE at bottom-right opens the next merchandise page with one Fire press and no charge. The controller retries an unacknowledged Fire edge with releases between attempts, then waits for the changed page base and rebuilt grid before continuing. Planning and target ranking include both pages, so first-page purchases cannot consume money reserved for a higher-priority second-page goal. Level-5 shop 1 now buys Protection first (unless already active), then additional Lasers within the four auxiliary mounts, followed by Power Up and useful remaining-budget items. Protection halves shield damage; this is not invulnerability. Existing Side Shot and Cannon are preserved. Runs209/210 spent6100 down to100 on Protection3000, one additional Laser2000 and one Power Up1000, with no damage. Run210 stops during shop closing at94802; it is not a validation of the next gameplay area. Advice, Autofire, Bitmap Shades, Forward Shot, and temporary Super Nashwan Power are never purchased. See plan-shop-autopilot.md for the source/platform caveat, verified mount layout, and transaction rules.

Current validation status

Four consecutive normal-speed runs from assets/xenonplay.sav reached the level-1 shop without losing a life. The three explicit repeat runs were autoplay-demo-0820-27.x2events, autoplay-demo-0820-28.x2events, and autoplay-demo-0820-29.x2events. Each collected 15 pickups (including the rear cannon), scored 12,600, completed the first dead-end retreat, and took one four-point shield hit from a $004180 horizontal wall projectile.

The original first-shop and following eye-boss section are also validated. Starting from assets/beforeshop2.sav, the former generic controller preserved Rear Cannon, bought a 500-credit Health Power upgrade, and left the shop. That purchase is historical rather than the new guide-derived policy: the level-1 shop-1 recipe prescribes no trades, but the 2026-09-02 spending pass now buys useful affordable extras afterward. A focused normal-speed run from pre-boss-0820-1.sav destroyed the vulnerable eye and all eight chain objects, lost no life, and detected the next shop at game frame 7319. The final validation took two four-point directional-projectile hits.

Level-2 work is now validated through game frame 26347 from xenon_tools/run_logs/level2-start-0822-2.sav. The run level2-tactics-0822-3.jsonl retained all three lives, collected 15 pickups, destroyed three wall shooters, and advanced the camera from world scroll 4564 to 3373. It ended with 15 shield after seven hits. The earlier comparable runs lost one or two lives in the left pocket. Auditing 1,802 captured frames against the shared plus level-2 runtime banks found zero missing textured/background sprite addresses. The apparent 50 unmatched IDs are intentional FLAT_COLOR stars and HUD meter segments, not atlas assets; audit_recording_atlas.py performs this distinction automatically.

Level-3 navigation and combat are validated through the final scripted formation gauntlet and its shop transition. level3-radial-formation-fix-0827-52.x2events cleared the first newly identified radial chains for 500 frames without shield damage. Continuing from that state, level3-final-waves-0827-54.x2events cleared the remaining 11-part chains, collected the released rewards, lost no shield or life, and entered shop:transition at game frame 61457. At scroll zero the game briefly presents an empty playfield between consecutive formations. Those gaps are part of a finite encounter and are not treated as evidence that navigation is stuck.

Running it

Start Hatari with the loopback controller enabled:

hatari --xenon-control-port 6802

Then start a reproducible run with the live inspector:

python xenon_tools/autoplay.py --ui --frames 600 \
  --trace xenon_tools/run_logs/systematic-run.jsonl \
  --catalog xenon_tools/run_logs/systematic-run.json \
  --map-output xenon_tools/run_logs/systematic-map.json

The recording wrapper can run either a human or the autopilot from the standard initial snapshot. --full-speed removes Hatari's host real-time throttle:

python xenon_tools/record_run.py fast-run.x2events --mode autopilot --full-speed

This uses Hatari's native fast-forward setting and does not intentionally change emulated CPU/video timing. It is nevertheless experimental for autopilot runs: host scheduling and client/server deadlines have produced different practical results. Normal speed is the correctness and acceptance baseline. Graceful shutdown restores real-time speed before saving the checkpoint and releasing input.

The default start state is assets/xenonplay.sav. --snapshot selects another state and --no-load uses Hatari's current state. All decisions and samples are taken on original Atari game frames, approximately once per four VBLs. Renderer extrapolation is never fed back into game logic.

The UI itself uses standard-library Tk and optionally uses Pillow to draw dimmed sprites from the baked runtime atlases. It keeps the shared atlas resident and switches the per-level bank using the level number exported in every frame; wall collision masks use the same shared-plus-level lookup. --atlas-assets selects the atlas directory and defaults to assets. Without Pillow the UI falls back to the same geometry and markup view. Closing it stops the run; Pause stepping freezes the controller on the current canonical frame so its model can be inspected.

The same UI replays native human recordings and autoplay JSON-lines traces offline:

python xenon_tools/replay_ui.py xenon_tools/run_logs/human-demo-01.x2events
python xenon_tools/replay_ui.py xenon_tools/run_logs/live-ui-run.jsonl --fps 10

Replay recomputes tracking, predictions, target priorities, and the cumulative wall map but never sends the proposed action to Hatari. It supports playback, single-frame stepping, timeline seeking, and selectable speed. Clicking an object in the scene selects it in the right-hand table; selecting a row highlights that object in cyan and displays its complete tracked state.

The dedicated second toolbar contains Go to game frame, which accepts the canonical Hatari counter displayed in the main status row and trace frame field. record N / total is separately shown as the file position used by the timeline. Snapshot restore does not reset the former, so values such as 125384 and 1..5539 are expected in the same replay. Enter or Go seeks to the exact or nearest captured game frame and Ctrl+G focuses it. The wall-map pane renders all solid cells discovered up to that frame in one vertically scrollable world canvas. A blue rectangle marks the current camera viewport, which is automatically kept visible during playback. Clicking this lower map places a yellow crosshair and reports a copyable level, tile coordinate, world-cell origin, exact world point, four-pixel navigation coordinate, solid/free state, and destructible-group ID above the canvas. Static solid-cell knowledge is monotonic except for cells identified offline as destructible. In particular, a tile draw disappearing while clipped at the screen edge no longer changes a previously discovered wall to free space. Two consecutive fully visible absences do clear a known destructible cell. This also reconstructs an opening destroyed before a mid-level recording began, when replay has no initial RAM image from which to restore that state.

The most recent 120 fully reconstructed replay states are cached, so stepping back within that window restores the tracker, predictions, controller state, and wall map directly. --cache-frames N adjusts the memory/speed tradeoff. A seek outside the cache still rebuilds from the start for deterministic results.

Sprite brightness adjusts atlas brightness and opacity up to the original 100% pixels. Clearing Diagnostic overlay removes the grid, wall shading, collision boxes, labels, prediction/aim lines, and action arrow from the game pane so only recorded sprite draws remain. Both controls work in live and replay modes.

Selected object only filters the game pane to the currently selected object's sprite and applicable overlays while retaining every object in the right-hand table. This makes it possible to follow one stable identity through an animation or crowded encounter. Overlay visibility remains independently controlled by Diagnostic overlay.

The panel below the object table shows the selected object's current atlas sprite undimmed, together with its spriteId and native dimensions. Because it uses the current tracked ID on every frame, the preview follows the object's animation sequence during playback.

Information flow

The implementation is separated into capture, modeling, planning, policy, and inspection layers. The arrows below are data flow; only the final input arrow changes the running game.

flowchart LR
    H[Hatari + Xenon 2] -->|protocol v15 canonical frame| C[XenonClient]
    C --> W[WorldState]
    W --> M[PersistentWorldMap]
    W --> R[ReactiveController]
    M --> R
    R -->|joystick mask + fire pulse| C
    C -->|SET_INPUT / STEP_GAME_FRAME| H
    W --> U[Live UI / trace]
    M --> U
    R --> U
    L[x2events or JSONL recording] --> P[ReplayModel]
    P --> W
    P --> M
    P --> R
    P --> U
  1. xenon_client.py steps Hatari, injects joystick/fire state, loads snapshots, and decodes the self-contained canonical frame stream.
  2. world_state.py turns renderer/object events into identity-based tracks and separates screen motion from world motion.
  3. world_map.py accumulates discovered wall/free cells in level coordinates and performs configuration-space A*, including routes that initially go backward and routes with diagonal segments.
  4. autoplay.py selects an objective, proposes a tactical movement region, constrains it to the strategic route, and arbitrates immediate survival. autoplay_ui.py draws exactly what these layers believe.

Main modules and classes

File Main type Responsibility
xenon_client.py XenonClient Binary protocol, canonical-frame subscription/stepping, input ownership, snapshots, memory, logging, fast-forward control, and paired AVI recording.
world_state.py WorldState Converts one canonical frame into stable identity tracks, world motion, classifications, player state, weapon observations, hits, and removals.
world_state.py TrackedObject One live object identity with object address, procedures, animation sprite, screen/world rectangles, history, motion state, pickup semantics, and formation linkage.
world_state.py GameMemoryState Synchronized gameplay RAM fields: fire state, attachments, ship speed, consumed input, steering, collision state, and scroll position/bounds. Shield, lives, and score are canonical frame fields held by WorldState.
game_model.py pure transition dataclasses/functions One policy-independent implementation of disassembly-derived ship, camera, Level-2 homing, and inclusive collision rules. Autoplay and validators call the same code.
level2_strategy.py / assets/xenon_level2_strategy.json generated offline mission policy Complete-map Level-2 backbone, monotonic phase ownership, boss order, and five homing staging gates. Live search validates safety but does not rediscover the mission. See LEVEL2_STRATEGY.MD.
world_map.py PersistentWorldMap Immutable 16x16 terrain plus stateful destructible gates, 4-pixel ship configuration space, clearance-biased 8-connected A*, smoothing, route connectors, and firing-lane routes. A one-time level RAM sync restores gate state from snapshots.
autoplay.py ReactiveController Tactic state, target selection, aiming, strategic-route ownership, candidate scoring, survival arbitration, dynamic hazard search, and retreat mission state.
autoplay.py Candidate, Decision, MovementGoal The nine joystick choices, their scored/safety result, and stable world-space regions pursued by a tactic.
autoplay.py FirePulse Generates press/release edges; alternating fire is faster than holding the button.
pickup_catalog.py PickupCatalog Semantic pickup/attachment/upgrade mapping extracted from game memory tables.
autoplay_ui.py AutoplayWindow Live scene, world map, paths, predictions, priorities, candidate diagnostics, and object inspection.
replay_ui.py Recording, ReplayModel, ReplayApplication Offline x2events/JSONL reconstruction, cached seeking, deterministic re-analysis, the same inspector UI, and optional VBL-synchronized original/Sprite AVI panes.
record_run.py command-line wrapper Reproducible load, human/autopilot selection, event/dual-AVI recording, graceful stop, checkpoint creation, speed restoration, and input release.
validate_game_model.py recording gate Compares protocol entry/exit telemetry, homing pre/post state, and shield transitions against the pure model. Any mismatch exits nonzero.
validate_model_branches.py snapshot branch gate Restores one snapshot for all nine joystick directions and proves the captured $661C result for every branch.

Protocol v15 embeds the compact gameplay and shop state in every canonical frame: fire timer/reload/rate upgrade, seven attachment records, ship speed, auxiliary count, and level scroll. A marked optional trailer adds the joystick byte actually consumed at $A00, signed camera intent $CDA, and signed horizontal steering accumulator $CDE, ship/wall collision state $42E, applied camera delta $CD8, forward/backward bounds $CE8/$CEA, backtrack window $CEE, default scroll intent $CCC, and non-default duration $CE0. It records the entry/exit states of $661C and $7B5A, plus the current player's sprite-header collision offsets. These are validation observations, not a second predictor. Each external input change has a monotonically increasing generation, and the $661C observation records the generation it consumed. STEP completion is a synchronous handoff: after emitting a canonical frame and its response, Hatari remains inside the $406 completion callback and services the control socket until the next STEP/RUN/RELEASE command installs the following state. The 68000 cannot begin another game frame in between. Frames without a ship update replace the level state rather than accumulating a FIFO of delayed movements. It also exports the exact zoom-interstitial wait predicate from $84C2, the current $84BE + $7F42 script entry, and the $A01 fire-edge latch. This makes a mid-transition snapshot self-describing: level_transition sends FIRE only when the script is stopped on $0011, then waits for the player object before resuming. Recordings therefore contain the values synchronized with the objects and human/controller input. The protocol also exposes SET_FAST_FORWARD; GET_MEMORY remains available for exploratory reads. Autoplay performs one full-RAM read when a level first becomes active to restore already-cleared destructible tile words from a mid-level snapshot; it is not part of the per-frame path.

Identity and coordinate systems

Objects are tracked by (identity_namespace, identity_id). Sprite IDs are animation frames and are deliberately not identities. Each track contains:

  • object/update/draw/hit procedure addresses and current sprite ID;
  • screen and world position/velocity, with both coordinate pairs in a 12-sample canonical-frame history;
  • stable fitted world prediction velocity plus raw $1A/$1C/$5E/$5F object state;
  • the game's collision rectangle from object offsets $36..$3C, with the renderer rectangle as a fallback;
  • age, creation/removal state, and classification.

The signed post-camera level scroll word is $0CD0; $0CD8 is the camera intent applied later in the same game frame. Object observations and draw commands occur in different coordinate phases:

update-pass object world_y = object_screen_y + $CD0 + $CD8
draw-pass tile/sprite world_y = draw_screen_y + $CD0

The first expression is also object_screen_y + camera_entry_scroll: object and ship updates happen before $7B5A, while the canonical frame stores $CD0 after $7B5A. Using only post-camera $CD0 injected a two-pixel error on alternating scroll reversals and made otherwise exact Level-2 homing paths unsafe. This phase split prevents camera scrolling from being mistaken for enemy motion. Dynamic motion fitting, scripted paths, hazard sweeps, projectile interception, pickup interception, firing lanes, and target distance/priority all operate in world coordinates. Walls likewise remain fixed in world space; prediction moves the ship and the camera, never the walls. Screen coordinates are used only for current visibility, playfield-edge constraints, current-frame renderer overlap, and the final projection of diagnostics back onto the game pane. The wall-mounted $4F7A2 target is the documented exception: its raw y anchor is already a level coordinate, while $36..$3C contains its separate screen interaction rectangle, so the tracker does not add scroll to that anchor a second time. All persistent objectives, pickup interception regions, firing-lane anchors, route waypoints, and controller progress checks use object/world anchors and world-space collision rectangles instead of screen samples.

Exact ship movement model

The shared game_model.py mirrors the 68000 disassembly of ShipMovement_ApplyJoystickAndScroll_FUN_0000661c ($661C). Horizontal input updates the signed $CDE steering state in [-6,+6]; neutral decays it one unit, while a direction reversal first clears the old sign. A held direction then moves 3, 6, or 9 pixels for speed levels 0, 1, or 2+. Vertical movement is 3+$C9E pixels. The game clamps only the horizontal anchor to x 14..304. Vertical comparisons happen before movement and have no post-clamp, so y=17 can become 12 and y=175 can become 180. At the vertical limits it installs y=16/176 and writes +1/-1 to $CDA to request camera scrolling. $6676 first resets $CDA from default $CCC on every call. $4E4/$4E6 retain the pre-move position. Candidate scoring applies this exact next-x arithmetic from the captured $CDE value instead of estimating ship momentum from rendered positions. New frame trailers also capture $42E, the wall-collision recovery state; a zero-to-nonzero transition invalidates the current route and forces replanning.

CameraScroll_ClampApplyAndUpdateBacktrackLimit_FUN_00007b5a is a second explicit state transition. A request different from $CCC increments $CE0; from age 35 it doubles the request and writes that value back to $CDA. It then computes new_scroll = old_scroll - request, clamps to $CE8..$CEA, and stores the actually applied request in $CD8. The rolling backward limit uses $CEE and current DOWN state. A single “camera velocity” cannot represent these states and is no longer used by the dynamic planner.

Level-2 homing and collision phase model

The $50798 enemy ticks its $34C2 animation, increments age, retargets every 16 frames, moves through the eight-direction tables, then adds $CD8 to its stored screen Y. Since $CD0 changes by -$CD8, those terms cancel exactly:

new_world_y = old_world_y + direction_dy

The planner extracts the eight animation loops and each sprite's $3D58 collision header from the live Level-2 memory image. It carries world position, age, direction, active state, sprite, countdown and cursor for every homing object in each search node; all of those values participate in beam deduplication. Two nodes with the same ship position but different projectile histories are therefore not merged. Player hulls come from the $6906 steering table; the current protocol frame also exports the current sprite-derived hull.

Collision order is represented explicitly:

current player hull --AABB--> current object bounds
          |
          v
ship movement and next sprite selection
          |
          v
homing animation / retarget / world movement
          |
          +--point test new homing anchor against OLD player hull
          |
          v
carry next player/object hulls into the following frame

A committed avoidance path is revalidated by replaying its actual actions through this same transition model. It no longer reuses a screen-space swept rectangle that merges the two collision phases.

Persistent wall map

Every canonical renderer frame describes the visible tile field. The mapper records solid and observed-free 16-pixel cells at their world rows. It retains rows after they leave the screen and clears them only when the identity namespace changes.

Global navigation uses a 4-pixel ship-anchor configuration grid derived from this whole discovered map. A node is usable only when the player's $36..$3C collision rectangle plus the selected reserve fits entirely in discovered-free cells; unknown space is blocked. Planning evaluates both a preferred 12-pixel reserve and an 8-pixel reserve, which still gives real clearance while permitting the level's intentional 48-pixel passages around the 32-pixel-wide ship. A direct 8-pixel route is preferred over a 12-pixel route that needlessly backtracks; among routes with the same forward/backtracking status, the larger reserve wins. Occupancy and wall-clearance are compact byte arrays (800 KiB for a 20,000-pixel-tall level), while the original 16-pixel cells remain the persistent source map. Cached 8-connected A* permits diagonal ship movement, rejects diagonal corner cutting, softly prefers the middle of corridors, and line-of-sight smooths the result into a short world-coordinate polyline.

The planner searches toward lower world rows. It may first travel down to an older row and then diagonally around a capped passage, so an upside-down U is represented as a real dead end instead of a special hard-coded level-1 case. A route is retained until its forward endpoint is reached or $42E reports a new collision; merely discovering another map row does not make the controller oscillate between new paths. The current route and learned wall field are visible in the UI and can be saved with --map-output.

This is an incremental discovered map, not an oracle. If the route is incomplete, dead-end mode uses DOWN at the lower screen boundary to reveal older rows while staying inside the currently rendered corridor.

Object recognition

Classification primarily uses updateProc and proc3; sprite ranges are used only where a generic procedure is shared by unrelated objects.

Type Current evidence or role
player updateProc=$6734
player_projectile Bullet draw procedure and spawn/motion evidence
directional_projectile Generic 16-direction straight-line shots ($4180/$0F0E) and Level-3 reflecting variant $50996
hostile_projectile Other known projectile procedures
pickup_carrier Known carrier update/hit procedures and sprite family
pickup CompassOrbitThenDropCollision_Update_FUN_00004ea0 object whose status/payload occurs in the game's pickup-animation or attachment-installer tables; cash uses its separate observed animation family. proc3 may change or be zero during release. Captured +$28/+$2A phase/direction make the orbit and final drop exactly predictable.
wall_shooter Persistent destructible projectile sources, including Level-1 $4F7A2/$4F602, Level-2 $4FAE2, Level-4 $51260, and Level-5 homing-missile cannon $4F7A6
swarm_enemy First-level counter-clockwise swarm procedures
formation_leader Shared $503B4 scripted leaders and Level-3 radial leader $4F620
formation_follower Shared $502C2, plus Level-3 radial head/body/tail procedures $4F76A/$4F7AE/$4F7F2; every body segment is damaging
enemy Other known enemy/hit procedures

Unknown objects are shown and logged but are not automatically considered hostile. This avoids turning effects and controllers into phantom obstacles.

Decision cycle and movement ownership

One canonical game frame produces exactly one joystick decision. Objective priority does not directly control the ship. The controller first selects a tactic and target, then several owners successively narrow or replace the allowed actions. This separation is what prevents an attractive pickup, formation firing lane, or target behind a wall from steering the ship into the wrong corridor.

flowchart TD
    A[Canonical Atari frame] --> B[Update identities, world tracks, RAM state]
    B --> C[Update persistent map and strategic A* route]
    C --> D[Choose tactic by precedence]
    D --> E[Rank reachable target and compute aim/intercept]
    E --> F[Score all 9 joystick candidates]
    F --> G[Tactic proposes candidates / movement region]
    G --> H[Enforce vertical reaction-space band]
    H --> I[Intersect with persistent route corridor]
    I --> J[Survival arbitration:<br/>collision time, exposure, route progress]
    J --> K{Worm/swarm needs<br/>multi-frame escape?}
    K -->|yes| L[24-frame formation / 36-frame swarm<br/>space-time search]
    K -->|no| M[Lowest-score eligible action]
    L --> M
    M --> N[Add alternating fire bit]
    N --> O[Inject and step one canonical frame]

The effective ownership order is:

  1. Physical legality: playfield limits, current wall geometry, and ship configuration space can hard-block an action.
  2. Tactic: proposes a direction or stable world-space region, such as a pickup catch envelope or swarm firing lane.
  3. Reaction space: normally prevents moving farther toward the top when the ship lacks time to react. The floor is screen Y=128 without a rear cannon and Y=112 with one; eight pixels above it, DOWN is actively preferred.
  4. Strategic route: ordinary tactics must remain compatible with the retained A* corridor, within a 28-pixel route tube. An ordinary swarm_escape may use the whole playfield; an active retreat remains route-biased.
  5. Immediate survival: among physically legal choices, maximizes predicted time to collision, then minimizes multi-segment exposure, then considers route progress and tactical score. A finite predicted collision can never be retained merely because it is more than eight frames away when a longer-lived action exists.
  6. Dynamic escape: a 24-frame formation or 36-frame swarm space-time search may replace the one-frame result during escape/retreat. Static walls, exact ship steering, camera movement, and predicted hazards are all in this search. Complete plans commit at most eight actions and are revalidated on every canonical frame. Equal-clearance paths prefer the lower-middle reaction band before route distance, preventing a locally safe branch from terminating at the top edge.

Screen Y in item 3 is intentional because it measures visible reaction time. Object trajectories, target reachability, collision prediction, movement goals, and routes remain world-coordinate calculations.

Tactic selection and switch conditions

The normal tactic is chosen by the first matching row. This table is the concise source for precedence; the following section explains each tactic's behavior.

Precedence Tactic Entry condition Normal exit/supersession
special inactive Player object is temporarily absent during a death/respawn transition. Player returns, or absence is classified as shop. Ship-relative routes and latched goals are cleared.
special shop:* Hatari's exact shop state is active, or player absence has been classified as the shop transition. The semantic shop controller preserves required equipment, switches Sell/Buy modes, quotes and confirms affordable upgrades, then selects Exit. --stop-at-shop can end at entry instead.
special boss_eye_hold Any $50478 eye-chain object is visible. Status $0100 identifies the vulnerable tail/weak point; $00FC identifies its seven predecessor-linked segments. All eye objects disappear or the shop transition begins. This explicitly cancels the complete obsolete navigation mission.
special level2_final_boss_left_entry Level 2 is in its final-arena phase and the $50DE6 boss controller is dormant. A latched retreat/cross-left/advance-left mission activates the boss without reconsidering tempting dead-end branches each frame.
special level2_final_boss_side_attack The $50DE6 Level-2 final boss has a nonnegative timer and positive health. Hold X near 14 for the Side Shot and regulate camera scroll from the boss's world Y so its moving eye remains near screen Y 176; exit when health reaches zero or the shop begins.
special level3_composite_standoff A Level-3 $4FADE articulated carrier/core family is present. Predict the vulnerable core 24 canonical frames along its exact update-table path, latch that world-X crossing as a fixed firing lane, and hold screen Y 152..156. Concrete chain/projectile collision avoidance may detour, but ordinary target motion cannot drag the ship away from the lane. Exit when the composite family disappears or the shop transition begins.
special level3_gate_attack The ordered Level-3 map mission has reached the current large wall-shooter firing station while one or more of its 22 tagged cells remain solid. Hold the disassembly-derived forward-cannon station and fire; survival may detour. Exit only after two complete live observations prove all gate cells free, then route to the next gate.
special level4_stage2_boss_approach Level 4 is in the final pre-boss scroll interval but the post-shop boss objects have not appeared yet. Move into the reachable left refuge X=24..36 near the lower screen edge and retain it when the boss appears.
special level4_stage2_boss_eyes One or both post-shop $4FF3C eye objects remain active. As soon as the right Side Shot is available, use the span route to commit to the upper-left lane (X=24..36, screen Y≈40) while the camera is still descending. Hold that lane through projectile waves and fire during retracted intervals; exit when both eyes have changed to spent $4FF0E objects.
special level4_stage2_boss_main_head Both side eyes are spent and the vulnerable $4FE74 main head remains. With a right Side Shot, move directly to the upper-left lane (X=24..36, screen Y≈40) and keep firing; the head is hit from that lane while the tongue and spent eyes remain below it. Loadouts without Side Shot retain the older Cannon station fallback. Exit when the main-head object disappears and the shop transition begins.
special level5_tank_* The $508E0 tank controller is live. Clear $5131E side emplacements, then four $507DA cannons, then the $506DE core. Reveal one actionable row at a time and hold DOWN in the lower screen band while firing; exit when the composite disappears.
1 swarm_escape A predicted swarm collision is within the 10-frame emergency horizon, or the short escape latch is still active. Threat and latch clear. Survival has full-playfield authority unless a retreat mission is active.
2 pressure_relief A visible wall shooter, horizontal fire, or at least three visible swarm members compresses reaction space while the exact camera RAM fields permit backscroll. Twelve actual $CD8=-1 backscroll frames are banked, the camera reaches its backward limit, eight asserted-DOWN frames make no progress, danger forces temporary escape, or pressure clears.
2a scroll_spawn_pacing The next captured scroll trigger is close and the current encounter is still active, or unused backscroll allowance remains. Release as soon as backscroll is saturated and the encounter has cleared. A pending next trigger alone must not retain this tactic; navigation resumes toward it.
3 dead_end_retreat / level2_lower_labyrinth_route The retained full-map route requires a lower/older world-Y turnaround, or Level 2 has entered its known lower labyrinth. Fixed escape endpoint is reached, death/reset occurs, or route mission is invalidated explicitly. The Level-2 variant owns the known left-corridor choice; ordinary combat does not cancel it.
4 wall_escape The live player rectangle intersects a rendered wall, or repeated blocked movement leaves its anchor in a persisted solid cell, and a recovery path exists. Ship reaches legal configuration space and normal routing resumes.
5 swarm_hold / swarm_sweep A pickup would pull the ship away from a swarm lane with a predicted crossing inside the 24-frame pickup guard. Pattern selects hold or sweep. Conflict disappears or emergency escape supersedes it.
6 collect_pickup At least one released pickup is visible and no higher-precedence condition owns movement. A Zapper may also temporarily preempt a retained Level-3 gate route after immediate survival has been ruled out. Pickup is collected/disappears, becomes unsafe due to a higher-precedence threat, or leaves visibility. The retained mission route then resumes unchanged.
7 combat_hold A reachable wall shooter exists, even while a distant swarm is visible. Shooter disappears/becomes unreachable, or escape/pressure/retreat supersedes it.
7a level5_barrier_clearance A live Level-5 $5131E or $510A2 progression barrier is reachable only from a forward-weapon station. Retain the full-hull world-space route below the barrier, fire forward/Cannon until its tagged 2x2 tile cells open, then hand off to narrow-gate egress. Dynamic survival may detour but the exploration route cannot replace this firing mission.
8 swarm_sweep Visible swarm geometry is a horizontal front. Pattern ends, formation appears, or escape/retreat/pickup takes precedence.
9 swarm_hold Another visible swarm pattern exists and no formation is visible. Swarm ends, formation appears, or a higher-precedence condition occurs.
10 formation_hold A visible formation exists among combat targets. Formation disappears or a higher-precedence condition occurs.
10 combat_hold Other visible combat targets exist. No reachable/prepositionable target remains, then it immediately falls back to advance; higher-precedence conditions also supersede it.
mission level3_initial_route Level 3 still has a closed gate in the reviewed G1–G9 order. Four-pixel A* follows the current closed-gate topology to that gate's firing station. It becomes level3_gate_attack at the station and completes only after all nine gates are open.
11 advance Nothing above applies. Any higher-precedence objective appears. Follows the persistent forward route when one exists.
post-selection target_preposition An ordinary combat target is not directly shootable but A* finds a firing-lane path. Disabled during escape, retreat, pickup collection, wall escape, and close-formation standoff. Firing lane is reached, target disappears, path becomes invalid, or a higher-precedence tactic occurs.

Three related values must not be confused:

  • tactic says what behavior currently owns the frame;
  • target says what the weapons should hit and is filtered by reachability;
  • movement goal/route says where the ship may safely go.

Target ranking and reachability

Within a tactic, lower numeric priority wins:

Priority Object/payload
-3 released smart bomb / Zapper
-2 released persistent ship attachment (rear/side cannon and similar equipment)
0 released scalar ship upgrade
2 cash pickup
3 segmented-boss weak point (status=$0100)
4 next surviving segmented-boss eye
6 wall shooter
8 formation leader
12 formation follower
15 earliest surviving swarm member
18 pickup carrier
40 other recognized enemy

Priority is applied only after filtering. A combat object must already have a clear lane for an installed weapon, or—only for wall shooters and pickup carriers—a configuration-space path to a usable firing lane. The inspector lists rejected identities with the reason outside a clear near-term weapon lane. Only the earliest surviving swarm member is targetable; followers are rejected because they will later cross the leader's latched lane. Within the winning priority class, non-formation targets remain identity-locked to avoid frame-to- frame target oscillation. Released pickups lock only within the best semantic payload class, so nearby cash cannot keep a rear cannon from becoming the target.

Current tactical policy

The policy first chooses a mode, then scores movement inside that mode:

  1. Swarm hold/sweep/escape. Stream-like waves choose one shared firing lane, move to it once, and hold it. For circular streams this is the centre of the currently open corridor, latched across changes of leader or lowest member; the controller never chases an individual member around its arc. The oldest surviving swarm identity is the firing target. When it is destroyed, the next-oldest member is promoted without changing the shared lane, because it will traverse the same point. Lane holding uses a stable world-x region wider than one exact ship step. It releases horizontal input inside that region instead of counter-steering toward an exact pixel, eliminating the former left/right limit cycle. The UI marks the raw ship world x and the shared green movement-goal region. A horizontally spread front is recognized from group geometry and uses swarm_sweep, aligning successive forward shots across the row. Imminent contact uses swarm_escape, but neutral and DOWN remain legal; there is no forced left/right commitment that can drive into the wave. At the lower boundary DOWN is modelled as scroll-only, not as fictitious ship displacement, and a four-step escape sweep rejects both visible and persisted walls. Collision scoring and threat detection inspect 56 canonical frames (about 4.5 PAL seconds) for swarm objects; swarm_hold no longer discards danger after frame four. Emergency swarm_escape activation is deliberately limited to the nearer 10-frame horizon (about 0.8 seconds), so a distant predicted crossing does not preempt a safe attachment collection. Holding also owns a screen-Y region of 124..150. Within that band it does not advance merely to improve an intercept; followers will cross the same world-X lane. The 36-frame dynamic escape uses lower-middle reaction space as its first tie-break after collision clearance.
  2. Pressure relief. Persistent shooters, their horizontal fire, and dense visible swarms can temporarily own a bounded camera-delay phase. The ship moves DOWN through the current configuration-space corridor centre, keeps firing, and holds DOWN at screen Y=172. Success is measured from the exact signed camera delta $CD8, not from a screen-space estimate. Immediate collision avoidance can interrupt any one frame, but equal-survival choices prefer DOWN and the phase resumes until it banks twelve backscroll frames or reaches the live $CEA limit. A pickup cancels the delay only after hostile pressure has cleared.
  3. Collect pickup. A released pickup preempts every non-imminent combat mode. A visible Zapper also preempts Level-3 gate-route movement after immediate survival arbitration; the gate route remains latched and resumes after collection. This matters because target priority alone does not own joystick movement: before this rule the diagnostics could name the Zapper while route pacing steered past it. pickup_catalog.py extracts the status-to-animation table at $3F96, the attachment installer dispatcher at $508C, and the shop name/purchase-handler tables at $10EA8/$10E2A from the Atari RAM dump. Joining shared handler addresses distinguishes persistent weapons/attachments from scalar ship upgrades without guessing from sprite ranges. Attachments outrank scalar upgrades, and both outrank cash. Identity lock applies only within the best available payload class, so nearby cash cannot keep a cannon from becoming the collection target. Its goal is the world-space core-ship anchor region that overlaps the pickup's collision rectangle, with two pixels of overlap reserve. ReVa disassembly $4F72 -> $3D58, $4F76 -> $5D86 -> $28B8 confirms that only core ship $3921C+$36..$3C collects pickups; side attachments do not. The previous historical X envelope could count a near miss as arrival and has been removed. The first released frame uses renderer geometry if stored interaction bounds are stale. Vertical pursuit remains active unless wall/hazard checks veto it; measured downward movement projects a catch Y when a drop is above the ship. A dense cash shower releases obsolete route constraints, but does not create a frozen centroid target. Each goal refers to an actual remaining pickup. A pickup goal is also reconciled with the persistent route as a region, not as one all-or-nothing diagonal command. If reaction-space policy forbids UP, the controller still takes a legal horizontal step toward the pickup (or waits) while remaining inside the selected route tube. This prevents the route follower from silently owning movement while the displayed tactic remains collect_pickup. Pickup value is loadout-dependent. In Level 3, Rear Shot and Side Shot share the wing mount, so a Rear Shot is excluded while Side is installed. $4EA0's exact phase/direction model predicts the future fixed-X drop lane. Candidate filtering keeps the ship on its current side of that lane with three pixels of reserve and replays eight frames of $661C steering coast before accepting an input. This is a temporary hard constraint only: it neither creates a tactic nor replaces the retained world route, and it disappears when the pickup falls off-screen.
  4. Formation hold. Formation leaders outrank followers as firing targets. The first usable leader intercept establishes a latched formation firing lane; the ship moves to it once and fires as the followers replay the leader's path. If a close segment makes lateral approach unsafe, the ship first moves down to create separation rather than waiting in place. $503B4 leaders now include the same $4A script bytes captured for $E1E/$9A40, and pink 32-frame leader paths are visible in the diagnostic overlay. Each $502C2 follower's captured MyObjectEntry.next pointer is resolved to the next live tracked object. Prediction then applies the game's real rule—at t+1 the follower occupies its successor's position at t—recursively until it reaches the scripted leader. Curved worm bodies are therefore no longer extrapolated as unrelated straight-line objects. Level 3 overlays the same scripted-motion engine with a second articulated family: $4F620 is the only vulnerable leader, $4F76A supplies the two head sections, $4F7AE the seven body sections, and $4F7F2 the tail. The leader increments object +$5E by $14 each canonical frame; carry allocates eight $4180 projectiles at the exact compass trajectories with speed scale 12. World-state capture includes the leader's command stream, so the controller predicts both the articulated path and the next radial volley before any shot is visible. All members remain one risk group, while firing stays locked on the current leader. The final Level-3 screen contains several consecutive chains separated by one or two enemy-free frames. map_advance may appear during a gap, but the next leader correctly regains formation_hold; after the finite sequence ends, reward collection and the shop transition proceed normally.
  5. Segmented eye boss. BossSegmentUpdate_ChaseAnchorOrPredecessor ($50478) is classified semantically instead of as a generic enemy. The $0100 status object is the vulnerable eye at the tail of the chain and the seven $00FC objects are one-hit predecessor-linked segments. Live private +$1C pointers—not creation order or a stale disassembly comment—establish the chain direction. The weak point is targeted first. The first bullet intercept establishes a latched world-X cannon lane, so changing animation frames cannot cause left/right chasing. The target-center intercept is converted to a ship-anchor goal with the captured player collision-rectangle offset; without that conversion the cannon fired about ten pixels to the right of the eye. Candidate scoring treats all eight segments as one articulated collision-risk group, while still counting per-segment exposure. Followers whose private +$1C predecessor pointer resolves to a live eye reuse that predecessor's predicted path, including the $0100 weak point itself. Object allocation precedes the visible entrance, so the controller stages downward without latching a lane while the weak point is above screen Y=-24. Immediate survival may temporarily leave the firing lane. Boss presence clears the retained A* path, route goal, and retreat state; clearing only the retreat boolean allowed that stale route to keep pulling the ship upward during survival ties. Released pickups no longer replace the weak point after the firing phase begins.
  6. Combat hold/target preposition. While a directly targetable enemy is present, UP is forbidden. Neutral/down/lateral movement is used for firing alignment and survival. At the bottom boundary DOWN slows or reverses level scroll without pushing the ship into the HUD. A carrier's bullet/target-motion intercept is latched when selected, so the ship occupies one firing lane and waits rather than chasing it. A carrier or wall shooter behind lateral wall geometry can instead enter target_preposition, which follows the persistent map to a clear vertical firing column below the target.
  7. Dead-end retreat/wall escape/advance. A forward route that necessarily visits an older (lower) world row enters dead_end_retreat before any combat holding mode, so formations cannot suppress the required sustained retreat. Retreat begins with an explicit scroll-back phase: the controller moves to the bottom action boundary, centres the ship in the current corridor using the persistent 4-pixel configuration map, and keeps DOWN asserted. Neutral input is not considered equivalent because automatic scrolling immediately consumes the gained distance. The phase ends only after the route's lowest turnaround has scrolled to about screen Y=120—not merely touched the ship at the bottom edge—so there is vertical room for a safe lateral crossing, or earlier when the game's live backward boundary says no additional scrolling is possible. A shallow local bend whose turnaround is already within 24 world pixels skips this long scroll phase. For a deep retreat, the controller reads the game's authoritative backward boundary $CEA. When $CD0 == $CEA, the original ship routine itself refuses to request backward scrolling, so the controller immediately releases DOWN and reconnects to the route instead of forcing an action that cannot move the camera. The older 24-frame plateau measurement remains only as a fallback for frames that do not contain the camera-limit trailer; it cannot override a live $CEA value. The centre of the current corridor is taken from the deepest clearance-biased A centerline point before the turnaround, then latched for the scroll phase. This is more reliable than querying one quantized map row, which may contain only the disconnected leg of the same U when the live ship anchor lies between configuration rows. Configuration-space A then connects to the suffix entering the alternate corridor. Worm survival may interrupt DOWN for an imminent contact, but the next safe frame resumes centred back-scrolling. If the ship overlaps a mapped wall cell, or commanded movement repeatedly makes no progress, wall_escape owns movement. Its path begins at the ship's real anchor (even when it is within the inflated wall reserve) and visibly connects to the closest legal anchor. Only when no current combat objective or pickup exists does the persistent route planner supply a waypoint. The follower looks ahead on the latched world-coordinate polyline. It evaluates the exact one-frame world displacement of every joystick candidate and minimizes cross-track error, so it can pulse a lateral axis while DOWN changes scroll slowly at the screen clamp. A geometric diagonal is therefore followed at the ship's real X/Y speed ratio instead of blindly holding down-left or down-right into the inside wall. Horizontal control projects the exact $CDE neutral stopping position before choosing input. Ship progress is projected onto the current and following polyline segments, so combat movement can pass a waypoint without later steering back to it. Consumed route points are never reconsidered as evidence of a dead end. Once dead-end retreat begins, it retains its escape endpoint through the forward leg. A dynamic dodge may leave the original polyline; configuration-space A then connects the ship directly back to that fixed endpoint instead of inventing a new progressively-forward goal or accumulating connectors to stale waypoints. If conservative wall inflation leaves the live ship anchor already inside forbidden configuration space, ordinary overlap checks would mark every candidate (including neutral) as blocked. In that state only an input that monotonically reduces the world-space distance to a legal configuration anchor is admitted; playfield edges remain absolute. This lets the route follower extract the ship instead of stopping. Route compatibility uses predicted world displacement*, including camera scroll, rather than joystick bits. Neutral input is rejected when automatic scrolling would physically carry the ship backward along the route. An imminent hazard may select a collision-free lateral or diagonal dodge within a 28-pixel tube around the planned polyline; a distant collision beyond eight canonical frames does not freeze current route progress. Near a predicted formation collision, a 24-frame bounded space-time search simulates the exact horizontal steering accumulator, screen-edge scrolling, configuration-space walls, predicted worm segments, swarms, and hostile projectiles. Its trigger examines the retained route action before one-frame survival pruning; a temporarily safe neutral input therefore cannot suppress an early detour around a worm that is about to close the route. It can begin a multi-action detour before the immediate exit closes. If all choices are threatened, the controller first maximizes time to collision, then minimizes overlap-weighted per-segment exposure, and only then prefers world-route progress. The multi-frame beam uses the same ordering: among branches that remain collision-free to its horizon, it maximizes the minimum clearance from every predicted segment before comparing distance to the retreat route. Route proximity can select between equally safe detours but cannot prune the safer side. During an active retreat, the beam also starts when any legal neighboring action sees formation contact within 12 frames, even if repeating the current straight route command is safe. This anticipates the next polyline bend instead of waiting until the worm has already closed it; the broader trigger is not used in ordinary formation combat, where it would add unnecessary reconstruction cost.

All modes retain immediate wall and hazard avoidance. Fast directional shots receive an explicit time-to-intersection calculation and vertical escape commitment. Swarm objects use a longer curved-motion horizon. Formation segments use a longer horizon and a multi-step escape model.

The diagnostic screen draws the latched swarm firing lane as a cyan dashed vertical line. Magenta paths show each swarm member's 56-frame prediction and pink paths show formation-leader predictions. Cyan circles labelled +N mark predicted crossings of the firing lane, making the lane choice and timing directly inspectable in live and replay UIs.

Scripted trajectories are calculated once per object and canonical frame, then shared by threat detection, all nine action candidates, and the UI. The complete 56-frame displacement sequence is generated in a single forward pass rather than re-simulating frames 1..N independently for every query.

Replay profiling on autoplay-demo-0820-10.x2events found wall geometry to be the largest generic reconstruction cost. World-space wall rectangles are now translated once per canonical frame; the corridor scan rejects walls outside the ship's vertical band; repeated expanded rectangles are hoisted out of per-wall loops; and boolean wall tests stop at the first opaque atlas pixel instead of counting the complete overlap. Carrier/target prediction also shares the same per-object scripted displacement series, while the formation escape beam caches repeated configuration- anchor distance queries. The first 500 reconstructed frames dropped from 17.8 to 4.8 seconds on the profiling machine with an identical tactic/action/score digest. The complete 2,620-frame recording reconstructs in about 60 seconds. Remaining late- frame cost is dominated by the intentional 24-frame, 48-node formation escape beam; reducing that horizon or width would change survival behavior and is not treated as low-risk performance work.

A later cold-frame profile of the dense Level-5 barrier section found a different configuration-space cost. navigation_anchor_escape_distance() usually returned zero, but first activated a complete level-wide A grid for each temporary banked ship geometry. Those point probes churned the 32-entry grid cache even though they did not request a route. The method now tests the exact cached shifted player mask first and materializes a grid only when the anchor is actually blocked and a nearest free node is required. The conservative grid plus live-pixel refinement used by strategic motion remains unchanged. On recorded frames 90163 and 90420 this reduced one cold decision from 3.206 to 2.501 seconds (22%) and the other from 2.896 to 2.700 seconds (7%). The remaining cold cost is full-grid construction for real A queries, not the A* search itself.

xenon_tools/profile_autoplay_frame.py writes standard cProfile/pstats .prof files. SnakeViz can open those files directly for an interactive browser icicle or sunburst view. For a representative live run, prefer the lower-overhead sampling profiler py-spy and emit Speedscope JSON; deterministic cProfile changes timing substantially on the most expensive frames.

Dormant formation chains are allocated far outside the playfield. Before scoring, the controller now samples their 32-frame approach and excludes members which cannot enter a generously expanded playfield. This avoids multiplying every action candidate by dozens of inactive segments. The leader displacement series and the recursively derived follower anchors are cached once per canonical frame. Linked worm segments are also grouped into formation chains for risk scoring. A candidate is charged once for the earliest predicted collision with a chain, rather than once per segment on every future frame. Individual segment rectangles remain in the collision test. A separate discounted exposure measure preserves the number and overlap of individual segment contacts for survival arbitration, because clipping one segment often leads to several consecutive hits. Diagnostics expose each candidate's earliest collision frame, exposure, and whether it was hard-blocked by immediate wall/playfield geometry.

Horizontal reversals have a strong hysteresis cost outside pickup/sweep modes. Target alignment uses a width/height deadband, so being anywhere in a valid firing lane is sufficient; the controller does not chase exact sprite centres.

Fire and weapons

Fire defaults to one canonical frame pressed, one released. This is the fastest possible sequence of distinct press edges at the present decision cadence and shoots faster than holding the button. --fire-period and --fire-on-frames can make it slower for experiments.

Weapon directions are learned from player projectile tracks:

  • negative y: forward;
  • positive y: rear;
  • negative/positive x: left/right side fire.

Targets are considered reachable only when near the screen and connected to the ship by a clear learned weapon lane. Current alpha-mask walls and the persistent world map reject intervening tiles. For wall shooters and pickup carriers, the map may additionally provide a route to a reachable firing lane; other blocked targets remain rejected. Thus a rear cannon does not target a worm in a separate corridor. Forward and rear shots use horizontal lead; side shots use vertical lead. Travel time uses the measured speed of matching player projectiles, with a conservative default until enough observations exist. Thus a rear cannon can attack enemies while the ship backtracks.

The second life in human-demo-01.x2events identifies attachment code $0044 as the rear cannon: it appears on collection and is immediately followed by matched forward and rear projectile streams. The controller therefore enables rear targeting directly from that attachment code, without waiting to observe a rear projectile.

The UI also exposes the compact gameplay RAM sample and frame trailer. Disassembly confirms $0C64 is the fire-rate upgrade level and $0C9E is ship speed. It also shows consumed input $A00, scroll intent $CDA, and steering $CDE. Seven six-byte attachment records begin at $0C66; their raw code/object pointers are displayed and traced. $0044 is mapped to rear cannon and is the first attachment mapped directly to WeaponState. The current model combines semantic attachment-slot codes with live attachment objects: $47A8 publishes each Laser mount's world-X offset and $4902 publishes each Cannon mount's world-X offset. Side/rear capabilities can also be learned from observed projectile directions. The broader PickupCatalog joins pickup animation, attachment installer, shop handler, shop name, and shop icon tables to identify 21 of 24 semantic pickup codes without guessing from sprites. $0CC6 remains the shield value and is not a weapon meter.

Object diagnostics distinguish fixed identity from the reusable Atari object address. For example, recording autoplay-demo-0819-2.x2events shows projectile #3215 active with status $0010 at object slot $39778, moving upward 9 pixels per canonical frame. At the top edge it changes to status $0004 and destroy procedure $107C, disappears, and the allocator later reuses $39778 for the new identity #3255. The selected-object panel labels these observed active/destroy-pending states and displays the captured next address; a recycled slot is not treated as the same tracked object.

$6086 player bullets do not refresh generic collision words $36..$3C. On allocation,

3215 therefore retained (185,52)..(194,66) from the previous occupant even though its

anchor was (257,161) and its current renderer rectangle was (254,154,16,8), directly above the ship. Trusting those plausible-looking stale words made the diagnostic overlay appear to spawn a projectile remotely behind a wall. Bullet geometry now always comes from the current renderer draw bounds; the stale raw words remain visible only as raw state for reverse-engineering.

Live inspector

The upper-left panel outlines the current 320x192 scene at 2x scale and adds a diagnostic margin 48 Atari pixels left and right, 32 above, and 48 below it. This exposes captured look-ahead objects immediately outside the hardware viewport and keeps every viewport border away from the canvas clipping edge. When Pillow is available, atlas sprites are drawn at low brightness/opacity; wall geometry is also dimmed so markup stays legible. For every tracked object it shows:

  • collision box, classification, stable identity, and target priority;
  • a velocity/prediction arrow;
  • the 56-frame swarm path and 32-frame formation-leader path;
  • current target with a highlighted box and firing/aim line;
  • chosen player action.

The lower-left panel is the accumulated world-coordinate wall map. It marks the current viewport, player world position, global exploration path in cyan, and the route which actually owns Level-5 movement in orange. The orange route has a dark illustrative clearance band whose width is the game's fixed $387C wall-mask hull plus the reserve selected by A*; exact collision remains the pixel-mask test, not the drawn line stroke. Its active waypoint is white. The exact $387C hull is green and the same active reserve is a dashed yellow rectangle; the changing object-interaction AABB is deliberately not substituted for the wall hull. It also reports navigation resolution, memory use, and route progress. The right panel shows tactic, input, target, firing direction, RAM weapon state, scroll velocity, map statistics, ranked objectives, every candidate score, and a sortable object table with screen/world motion and procedure/sprite addresses. Selected-object details state whether that identity produced a renderer draw in the current frame and whether its collision bounds are onscreen, above, below, left, or right. A nonzero status means the object is a live linked-list member; it does not imply that its pixels intersect the 320x192 viewport, and an invisible formation leader may intentionally use a no-op draw procedure. The map uses one uniform display scale on both axes, so every Atari 16x16 wall tile is square. Earlier revisions compressed world Y to 0.55 display pixels per Atari pixel while scaling X to the canvas width; that distortion made the nearly square ship configuration footprint appear several times too wide.

The JSONL trace contains the same key evidence: tactic, action, target ranking, screen/world tracks, firing direction and intercept, candidate scores, scroll, weapon observations, compact gameplay RAM state, learned-map statistics, and planned route. It additionally records pickup code, extracted human name, animation address and subtype, fitted velocity, swarm pattern, rejected targets/reasons, and the raw per-object state listed above.

Wall draws have a one-pixel coordinate-origin offset from nominal 16-pixel world boundaries in the captured renderer stream. Persisted-map rasterization recovers the nearest aligned source-tile row (and accounts for clipped source offsets) rather than flooring both rectangle edges. The old calculation frequently turned one visible tile into two solid world rows; at frame 5882 this fabricated the occlusion that rejected all wall shooters. With the corrected map the controller selects #1086 and prepositions to its firing lane.

Disassembly resolves the first-level swarm motion. $E1E jumps to $9A40, which calls $9ABE and then $9ACA. $9ACA updates 16.16 x/y through the signed sine table at $9C80; object offsets $28/$2A/$4E/$52/$54 are remaining substeps, phase, phase delta, phase acceleration, and substeps per canonical frame. When a segment expires it dispatches the command stream at object $4A. Opcode 2 at $9C18 is a ten-byte arc record containing initial phase, phase delta, acceleration, and duration. $E2A is only the damage callback.

New captures keep the exact 98-byte object record and append 64 bytes from the current $4A command pointer to the same variable-size raw envelope for scripted swarm/carrier objects and $503B4 formation leaders. This needs no protocol-version change and old 98-byte recordings remain readable. The predictor executes the current sine segment and consecutive opcode-2 records, including a transition partway through a game frame. A fitted eight-frame velocity remains the fallback for older recordings and script opcodes not yet decoded; unlike the removed last-frame acceleration estimate, it does not flip the displayed future path with integer-position jitter.

Level-2 worm holes and exact damage sources

The three-eye arena has an explicit left-upper / right-upper / lower-main target order. Allocated $50C48 eye slots, not rendering visibility, determine which eyes remain. Encounter ownership starts when any eye enters the action area and the arena controller is present: the lower, initially invulnerable eye is visible about 320 world pixels before the upper eyes. This also works when resuming a checkpoint below the arena. Generic worm-hole clearance must not hold the ship below that lower eye instead of routing to the upper side eyes. The center becomes the objective only after both side-eye objects are gone; live collision avoidance still protects the route.

During this encounter, a complete scripted-worm escape retains its entire validated action suffix (typically 32 frames), rather than discarding it after eight. Each next action is still checked against current hazards and walls; incomplete paths still trigger replanning. This is limited to level2_boss_eye_route and level2_boss_eye_hold, leaving other encounters' commit limits unchanged. The failed trace's worm trajectory matched the script forecast exactly: the regression was throwing away the safe escape before finishing it, not the motion tables or the prediction coordinate system.

The last eye's death handler $50BDC..$50BE8 clears 460 words at $5A08E: resident-map $5873E rows 162..184, all 20 columns. The map asset now tags this as level2-three-eye-boss-arena destructible terrain. Existing two-complete- frame observation/RAM-restore reconciliation opens it and invalidates navigation caches. Unrelated walls remain monotonic. It is not a gate-egress mission. Without this metadata, navigation dodged nonexistent walls during cash collection.

Validation (2026-09-02): resume full-game-02.validation/checkpoints/L2-f18688-periodic.sav with validate_campaign.py run NAME --connect --port 6903 --resume PATH --frames 1200. full-game-02-boss-check-0902-01.x2events destroyed the left eye at frame 19005 and right eye at 19574. Continuation full-game-02-boss-check-0902-02.x2events destroyed the center at 19933 and entered the shop at 20039. Neither trace selected the center while a side eye remained. Both have paired, decoded 640x400 AVIs. This proves encounter activation/order and completion, not lossless survival: the first part took six worm-contact hits and lost one life (19158); the second part took no damage. The first-part trace retains that separate survival failure.

After the survival/cash fixes, the final combined validation is run_logs/full-game-02-worm-cash-final-0902.validation/ full-game-02-worm-cash-final-0902.x2events (same initial frame 18688). It entered the shop at 19646, with one hit / eight shield damage / zero lives lost, and 2050 cash versus the earlier 1300. The remaining hit at 19072 was a $508D0 formation follower during survival:constant_input_fallback; do not report this as no-hit survival. Both 640x400 AVIs, detailed trace, terrain RAM, reports and checkpoints are adjacent in that campaign folder. A separate 350-frame post-boss test from checkpoint 19439 collected eight pickups with no additional damage. Synthetic coverage checks complete-plan retention, core-only pickup overlap, stale-centroid removal, boss-terrain clearing and post-destruction RAM restore. Cross-level live regression remains a separate run.

Level 2's $50C48 objects store (tile column, world tile row) in their anchor words, not ordinary screen pixels. WorldState converts these to (x*16, y*16) world coordinates and retains dormant holes as worm_hole hazards. The game animates/arms a hole and allocates its twelve worm members in the same canonical update, so there is no useful visual warning horizon. The controller therefore reserves a persistent emergence zone around every hole. If the ship is already inside that conservative zone, it latches a nearby two-dimensional world-space escape point, preferring a short DOWN move and minimal lateral motion. The ordinary formation planner resumes after the buffered ship rectangle leaves the zone; live worm segments can still preempt the latch for immediate survival.

Protocol v12 records authoritative damage at the common damage routine $65A4 and confirms it at the corresponding $CC6 shield write. The entry telemetry retains the return site and source registers, so generic contact at $67D8, directional-projectile contact at $41DC, and Level-2's direct homing point test at $50834 are distinguished. Each event contains fixed identity, update/sprite procedure, actual damage, source/player geometry, scroll and cycle. Lethal unsigned underflow is reported as the remaining shield rather than as a huge damage value. This replaces both call-site-specific omissions and post-frame proximity guesses.

Regression tests and acceptance

Run the synthetic and protocol regression suite from the repository root:

python -m unittest discover xenon_tools

The suite includes focused tests for the closed-gate Level-3 mission, live destructible-cell opening, bounded escape commitment, and the existing movement, combat, shop, replay, and rendering regressions. The important synthetic cases cover exact ship steering, pickup precedence, stable swarm lanes, scripted swarm motion, leader/follower formation prediction, full-map dead-end detection, diagonal configuration-space routing, retreat scrolling and completion, dynamic hazard detours, reaction-space recovery, target reachability, shop detection, protocol decoding, replay seeking, and visualization helpers.

Passing these tests is necessary but not sufficient. The live acceptance gates are a normal-speed run from assets/xenonplay.sav that reaches the first shop with zero lives lost, and a run from the saved pre-boss state that destroys all eight eyes and reaches the next shop without losing a life. Runs record shield, pickup, retreat, shop, and boss telemetry, stop gracefully, save checkpoints, and release Hatari input. Fast-forward runs are tracked separately.

What is deliberately not claimed yet

  • The first Sell/Buy shop transaction and the following segmented-eye boss are validated. Later shops, equipment conflicts with enough cash to require a sale, and subsequent levels are not yet validated.
  • The memory-table joins name 21 of 24 semantic codes. External documentation and the literal Z animation identify $0088 as the one-shot ZAPPER smart bomb. Disassembly identifies attachment $0094 as the base FORWARD SHOT controller. Only $005C (animation row #04, $002D6C) remains unidentified.
  • Configuration-space planning conservatively expands 16-pixel wall cells; immediate one-frame candidate rejection still uses the more precise sprite alpha mask.
  • An articulated worm is still represented as multiple predicted segments, not yet as one swept ribbon with a detected tail gap.
  • Destruction of a wall hole versus temporary disappearance still requires better semantic confirmation.
  • Boss logic currently covers the first segmented-eye encounter only. Other boss weak points and the dive input are not implemented.

The next experiments should be measured in the UI/trace rather than by adding another level-specific steering constant: compare map/path and hit telemetry across repeatable runs from the same snapshot and map and identify the remaining unnamed scalar-upgrade animations from shop/live observations.

The earlier broad swarm_escape policy was active during 142 of 158 frames in a human swarm pass that required no horizontal input. The current design instead uses a latched firing lane, a 56-frame path prediction, a 24-frame pickup guard, and a 10-frame emergency threshold. It has passed the current level-1 baseline, but different swarm scripts on later levels remain unvalidated.

Level-4 directional wall shooters

Level 4 overlays $51260 with Level4DirectionalWallShooter_Update. These are destructible wall targets and projectile sources, not passive destructible map decorations. WorldState classifies them as wall_shooter with subtype level4_directional_wall_shooter, using the world anchor at +$20/+$24 and the 28x28 interaction rectangle at +$36..+$3c.

The canonical object capture already contains the state needed to forecast the next shot. Word +$28 is the six-phase animation/fire state and byte +$5e is the carry timer incremented by $10. Phase 4 allocates a shared $4180 directional projectile: status $d0 fires trajectory 6 to the left and $d4 fires trajectory 2 to the right, at speed scale 6. The survival planner reserves the projectile sweep before allocation; combat gives the still-live source the ordinary high wall-shooter priority instead of reacting only after a narrow corridor has filled with bullets.

The attack planner uses the geometry of every installed forward weapon rather than assuming that all shots leave the ship centre. Attachment code $0034 (shop item 15, Cannon) runs $4902; in the validated Level-4 loadout its live attachment anchor is (shipX+26, shipY+16). It spawns $4e50 Cannon projectiles at attachment Y-11, and $4e50 moves them upward by 10 before the first rectangle collision query. Thus the effective first tested projectile position is (shipX+26, shipY-5), while the ordinary forward shot remains centred on ship X.

For each proactive Level-4 shooter objective, the planner builds candidate firing stations in world coordinates for the ordinary forward shot and each live Cannon offset. A station may be well below the target: projectile travel is not restricted to a one-frame overlap. The player bullet collision paths at $6086->$3950 and $4e50->$3908 scan object collision rectangles and do not query the tile map, so wall cells are not treated as imaginary bullet occluders. Among reachable stations, the planner selects the one with the largest ship-wall clearance before considering path length and latches the matching weapon geometry; this prevents a centre-shot route from being retained after aiming switches to an offset Cannon.

The planner does not route across a shooter's horizontal fire lane merely to create a rear-cannon shot. Rear fire remains opportunistic when the retained navigation route naturally places the ship above a target. The upper-right emplacement at world (272,4000) in the first Level-4 corridor is therefore modeled as a navigation-only combat hazard: its body and predicted projectiles remain in every survival calculation, but it cannot suspend route progress. This is deliberately separate from the earlier (256,4048) shooter, whose reachable below-target Cannon station is a required opening objective.

Validation began from the earlier checkpoint level4-entry-inventory-0827-01.sav, then retained controller state into the focused synchronized-AVI proof level4-first-shooter-proof-0828-05.x2events. The original recording's shooter #561762 is replay identity #603029, anchored at world (256,4048). The ship held near world X 229, putting the Cannon lane near X 255; the shooter changed from live status $d0 to destroying status $04 at frame 63608 and then vanished. The 40-frame proof gained 400 points with zero shield damage and zero lives lost. A longer continuation (level4-first-shooter-early-0828-04.x2events) subsequently lost a life to an unrelated swarm after the shooter was destroyed; this is a separate Level-4 survival issue, not a failure of the firing-station model.

The following continuation exposed a missing object-graph field in the swarm model. $E36->$613E copies its position from a MyObject pointer at +$56; the controller now records this as follow_target_object_id and derives the follower's world trajectory from that target in the same canonical frame. Internally screen-relative script anchors are converted at the predictor boundary. All route, collision, beam, queued plan, trace, and overlay geometry remains world-space. With that correction, level4-progress-0828-10.x2events survived the formerly lethal swarm with no damage. After classifying the upper-right shooter as navigation-only, level4-progress-0828-13.x2events advanced from scroll about 3860 to 3647, took no damage, gained 3300 points, collected a pickup, and still destroyed that shooter opportunistically.

Level-5 homing-missile cannons

Level 5 overlays $4F7A6 with a destructible wall cannon. Status $0100 is the left-wall form that launches right; $0104 is the mirrored right-wall form. Unlike older wall targets, its +$24 Y anchor is screen-relative because the update routine applies camera delta $CD8 directly. WorldState therefore adds the canonical scroll position before navigation, targeting, or velocity analysis.

Byte +$5E is the launch accumulator. Each canonical update adds $14; carry immediately calls $4F882, which allocates a $4FA1E curved homing missile and then seeds the next interval with a random value from 0 through $3F. The next launch delay is consequently exact from the captured accumulator even though the following interval is not. The launcher is classified as wall_shooter with subtype level5_homing_missile_cannon and priority 6. Its transient missiles are classified separately as level5_curved_homing_missile at priority 15, so combat destroys the allocator before chasing its output; immediate collision survival can still override the firing lane.

In level5-cannon-priority-0829-92.x2events, the controller acquired the first launcher as soon as it entered the capture margin and kept a launcher target through the encounter. It destroyed three wall shooters. The run still lost its remaining life after the pre-existing missile population grew, so source classification is verified but complete Level-5 survival remains a separate task.

The survival model now preserves the game's two collision phases. The global player AABB in a captured frame is the rectangle already tested before the ship's current movement; the sprite-header offsets belong to the following collision pass. Candidate input therefore cannot retroactively move a frame-one collision hull. Future hulls use the exact one-frame input latch instead of translating the old global AABB to the new anchor.

$4FA1E is no longer extrapolated with one screen-space velocity. Each beam node stores missile age, compass direction, turn bias, world anchor and interaction rectangle. Every eighth update turns that state toward that node's predicted ship anchor, then applies the disassembled world delta. A cannon's exact next launch, derived from +$5E, is inserted into both new beam searches and retained-plan revalidation before its MyObject slot exists. Static Level-5 configuration-space contact participates in survival arbitration because wall replay removes control while the missiles keep turning.

Reachable launchers use level5_launcher_suppression: the controller stays below the source, uses a forward firing station, and does not replace the source with a pickup or one transient missile. "Reachable" requires a current weapon lane or a configuration-space preposition path. A merely visible launcher across a wall is still a survival hazard, but it cannot suspend navigation indefinitely.

The launcher tactic owns its preposition path end to end. A firing-lane path no longer renames it to generic target_preposition; both the immediate arbitration and the space-time beam use the same world-coordinate route segment or forward firing station. Once the path becomes a direct lane, horizontal control moves the ship anchor into the target's forward-cannon interval before adjusting the lower-screen hold band. This prevents the global level route or an equal-survival open-space choice from making the ship look as though it is trying to use Side Shot against a launcher above it.

The sequential validation recordings are level5-damage-fix-0829-94.x2events and level5-source-model-0829-97.x2events. Across the formerly fatal homing section the exact planner took no curved-missile hits and lost no life; one ordinary $4180 projectile and one scripted swarm body still caused 12 shield damage. Starting a fresh controller at that late checkpoint is intentionally not counted as equivalent validation: it discards the retained map and encounter state. level5-cannon-validation-0829-98.x2events also establishes the remaining failure, but the launchers were not actually isolated or unreachable. The destroyed horizontal laser at row 251 leaves a 32-pixel opening and the game's shifted ship mask can pass through it. A large comfort reserve may disconnect that opening even though the game hull fits. The planner therefore compares its normal 12/8-pixel routes with an exact-hull route whenever a verified-open gate is immediately ahead; the first route that actually crosses the gate is the topology proof.

While the ship is immediately below a verified-open destructible gate crossed by that route, destructible_gate_egress owns strategic movement until the complete ship hull clears the former wall row. It aligns with the route's crossing anchor and holds forward movement instead of rebuilding a changing launcher firing path. The Level-5 missile beam keeps physical wall intersection and missile collision as hard constraints, but it does not demand surplus wall manoeuvre clearance inside the aperture; after adequate missile clearance, progress through the opening is the ranking objective. The immediate sprite-mask scorer may disagree with the full configuration replay by one quantized pixel at the lip. For the exact forward edge of this verified route only, the full replay wins and Xenon's $42E wall result remains the runtime authority.

level5-gate-egress-0829-102.x2events verifies the topology fix: the controller crosses the former laser row, advances scroll from about 3865 to 3779, reaches the cannon region and destroys one cannon. It begins from an already damaged late checkpoint and still dies after nine to thirteen missiles accumulate, so complete encounter timing/survival remains separate from the now-correct gate traversal.

level5-launcher-alignment-0829-103.x2events verifies the firing-station handoff. On the first frame after gate egress (88427) the tactic is already level5_launcher_suppression, the weapon is forward, and the ship moves right. It advances from world X=110 to X=261, settles under the X=288 launcher, and the launcher changes to destroying_hostile at frame 88466 without shield loss during that approach. The late checkpoint starts with only 27 shield and the longer run still loses its final life after the number of live missiles grows to sixteen; that later multi-source survival problem is not evidence of firing-lane failure.

Launcher acquisition now minimizes estimated physical time to a safe forward firing lane. The estimate follows the same cached 4-pixel configuration-space path used by movement and divides each simultaneous X/Y leg by Xenon's measured 9/6-pixel actuator speeds. Fire-accumulator deadline and viewport membership are only tie-breakers: chasing the source that fires next is useless when a nearer source can be destroyed first, and the extended stream is intentionally used to align before a new cannon crosses screen Y=0. A changed route segment invalidates the old committed dodge prefix, so actions ranked for the previous waypoint do not pull away from the new firing column.

For Level-5 homing avoidance, the current screen Y is admitted when the ship is already above the preferred 136-pixel lower band. The previous fixed lower bound made every first beam action illegal and returned a zero-frame plan; the greedy fallback then kept moving UP into a launcher. Actual object/wall collision and projectile clearance remain ahead of attack time, but once those are satisfied, time to the firing lane ranks ahead of surplus wall reserve and lower-screen comfort. Level-5 launchers no longer fall back to Side/Rear targeting: suppression means reaching a forward-cannon station below the source.

level5-launcher-rush-0829-106.x2events is the accepted validation. Gate egress starts at frame 88378 and completes at 88384 instead of taking roughly forty frames. A new right-wall cannon exposed at 88400 is acquired immediately; the ship reaches its forward lane at about 88418. Over the bounded 250-frame run the controller destroys three launchers and takes no additional damage after the checkpoint's earlier scripted-swarm hits. Runs 107--109 tested stronger identity commitment and pre-gate-source deferral, but both reduced suppression and were removed rather than retained as encounter-specific exceptions.

Level 5 has a second authored destructible-barrier family in addition to the five $50DAE horizontal lasers. The backward 14-byte spawn table at the pointer in $4F014 uses record types 3 and 9 for $5126A/$51250. Its status is $00F4, update procedure is $5131E, hit procedure is $514AA, and object +$46 holds the resident tile pointer. Final destruction clears a 2x2 tile block (the next row is +$28 bytes), opening a 32x32 passage. All twelve authored barriers are tagged in assets/xenon_level_maps.json; snapshot RAM and complete live tile observations may then change only those tagged cells from solid to open.

A third family uses backward-spawn record type 4. $51000 installs update $510A2 and hit $5119C; on death, clr.l (A2) and clr.l $28(A2) clear two adjacent columns on each of two map rows. Five such 2x2 barriers are now derived from the spawn table and tagged in the same asset. They connect route seams near world Y 2960, 2880, and 2760 which previously appeared as separate offline components. The remaining full-width break at tile rows 139..152 is an authored arena/shop transition and is displayed as a scripted transition, not a traversable ship corridor.

Both Level-5 2x2 families can use level5_barrier_clearance, but sharing an update procedure does not make every instance a route gate. Route ownership requires two independent facts: the retained path has not yet advanced a full ship height beyond the tile row, and the local path tube passes within 32 world pixels of that controller's resident footprint. This separates a real seal from a same-row shooter mounted on a remote wall.

A route-blocking instance uses the permanent forward gun and forward Cannon mounts. The planner searches the complete 4-pixel configuration space for a safe station below the target and retains that world-coordinate route, permitting necessary down/across/up detours around a sealed bend. A bypassable instance is still a hostile predicted projectile source. It can be attacked by Side Shot when the live horizontal ray is clear, but it cannot acquire a forward-cannon preposition mission or steer the ship away from the valid navigation corridor.

The route is also constrained by Xenon's camera state. Every station and every intermediate A* waypoint must be no farther back than scrollBackwardLimit + 176; resident geometry beyond that Y remains in memory but is no longer reachable by the ship. A reachable barrier therefore owns the strategic mission before optional pressure relief, pickup collection, or ordinary swarm tactics can consume the sixteen-pixel back-scroll window. Open-gate egress still precedes the next barrier. Swarm/projectile prediction retains movement veto inside the mission instead of erasing the mission and its deadline.

$5131E uses object +$5E as a fire accumulator. It adds the byte at $4F065 ($12 in the captured Level-5 image); carry emits one projectile and replaces the accumulator with RandomByte() & $3F. Object +$2A is an eight-direction trajectory index which turns one step per update toward the player and selects the spawn offset/projectile routine. This explains the recurring aimed volleys seen while approaching a barrier and is annotated in the ReVa database.

$510A2 has the same accumulator layout and $12 increment, but a carry emits all eight $4180 directions from the centre of the 2x2 tile pair at speed 10. The canonical object envelope already captures +$2A and +$5E, so no protocol extension is necessary. WorldState now exposes the two timers and the $5131E direction semantically. Before allocation, survival advances the captured direction one octant per intervening update toward the current player anchor and inserts the next speed-8 aimed shot or eight independent speed-10 radial sweeps into world-coordinate space-time model as live projectiles. Keeping the radial members separate is important: their bounding union would incorrectly turn the harmless interior of the volley into a solid hazard square.

level5-barrier-clearance-0830-125.x2events, started from the earlier run-113 checkpoint while the firing routes were still camera-reachable, is the first end-to-end validation of the generalized policy. It removes seven wall shooters/ barriers and crosses multiple newly opened passages while advancing scroll from about 3234 to 2703. It loses no life, but takes 20 shield points from three contacts; barrier discovery/navigation is validated, while volley survival still needs improvement before this run is a no-damage acceptance test.

Run level5-prefire-prediction-0830-126.x2events validates the pre-allocation model from the same run-113 checkpoint. It destroys eleven wall barriers without a life loss. None of its six damage events comes from a speed-8 $5131E or speed-10 $510A2 projectile; five are speed-6 projectiles from the existing scripted/swarm family and one is direct swarm contact. The barrier-volley omission is therefore fixed, but the run is deliberately not claimed as a general no-damage Level-5 acceptance test.

Run 126 also exposed a separate mission-ownership error after those barriers opened. destructible_gate_egress retained unconditional strategic precedence while a six-member $E1E stream was already predicted to cross that connector. The 48-frame space-time search knew about the bodies and their speed-6 aimed $4180 allocations, but its route destination still pulled the ship through the opening; the resulting incomplete plan eventually committed a long neutral prefix and took five projectile contacts plus one direct member contact.

An open connector has no destruction deadline. Once no reachable Level-5 progression barrier remains, the controller therefore evaluates that connector with the 48-frame swarm horizon. A predicted crossing temporarily selects swarm_escape while leaving the same egress polyline retained; the normal 24-clear-frame swarm latch prevents a gap between followers from releasing it. When the stream clears, destructible_gate_egress resumes the unchanged route. The rule is deliberately disabled while a reachable barrier remains, because barrier suppression is time-bounded by the camera window.

level5-speed6-post-barrier-final-0830-133.x2events validates the handoff: after the last early barrier contact at frame 90179, the post-barrier stream and its speed-6 shots cause no further damage through frame 90279, and the open-gate route resumes after the stream clears. This run is not a no-damage Level-5 acceptance test: its independently earlier barrier-clearing trajectory took 12 shield damage from three speed-8 projectiles before the new policy was eligible. Synchronized original and Sprite Stream AVIs accompany this final validation.

The remaining speed-8 contacts were not a barrier-timer error. $5131E turns object +$2A toward the live ship on every update before the +$5E carry, so the future shot depends on the complete pre-allocation ship path. The older predictor advanced the direction toward one frozen current-player anchor. The space-time beam now rolls the direction through every world-coordinate ship anchor in each candidate path and includes that direction in its deduplication state. Histories which bait the same barrier differently can no longer be merged merely because the ship later reaches the same position.

There is a second survival invariant at the beam boundary. If an incomplete beam does not outlive the best legal constant input, rejecting that beam is correct, but control must pass to that longest-lived constant input. It must not return to route-local one-pulse arbitration. Run 134 exposed the old hole at frame 90156: the beam proved 14 safe frames and declined ownership because DOWN-LEFT survived 17, after which the route selected RIGHT with a predicted impact at +3. The fallback now compares full dynamic and static survival for one newly replanned input frame.

level5-barrier-swarm-acceptance-current-0830-137.x2events is the deterministic acceptance run from level5-passage-fix-0830-113.sav. Across 240 canonical frames it destroys three barriers, crosses into the following swarm encounter, and keeps shield at 35 with zero hits and zero lives lost. Synchronized -original.avi and -sprite.avi recordings accompany it. A representative frame-90123 profile is saved as level5-barrier-frame-90123-optimized.prof; the exact 48-frame beam is still the dominant combat cost, while the path-dependent barrier rollout itself is a much smaller component and caches equivalent direction histories.

level5-passage-fix-0830-116.x2events validates this second family at the first left opening. The ship aligns from world X=24 to X=51, advances from world Y=3411 to Y=3344 in 17 canonical frames, and hands control back after the full hull clears rows 210--211. Shield remains 35 and no life is lost. Two controller bugs were necessary to expose: the route selector must not accept a short high-reserve frontier below a known-open gate when a narrower route crosses it, and the one-frame alignment/UP pulse must not be evaluated as though the same input were held past the next route bend. The crossing is revalidated every canonical frame; moving hazards still have veto authority.

Stable route tracking through bends

The route follower classifies a bend from the previous route segment to the waypoint, not from the ship's sub-tile position to that waypoint. The old test changed a gradual bend into a sharp corner whenever the ship crossed a small X threshold, causing alternating horizontal commands at an unchanged route. Candidate route progress is now compared at one common frame horizon. Within a bounded tracking tube, forward progress along the polyline is preferred before minor centreline error; this prevents a DOWN-at-bottom candidate's scroll setup lookahead from being compared against one-frame lateral candidates.

One Level-5 route contract

Level 5 now migrates route-bearing movement behind Level5RoutePlan without changing the established controllers for Levels 1--4. A plan contains the world polyline, its monotonic waypoint index, purpose, target/group, weapon direction, the exact fixed $387C hull signature, selected wall reserve, and persistent-map revision. Navigation, resident-barrier preparation, and live firing-station paths all publish this same structure and use one configuration-space follower.

This fixes a model error rather than adjusting target scores. Previously a firing path could be found only with zero extra reserve, after which static prediction and the survival beam silently restored the generic 8- or 12-pixel reserve. Those layers correctly rejected a path in their different configuration space and chose DOWN/neutral/LEFT-RIGHT alternatives, producing an apparent target chase beside a diagonal wall. Candidate scoring now obtains its reserve directly from the active plan. A* path, follower, static lookahead, and survival therefore answer the same wall question.

The first live validation exposed a second owner that the initial migration had not removed. After the Level-5 follower published level5_route:barrier_attack, the generic strategic constraint intersected its choices with the older global recovery path and changed movement_goal back to navigation_route. Survival then correctly reported mission_goal_changed and followed the wrong route. An active Level-5 contract is now explicitly exempt from that pass; the cyan route remains visible context but cannot overwrite the orange movement goal.

The plan is retained across ordinary frames and one-frame survival overrides. Changing object animation does not rebuild it. If the persistent map revision changes, every remaining segment and the live rejoin connector are revalidated with the same pixel mask and reserve; leaving the route tube, losing the target, changing the weapon mission, or exceeding the camera backtrack window invalidates it explicitly. Full segment revalidation is not repeated when the map is unchanged. The JSONL trace records the complete level5_route_plan, and the inspector's L5 ROUTE, L5 CONTRACT, and L5 PROGRESS rows expose purpose, last event, target, weapon, reserve, hull, map revision, and waypoint. Cyan and orange routes must not be interpreted as two simultaneous movement owners: cyan is the global candidate route; orange is the active contract.

Fire is modeled as one global trigger, not as a selected weapon. For a Level-5 barrier the planner enumerates ordinary forward fire, every live Laser/Cannon mount offset, and both Side Shot directions. Each candidate contains a full-hull configuration-space path to its firing region and records every installed stream which intersects the target from that station. The chosen weapon_direction is only the route-producing lane; FIRE LANES and the JSONL firing_directions field show all simultaneous contributors.

Weapon reach uses the collision routines' real viewport rules rather than the renderer look-ahead band. Ordinary bullets are discarded outside 312x192 before collision; Cannon retains its 13-pixel sprite overreach above the anchor; Laser clips a crossing beam at the top edge but cannot hit a target wholly above it. If survival prevents progress toward a geometrically valid station for twelve observed attack frames, the dominant approach direction is cooled down for 96 frames while the shooter remains a hazard. The next plan must use an orthogonal or opposite approach (often a Side Shot row) instead of oscillating toward the same dynamically blocked forward station.

level5-unified-route-0831-45.x2events validates the corrected ownership from the same pre-diagonal checkpoint used by failed run 44. Across 180 canonical frames the active barrier goal is never replaced by navigation_route; the controller destroys three wall shooters, gains 1,600 points, and retains all 35 shield with no life loss. Both synchronized AVI streams and the JSONL contract trace accompany the recording.

Run 46 exposed an earlier sequencing error. While the row-164 $5131E pair was still represented by unconsumed scroll-spawn records, generic spawn pacing descended beside the later A8 continuation route. It reached X=54; the installed Cannon lane for the selected left controller was X=72..80. Realignment under the newly allocated projectile field took 51 camera pixels and made the central return dogleg unreachable. Pending Level-5 directional records now own a pre-allocation pacing station: the resident trigger selects the controller row, the real weapon offsets select its X interval, and configuration-space A* reaches that lane while descending to the bottom screen band. The global A8 path remains visible context but cannot own movement until the row has allocated and been cleared.

That selected suppression column is also mission authority after allocation. A configuration-space path to a different same-row controller proves only geometric reachability; it does not authorize abandoning the retained column while both controllers are firing. Mandatory route blockers may still request their exact forward-cannon path. Other peers remain in projectile prediction and may be hit by Side Shot, but cannot acquire forward preposition unless they lie in the latched column. level5-prespawn-firing-lane-validation-0831-49.x2events validates this handoff: the controller destroys both left-column shooters plus one earlier source, advances through the connector, and retains all 35 shield across 300 canonical frames. The corresponding run 48 selected the opposite column, became pinned at world X=14, and took four damage; it is retained as the negative comparison.

The row-172 encounter now has an explicit second phase. An exact full-hull X/Y crossing ends the lower-platform suppression contract; the ship then remains in the intermediate chamber while the right stack is live. Forward positioning is retained when it is still needed, but a remaining source beside the ship is held and fired on with Side Shot. A failed one-frame A* refresh cannot erase the established firing route or release map_advance; only disappearance of the live controller completes the combat phase.

This is not based on an assumed 16-pixel allowance. The live protocol exposes CameraBacktrackWindow_cee, and run 46 reports $CEE=$0010 throughout the encounter. The game routine at $7B5A applies $CEA = min($CEA, newScroll + $CEE) during ordinary forward progress; $66C6 permits DOWN backscroll only while $CD0 != $CEA. The allowance is therefore a rolling camera leash which can be lost while fighting, not an indefinitely reusable 16-pixel movement command.

Level-4 chained-head boss

The Level-4 overlay stores interior continuation addresses from one monolithic boss routine in MyObject.updateProc. WorldState recognizes the actual live entrypoints rather than only the outer $505AA initializer: $5060C/$5072C are the primary anchor/chain, $50782 is the main head, $507F6 is its damaging face, $509DC/$50A50 are the secondary anchor/chain, $50A60 is the secondary small head, and four $50AEE objects are body heads. The chain predecessor at object +$0E and health at +$32 are exposed in traces and the replay UI.

The hit callbacks establish a strict phase order. Death of $50A60 and each $50AEE decrements the shared counter at $D82; $50554 rejects main-head damage while that counter is nonzero. The controller first passes the large swinging face. It then clears the four fixed $50AEE heads as two world-space Side Shot rows: lower row first, both X=120/200 members while holding one Y, then the upper row. The retained level route is suspended during these firing stations so it cannot pull the ship past a live row.

The fifth $50A60 head is not another fixed row. It swings on the upper chain, so the controller holds the exterior refuge and fires across a fixed orbit lane at secondary-anchor Y - 76; it does not chase the animated head coordinate. Only after all five heads disappear does it target the main head. The entry bypass is complete as soon as any small head has been destroyed; later back-scroll must not reactivate it.

Installed attachments are not added to wall configuration space. Navigation uses the game's fixed $387C nonzero player mask bounds (-14,-14)..(15,10); the animation-dependent interaction rectangle remains authoritative for object damage. For the main phase, the preferred route stays at the upper-left refuge lane (world X 24..36, screen Y≈40). A right Side Shot is fired horizontally across the head; the spent eyes and the tongue are lower than this stream in the captured collision geometry, so they do not consume it. The encounter keeps the target even while it is off-screen and preserves the exterior-X invariant against generic route and short-horizon dodge preferences. Loadouts without a right Side Shot retain the older below-anchor Cannon station as a fallback. Near-term face prediction uses live world velocity because long-history smoothing hides the rapid stationary-to-swing transition.

level4-boss-0828-22.x2events clears all four fixed heads with zero damage. Run 24 holds the fifth-head orbit lane and reduces it to three health without contact; the following checkpoint finishes it. Runs 26 and 27 cross below the main pendulum in the left refuge and destroy the boss without losing a life. Across the staged validation from run 20 through run 27, shield falls from 27 to 15 through three four-point projectile hits; there is no head or chain contact. Run 28 explicitly tested following the main head's Y while holding X=20. It is unsafe: at frame 67166 the visible $50782 head was still separated, but the separate $507F6 damaging face occupied X=30..43 while the ship occupied X=10..31. The one-pixel overlap caused 16 damage, followed by a four-point projectile hit. The controller therefore keeps the upper Side Shot lane fixed instead of following the animated head.

Level-4 post-shop eye/head boss

The later three-eye head is a separate monotonic encounter. Disassembly shows that the two side eyes use $4FF3C while vulnerable and $4FF0E after being spent. Their hit callback $4FC1C decrements both object health at +$32 and the shared remaining-eye counter at $D84. The main $4FE74 hit rectangle is disabled while $D84 is nonzero, so targeting the centre before both eyes are spent cannot advance the fight.

The safe refuge is the reachable world-X interval 24..36. At X=36 the player's live hull ends at X=47, while the observed chain reaches X=50. The pilot remains there while the tongue is exposed and may choose DOWN among equally safe moves at the lower screen edge. This counters automatic forward camera pressure only while the game still permits backscroll; projectile survival retains authority. The tongue is considered fully hidden only when all sixteen chain collision rectangles occupy one 16-pixel vertical band. Any larger span is an exposed or returning chain, so attack movement is forbidden. The earlier upper-left transition is an intentional exception to the old lower-refuge staging rule: the persistent span route moves around the known wall bend before the first directional projectile wave reaches the lower band. Emergency collision avoidance still owns a frame when the route itself is not legal.

During the eye phase, the right Side Shot can hit each active eye from the refuge; the selected eye changes only after its hit procedure becomes the spent form. During the main phase, the same stream remains useful from the upper lane: captured frames 82646--82647 show Side Shot bullets reaching the main rectangle and reducing health from 100 to 96 while the ship stays above the spent eyes. The controller therefore moves to that lane immediately and keeps FIRE active; damage still occurs only during the head's vulnerable animation windows. A loadout without a right Side Shot uses the retained Cannon timing and retracted tongue interval as a fallback.

Replay performance note: the default Recorded replay view reuses the captured Level-4 boss decision rows from the JSONL sidecar instead of running the nine candidate hazard scorer again for every boss frame. Optimized and Reference views continue to recompute decisions so they remain useful comparisons.

level4-stage2-boss-refuge-0828-82.x2events validates the full eye phase and safe transition to the centre phase with shield 15 and no damage. Starting from that state, level4-stage2-main-aim-0828-83.x2events reduces the main health to zero, reaches the following shop at frame 85981, and still finishes with shield 15. The post-boss checkpoint is level4-stage2-boss-cleared-0828-83.sav.

Level-5 tank boss

The tank is a composite encounter, not a collection of generic wall targets. The Level-5 descriptor table at $50538 creates a non-damageable $508E0 controller, four damageable $507DA cannon children (hit callback $50870, initial health 40), four non-damageable $5078C armor children, and one damageable $506DE central core (hit callback $50722, initial health 200). The tracker exposes these as level5_tank_controller, wall_shooter/level5_tank_cannon, level5_tank_armor, and level5_tank_core. Armor is a contact hazard but never a target.

The encounter has a monotonic target order:

  1. The pending script record with path index 110 identifies the arena before the composite objects allocate. Run 69 located the last lower-band crossing at scroll 2540, 140 pixels before its 2400 trigger; the preparation window is therefore 144 pixels, not the original too-late 96. An already acquired entrance takes precedence over the pending spawn: cross the nearest side's two verified-open $5131E rows with continuous UP. These are semantic resident-map groups (r164 then r162 at column 6 or 12), not per-session object identities. During this bounded crossing, its collision-checked route overrides the generic screen-Y reaction-room preference; predicted physical contact still has absolute veto.
  2. Latch the side of the corridor actually crossed and destroy that side's $5131E emplacement from below with the nearest valid Forward/Laser/Cannon lane. Preparation never recomputes “nearest side” after a survival displacement. If the requested DOWN+LEFT/RIGHT diagonal is wall-blocked but its horizontal component is safe, retain the horizontal progress instead of broadening to neutral or the opposite direction. When only the opposite emplacement remains, use Side Shot only if the live world-space row already intersects it. Camera scrolling cannot create that alignment by itself because it advances the player's world Y too. Otherwise commit a lower-band cross-arena transfer toward its forward firing station, with survival choosing the safe vertical component but not changing the destination.
  3. Destroy the four mounted tank cannons. Their $507DA anchors are screen-space child coordinates even though they use the wall-shooter class; translate them by camera scroll at the perception boundary. Choose one volley station across the complete live cannon set, maximizing distinct mounts hit before travel distance. Latch that station and the set of mounts it can hit until every live member of the set disappears: a projectile dodge may temporarily displace the ship, but must not redefine the firing objective. Mounted-cannon and core combat use an explicit attack -> evade -> return state machine. attack holds that station and observes the live +$32 health counter. An imminent predicted contact enters evade and commits a bounded six-frame safe prefix through the closest approach. When that prefix ends, return makes the same station mandatory until it is reacquired; only then does attack resume. The generic space-time beam therefore solves collision geometry but cannot silently redefine the combat mission. Run 81 destroyed the first two cannons with shield still 35. Transferring to the remaining pair under the accumulated projectile field is still an open encounter phase; a speculative cross-tank first pair was tested in run 82 and rejected because reaching it caused damage before either first mount died.
  4. Attack the central core only after no mounted cannon remains. Core station selection minimizes estimated time to kill, not horizontal travel distance: it sums the sustained damage of every forward-going stream intersecting a candidate world-X lane, adds one-time travel and central-column evade/return cost, and latches the winner across attack/evade/return. For the Level-5 run-162 loadout, the permanent forward gun deals 3 damage about every two canonical frames, versus Cannon's 2 about every 4.5 frames and Laser's 2 about every eight. The resulting core station is world X 155.4995; a survival displacement cannot replace it with the nearby X=126 Cannon lane.

The Homing Missile released between the side-emplacement and mounted-cannon phases is deliberately skipped when Side Shot is installed. Both occupy a wing mount; collecting Homing Missile replaces Side Shot and removes the two lateral streams which help intercept the tank's approaching missiles. Pickup desirability therefore includes replacement cost instead of treating every attachment as an upgrade. If no valuable wing attachment would be displaced, the existing exact $4EA0 timer/direction rollout remains available for deadline-aware collection.

Every phase uses actual collision-viewport membership rather than the renderer's extended object band. The lower-screen camera hold belongs only to the entrance and side-emplacement phase. Once those sources are gone, an off-screen tank is a one-sided fight: it continues allocating missiles while no player stream can hit its weak points. Cannon/core reveal therefore drives UP to screen Y 112, holds neutral there while automatic scroll exposes the next weak point, and requires 24 pixels of collision-viewport depth before entering the firing phase. A one-pixel intersection is not stable: the first DOWN frame would hide the vertically oscillating mount again. The firing phase then holds DOWN to regain the lower reaction band. Generic screen-Y recovery and indefinite repeated-input corridor lookahead are disabled inside this arena contract; immediate wall blocks and the projectile planner remain authoritative. Survival may still override a concrete frame, but only the explicit state transition can release and later reacquire the station; it cannot promote armor/the core prematurely.

The general 32-frame hazard horizon is also inappropriate for a tank which continuously replenishes projectiles: it made some future threat own nearly every frame and starved the attack. Tank cannon/core combat enters its six-frame committed evade only for contact predicted within 12 frames. Outside that window attack continues firing; after the closest approach, return reacquires the same station. This is a tank-only engagement deadline, not a reduction of the global formation or corridor survival horizon.

Reveal may compute a provisional horizontal lane, but it does not latch the durable volley station. Survival and camera motion can displace the ship greatly before a weak point is truly reachable. On the reveal-to-combat boundary the station is recomputed from the ship's actual position, then retained across attack/evade/return. Run 120 changed the obsolete pre-reveal X=197 station to the reachable X=114 left-mount station, reduced total cannon health from 160 to 130 in 400 frames, lost 12 shield, and lost no life. This is an improvement, but not yet a complete tank kill; the retained station still spends too much time in return under the accumulated projectile field.

Two follow-up experiments were rejected. Keeping the attack phase latched across the tank's complete vertical bob reduced damage to 28 health over 500 frames and left shield 7. Replacing station steering with a one-transition endpoint rank revealed the tank faster but caused zero damage and lost the life in run 123. Neither mechanism remains in the controller.

Tank missile defense distinguishes collision behavior, not sprite appearance. The curved $4FA1E missiles use the ordinary damage callback (proc3=$0E2A), start with six health, and can be destroyed by Side Shot. The planner advances the exact missile state and the live/future left/right bullets together; a bullet can damage one missile only and is consumed on contact. Straight $4180 shots and the growing $50B38 central column have no-op hit callbacks and remain hard obstacles. Thus Side Shot creates additional safe trajectories but is not incorrectly treated as a shield against every tank projectile.

The tank beam keeps two ranks. Intermediate pruning preserves simple evasive branches, including paths which temporarily move away from the weapon lane. After all surviving nodes have the same proven horizon and at least 48 pixels of lateral reserve, terminal selection minimizes distance to the retained firing station before turn count. The threatened mission-aligned command—not a greedy edgeward fallback—is used to trigger this joint plan. Generic single-projectile lateral hints remain diagnostic during tank combat and cannot overwrite the station; the beam already models the complete projectile set.

level5-tank-side-defense-validation-0901-136.x2events validates this combined behavior. No curved missile hit the player, six selected paths credited an exact Side Shot interception, mounted-cannon health fell from 160 to 75, and no life was lost during 320 canonical frames. Shield still fell from 35 to 3: the first two eight-point contacts were already imminent in the checkpoint, followed by two six-point $50B38 central-column hits and one four-point $4180 straight shot. Those non-destructible hazards remain the next tank-defense problem. Firing stations are derived from every equipped forward-going lane, not just the main gun. For example the current right Cannon attachment is +26 from the ship, so its valid ship interval for the right emplacement's X=260..284 rectangle is X=234..258. The controller chooses the nearest point in that interval (X=234 when approaching from the left), rather than the rectangle center X=246 or the main-gun center X=272. This reduces steering inertia and remains farther from the wall. The selected Forward/Laser/Cannon station owns the movement goal and the diagnostics name the lane actually expected to intersect the target. The nearest-point rule in this example applies to ordinary emplacements and the mounted-cannon volley phase. The high-health central core instead uses the time-to-kill calculation above; otherwise proximity systematically selects a slow attachment for a long fight. The cannon and core hit callbacks decrement the unsigned word at object +$32. That live health is exposed in traces and in the replay object's health column. Volley geometry still maximizes the number of mounts and weapon streams hit, but among equally productive visible mounts it names the one with least health so a nearly destroyed component is finished. An off-screen member may still be hit by the same volley, but cannot become the named target and spuriously switch the phase back to cannon_reveal.

level5-tank-core-dps-validation-0902-164.x2events validates the new core objective from the final-cannon checkpoint. The station remained latched at X=155.4995 and the named weapon remained forward through large evasive displacements; core health fell from 170 to 156. This run did not defeat the tank: after frame 92407, existing survival/camera arbitration kept the ship away from the retained station, returned the phase to core_reveal, and the last life was lost to curved missiles. That is a remaining station-reacquisition/control problem, not evidence for returning to the lower-DPS Cannon lane.

level5-tank-fast-egress-validation-0901-106.x2events validates the revised entry contract. Frames 91441--91455 hold UP through the two left entrance rows; the first side emplacement disappears by frame 91565, the ship immediately crosses the arena, and the second disappears by frame 91625. Shield remains 35 throughout.

The controller also emits a dedicated $50B38 central projectile which cannot be inferred from ordinary velocity. Its object state exposes a collision extent at +$28 and signed vertical step at +$2A. It grows at a fixed world position by 16 pixels per frame to a maximum 48-pixel column, then falls by +$2A in screen space (normally 16), making subsequent world motion depend on the candidate camera trajectory. The tracker classifies it as level5_tank_projectile, never as a target, and survival predicts this exact grow-then-fall AABB for 20 frames.