qvm: hitscan rejection sampling
Browse files- docs/ASSET_PIPELINE_TODO.md +3 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/QVM_SPREAD_SAMPLING.md +7 -0
- docs/research/README.md +3 -0
- docs/research/qvm-spread-sampling.json +0 -0
- scripts/analyze_qvm_spread_sampling.py +28 -0
- scripts/test_qvm_spread_sampling.py +25 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -511,3 +511,6 @@ Research progress: RE-609 [report](research/QVM_SPREAD_PARAMETERS.md); remaining
|
|
| 511 |
|
| 512 |
|
| 513 |
Research progress: RE-610 [report](research/QVM_SPREAD_SPEED.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 511 |
|
| 512 |
|
| 513 |
Research progress: RE-610 [report](research/QVM_SPREAD_SPEED.md); remaining native/content/runtime/platform scope stays open.
|
| 514 |
+
|
| 515 |
+
|
| 516 |
+
Research progress: RE-611 [report](research/QVM_SPREAD_SAMPLING.md); remaining native/content/runtime/platform scope stays open.
|
docs/FINDINGS.md
CHANGED
|
@@ -869,3 +869,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 869 |
|
| 870 |
|
| 871 |
**RE-610 — Hitscan movement speed normalization and airborne floor:** 290493 normalizes velocity, applies an airborne ID14/23 floor below224.4, caps at340, then converts speed before its200 split. [Evidence](research/QVM_SPREAD_SPEED.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
|
| 870 |
|
| 871 |
**RE-610 — Hitscan movement speed normalization and airborne floor:** 290493 normalizes velocity, applies an airborne ID14/23 floor below224.4, caps at340, then converts speed before its200 split. [Evidence](research/QVM_SPREAD_SPEED.md).
|
| 872 |
+
|
| 873 |
+
|
| 874 |
+
**RE-611 — Hitscan rejection sampling and RNG consumption:** Each spread candidate consumes four actual36717 draws; acceptance requires radius²<1 and radius²>local40×0.15. Zero RNG state never escapes its rejection loop. [Evidence](research/QVM_SPREAD_SAMPLING.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -831,3 +831,6 @@ Research progress: RE-609 [report](research/QVM_SPREAD_PARAMETERS.md); remaining
|
|
| 831 |
|
| 832 |
|
| 833 |
Research progress: RE-610 [report](research/QVM_SPREAD_SPEED.md); remaining native/content/runtime/platform scope stays open.
|
|
|
|
|
|
|
|
|
|
|
|
| 831 |
|
| 832 |
|
| 833 |
Research progress: RE-610 [report](research/QVM_SPREAD_SPEED.md); remaining native/content/runtime/platform scope stays open.
|
| 834 |
+
|
| 835 |
+
|
| 836 |
+
Research progress: RE-611 [report](research/QVM_SPREAD_SAMPLING.md); remaining native/content/runtime/platform scope stays open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -737,3 +737,8 @@ Full-pool retry relaxes the recent-free guard; source count1022 enters diagnosti
|
|
| 737 |
## 2026-10-20 — RE-610 Hitscan movement speed normalization and airborne floor (separate task commit)
|
| 738 |
|
| 739 |
290493 normalizes velocity, applies an airborne ID14/23 floor below224.4, caps at340, then converts speed before its200 split. 48 velocity/ID/ground fixtures reproduce source-order f32 math independently, including the discontinuity at224.4. Full suite: 941 tests pass. Report regenerates byte-identically (101488 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 737 |
## 2026-10-20 — RE-610 Hitscan movement speed normalization and airborne floor (separate task commit)
|
| 738 |
|
| 739 |
290493 normalizes velocity, applies an airborne ID14/23 floor below224.4, caps at340, then converts speed before its200 split. 48 velocity/ID/ground fixtures reproduce source-order f32 math independently, including the discontinuity at224.4. Full suite: 941 tests pass. Report regenerates byte-identically (101488 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
| 740 |
+
|
| 741 |
+
|
| 742 |
+
## 2026-10-20 — RE-611 Hitscan rejection sampling and RNG consumption (separate task commit)
|
| 743 |
+
|
| 744 |
+
Each spread candidate consumes four actual36717 draws; acceptance requires radius²<1 and radius²>local40×0.15. Zero RNG state never escapes its rejection loop. Independent PRNG/f32 sampling oracles check every draw and candidate across12 seed/counter fixtures, with explicit32-attempt exhaustion. Full suite: 945 tests pass. Report regenerates byte-identically (142282 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -874,3 +874,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 874 |
|
| 875 |
|
| 876 |
- [x] RE-610 Hitscan movement speed normalization and airborne floor ([report](research/QVM_SPREAD_SPEED.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 874 |
|
| 875 |
|
| 876 |
- [x] RE-610 Hitscan movement speed normalization and airborne floor ([report](research/QVM_SPREAD_SPEED.md)).
|
| 877 |
+
|
| 878 |
+
|
| 879 |
+
- [x] RE-611 Hitscan rejection sampling and RNG consumption ([report](research/QVM_SPREAD_SAMPLING.md)).
|
docs/research/QVM_SPREAD_SAMPLING.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Hitscan rejection sampling and RNG consumption
|
| 2 |
+
|
| 3 |
+
RE-611. Reproduce with `python3 scripts/analyze_qvm_spread_sampling.py`; check with `python3 scripts/test_qvm_spread_sampling.py`. The [machine report](qvm-spread-sampling.json) retains source identity and bounded fixtures.
|
| 4 |
+
|
| 5 |
+
The sampler executes actual290995–291078 with actual36717 for every call. Two sums of centered low15-bit samples form x/y; both radius boundaries are strict. Source291079 loops back to290995 when rejected; the bounded harness restarts at that exact point and retains RNG state. It stops at291081 when accepted. The actual parameter/speed prefixes establish local40; the intervening290969–290994 branch only alters local44 and is outside this sampler, so this does not establish the later trigonometric offset path. Seed0 remains0, produces x=y=−1 and rejects radius²2 forever in the source; the harness reports budget exhaustion after32 attempts, never a successful return. This does not prove zero is a legal runtime seed. Tests reconstruct all candidate draws, f32 components, radius and final RNG state independently. Native trig, endpoint and contact remain pending.
|
| 6 |
+
|
| 7 |
+
No original executable/full QVM/native collision/physics/rendering executes. Native responses and legal input domains are supplied or unverified where stated; full runtime/macOS/Linux Rust/Bevy/Solari gameplay remains open.
|
docs/research/README.md
CHANGED
|
@@ -432,3 +432,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 432 |
|
| 433 |
|
| 434 |
[RE-610: Hitscan movement speed normalization and airborne floor](QVM_SPREAD_SPEED.md). Reproduce with `python3 scripts/analyze_qvm_spread_speed.py`; check with `python3 scripts/test_qvm_spread_speed.py`.
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
|
| 433 |
|
| 434 |
[RE-610: Hitscan movement speed normalization and airborne floor](QVM_SPREAD_SPEED.md). Reproduce with `python3 scripts/analyze_qvm_spread_speed.py`; check with `python3 scripts/test_qvm_spread_speed.py`.
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
[RE-611: Hitscan rejection sampling and RNG consumption](QVM_SPREAD_SAMPLING.md). Reproduce with `python3 scripts/analyze_qvm_spread_sampling.py`; check with `python3 scripts/test_qvm_spread_sampling.py`.
|
docs/research/qvm-spread-sampling.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_qvm_spread_sampling.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Bounded rejection sampling using actual source RNG and explicit attempt limit."""
|
| 3 |
+
import itertools
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
from qvm_research_support import source,execute
|
| 6 |
+
from qvm_server_fire_support import seed
|
| 7 |
+
from analyze_qvm_spread_speed import speed
|
| 8 |
+
from analyze_qvm_spread_parameters import LOCAL
|
| 9 |
+
from analyze_qvm_server_shot_setup import random_word
|
| 10 |
+
from analyze_qvm_acceleration import PS
|
| 11 |
+
from analyze_qvm import write_json
|
| 12 |
+
|
| 13 |
+
def sample(ins,m,attempt_limit=32):
|
| 14 |
+
if not 1<=attempt_limit<=64:raise ValueError('Attempt limit outside1..64')
|
| 15 |
+
m,p=speed(ins,m);before=m[21044]&0xffffffff;attempts=[]
|
| 16 |
+
def rng(i,m,a,c):m,r=random_word(i,m);return m,c,r['result'],r
|
| 17 |
+
for _ in range(attempt_limit):
|
| 18 |
+
m,_,r=execute(ins,290995,291081,m,(0,0,0),LOCAL,{36717:rng},boundaries=[291079]);accepted=r['stop']==291081
|
| 19 |
+
attempts.append({'draws':[child['effect']['result'] for child in r['children']],'seed_after':m[21044]&0xffffffff,'x':m[LOCAL+28]&0xffffffff,'y':m[LOCAL+32]&0xffffffff,'radius_squared':m[LOCAL+128]&0xffffffff,'accepted':accepted,'execution':{'calls':r['calls'],'trace_length':r['trace_length'],'trace_sha256':r['trace_sha256']}})
|
| 20 |
+
if accepted:break
|
| 21 |
+
return m,{'speed':p,'initial_seed':before,'final_seed':m[21044]&0xffffffff,'attempt_limit':attempt_limit,'threshold_input':m[LOCAL+40]&0xffffffff,'attempts':attempts,'status':'accepted' if accepted else 'attempt-budget-exhausted'}
|
| 22 |
+
|
| 23 |
+
def main():
|
| 24 |
+
q,raw,h,ins=source();rows=[]
|
| 25 |
+
for rng,counter in itertools.product([0,1,0x12345678,0xffffffff],[0,10,100000]):
|
| 26 |
+
m=seed(raw,h,22);m[PS+216]=counter;m[21044]=rng;rows.append(sample(ins,m)[1])
|
| 27 |
+
write_json(Path('docs/research/qvm-spread-sampling.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Actual290995–291080 with actual36717 per draw after actual parameter/speed prefixes. At most32 candidate attempts; budget exhaustion is not a QVM return. No global RNG seeding legality/native trig/endpoint/trace/damage inferred.'})
|
| 28 |
+
if __name__=='__main__':main()
|
scripts/test_qvm_spread_sampling.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json,unittest
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from test_qvm_ground import word,n,f
|
| 4 |
+
from test_qvm_server_shot_setup import random_oracle
|
| 5 |
+
class SpreadSamplingTests(unittest.TestCase):
|
| 6 |
+
@classmethod
|
| 7 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-spread-sampling.json').read_text())
|
| 8 |
+
def test_independent_rng_and_rejection_oracle(self):
|
| 9 |
+
for p in self.r['probes']:
|
| 10 |
+
seed=p['initial_seed']
|
| 11 |
+
for a in p['attempts']:
|
| 12 |
+
draws=[]
|
| 13 |
+
for _ in range(4):seed,v=random_oracle(seed);draws.append(v)
|
| 14 |
+
xy=[f(f(f(float(draws[j])*n(939524352))-.5)+f(f(float(draws[j+1])*n(939524352))-.5)) for j in [0,2]];radius=f(f(xy[0]*xy[0])+f(xy[1]*xy[1]));accepted=radius<1 and radius>f(n(p['threshold_input'])*n(1041865114));self.assertEqual(a['draws'],draws);self.assertEqual((a['x'],a['y'],a['radius_squared']),(*map(word,xy),word(radius)));self.assertEqual(a['accepted'],accepted);self.assertEqual(a['seed_after'],seed)
|
| 15 |
+
self.assertEqual(p['final_seed'],seed)
|
| 16 |
+
def test_only_final_candidate_can_be_accepted(self):
|
| 17 |
+
for p in self.r['probes']:
|
| 18 |
+
self.assertFalse(any(a['accepted'] for a in p['attempts'][:-1]));self.assertEqual(p['status']=='accepted',p['attempts'][-1]['accepted'])
|
| 19 |
+
def test_zero_seed_is_explicit_budget_boundary(self):
|
| 20 |
+
for p in self.r['probes']:
|
| 21 |
+
if p['initial_seed']==0:self.assertEqual(p['status'],'attempt-budget-exhausted');self.assertEqual(len(p['attempts']),32);self.assertEqual(p['final_seed'],0);self.assertTrue(all(n(a['radius_squared'])==2 for a in p['attempts']))
|
| 22 |
+
def test_actual_rng_calls_per_attempt(self):
|
| 23 |
+
for p in self.r['probes']:
|
| 24 |
+
for a in p['attempts']:self.assertEqual([c['target'] for c in a['execution']['calls']],[36717]*4)
|
| 25 |
+
if __name__=='__main__':unittest.main()
|