Xenon 2

Autopilot · write-up

Native candidate-scene preparation

xenondoc/AUTOPILOT_NATIVE_CANDIDATE_PREPARATION.MD · 5 KB · updated 2026-09-17

Changes

ABI 16 adds XapPickupMotion and xap_prepare_pickup_paths. Python captures one descriptor per pickup; one C call fills the caller-owned pickup-major rectangle buffer for the whole candidate horizon. C advances ordinary linear movement and released equipment's approach/orbit/drop state machine. Compass geometry remains in screen coordinates; candidate evaluation applies its predicted scroll as before. This changes preparation only: collection rules, avoidance and scoring stay intact.

Candidate shell preparation borrows the existing ShellPaths.points pool when all requested shells and frames are covered. Shell descriptors use that pool's actual offsets, even if the scene uses only a subset of its shells. Mixed scenes bulk-copy available paths into a contiguous buffer and predict unsupported shells in Python. No per-frame tuples or point objects are created for supported shells.

The Python reference paths remain available. Fresh-process comparison variants:

  • c-candidate-preparation-old: prior shell and pickup preparation.
  • c-candidate-shell-reference: Python oscillator preparation matching native motion.
  • c-candidate-shell-reuse: shell reuse only, Python pickup preparation.
  • c: shell reuse and native pickup preparation.

The API retains no pointers, performs no allocation and has no Python callbacks. It remains callable through ctypes, with a standalone WASM export; no Hatari driver integration or WASM performance testing was added.

Validation

Differential tests cover mixed linear/compass pickups, every direction, orbit timer boundaries, the drop transition, fractional observed coordinates, nonzero world scroll, invalid capacity, empty batches, borrowed shell offsets and mixed fallback. Replay comparison results are recorded below after measurement.

Shell prediction correction

The old candidate anchor path used the controller's linear displacement predictor, although body collision already used the captured shell oscillator. Borrowing the native pool corrects burst-release positions around reversals. Compared with the old implementation, the Level 1 recording has 32 changed verdict frames, including 24 changed actions, concentrated at frames 7520–8087. These are expected model changes, not an exact-equivalence result or proof of better live completion.

For a fair performance comparison, c-candidate-shell-reference uses the existing standalone Python oscillator for eligible shells, retaining per-frame prediction and packing. It matches the native model; all three same-model variants have zero changed verdicts over both Level 1 repeats (7,693 observations each).

Unprofiled Level 1 timings

Sequential workers pinned to logical CPU 4, reversed order on repeat two:

Preparation Run 1 mean ms Run 2 mean ms Average ms
Python oscillator and pickup preparation 3.879 3.852 3.865
Shared shell paths only 3.758 3.826 3.792
Shared shells and native pickups 3.755 3.761 3.758

The same-model end-to-end replay improvement is about 2.8%. Shell reuse saves about 1.9%; pickup batching adds about 0.9% relative to shell reuse. The latter is small compared with shell-only run variation, so its standalone timing benefit remains tentative; removed calls are checked separately with cProfile.

The old-linear comparison averaged 3.907 ms versus 3.732 ms (4.5% faster), but includes changed decisions and is not the isolated optimization estimate.

All 911 tests pass, including shell reversal differential tests. No new live level completion or phone/WASM speed claim is made by these replay measurements.

Separate profile

Against the same-model Python reference, shell reuse removes 134,862 Python oscillator calls. Pickup batching removes all 218,848 candidate pickup_rect queries, replacing them with 2,033 batch calls. Total recorded Python calls fall from 138,932,292 to 138,003,170 with shell reuse and to 135,177,105 with both changes. The native pickup wrapper takes 0.041 cumulative seconds in the instrumented run; the previous pickup queries take 0.697 seconds. These overlapping profiler costs must not be added to estimate end-to-end gains.

Candidate preparation's remaining 1,289 anchor calls equal its 1,289 aimed-shot models. Thus no shell fell back to Python in this Level 1 recording; unsupported shell handling is a compatibility path, not an observed unknown shell type.

Artifacts under xenon_tools/run_logs/:

  • candidate-preparation-same-model-timing-0907/comparison.json: repeated timings.
  • candidate-preparation-profile-0907/: separate three-way profile.
  • candidate-preparation-timing-0907/comparison.json: old linear model comparison, intentionally reports changed verdicts and exits with the comparator's error.
  • candidate-preparation-final-tests-0907.txt: 911 passing tests.

Cross-level replay checks

Single same-model comparisons retain identical verdicts in the available Level 2–5 recordings. Their timings are validation samples, not repeated speedup estimates.

Recording Python preparation ms Native preparation ms Changed verdicts
level2-homing-dual-collision-0824-60 6.367 6.250 0
level3-stage2-smallshot-0826-213 8.497 8.540 0
level4-wall-model-0827-07 6.988 6.839 0
level5-tank-exact-homing-dev-0901-157 2.603 2.609 0