Autopilot · write-up
Native candidate-scene preparation
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 |