#!/usr/bin/env python3 """Lane PAR: tabulate shim.airng.txt brackets across runs.""" import re, sys, glob, os # Nested entry points: RNG_Chance's own body calls RNG_NextFloat, and both are hooked, so a # Chance that drew is recorded TWICE -- once against the game's call site and once against # 0x008e6e09, the instruction after Chance's internal `call RNG_NextFloat`. The same holds for # IntRangeBell/GaussianRange (0x008e6d80.., 0x008e6e30..), which this workload never hits. # `left_delta` is measured on the object and is unaffected; `observed` needs this subtraction. # This is the double-count draw_sites.h warns about, seen for the first time. NESTED = [(0x008e6dd0, 0x008e6e30), (0x008e6d80, 0x008e6dd0), (0x008e6e30, 0x008e6ec0)] def nested(va): return any(lo <= va < hi for lo, hi in NESTED) SITE = { 0x008e6e09: "(inner) RNG_Chance's own NextFloat -- double-count of the Chance row", 0x00578d15: "cl_Chance->RNG_Chance (facade 0x00578cf0)", 0x006ad878: "cl_RandFloat->NextFloat @AIComposeShipBlueprint 0x006ad700", 0x00579915: "cl_RandRange->NextInt (facade 0x005798e0)", 0x0069086f: "cl_Chance(0.5f) @0x00690860", 0x006cce89: "RNG_NextInt @DesignNameGen 0x006ccdb0", } def parse(p): rows=[]; sites={}; gens=[]; census=[] for ln in open(p, errors="replace"): m=re.match(r"airng seq=(\d+) pid=(\d+) agent=0x(\w+) rng=0x(\w+) pin=(\S+) left=(-?\d+)->(-?\d+) idx=(-?\d+)->(-?\d+) left_delta=(\d+) observed=(\d+) calls=(\d+) residual=(-?\d+) foreign_words=(\d+) foreign_calls=(\d+)", ln) if m: g=m.groups() rows.append(dict(seq=int(g[0]),pid=int(g[1]),agent=g[2],rng=g[3],pin=g[4], left_in=int(g[5]),left_out=int(g[6]),idx_in=int(g[7]),idx_out=int(g[8]), delta=int(g[9]),obs=int(g[10]),calls=int(g[11]),resid=int(g[12]), foreign=int(g[13]))) lm=re.search(r"life_in=(\d+) life_out=(\d+)", ln) if lm: rows[-1]['life_in']=int(lm.group(1)); rows[-1]['life_out']=int(lm.group(2)) continue m=re.match(r"airngsite seq=(\d+) pid=(\d+) ret_rva=\S+ va=0x(\w+) entry=(\w+) calls=(\d+) words=(\d+)", ln) if m: sites.setdefault(int(m.group(1)),[]).append((int(m.group(3),16),m.group(4),int(m.group(5)),int(m.group(6)))) continue m=re.match(r"airngen seq=(\d+) rng=0x(\w+) life_words=(\d+) life_calls=(\d+)", ln) if m: gens.append((int(m.group(1)),m.group(2),int(m.group(3)),int(m.group(4)))) m=re.match(r"airngcensus seq=(\d+) rng=0x(\w+) ret_rva=\S+ va=0x(\w+) entry=(\w+) calls=(\d+) words=(\d+)", ln) if m: census.append((int(m.group(1)),m.group(2),int(m.group(3),16),m.group(4),int(m.group(5)),int(m.group(6)))) return rows,sites,gens,census for p in sys.argv[1:]: tag=os.path.basename(os.path.dirname(p)) rows,sites,gens,census=parse(p) print(f"===== {tag} ({p})") for r in rows: life = f" life={r.get('life_in','?')}->{r.get('life_out','?')}" if 'life_in' in r else "" kind = "HUMAN" if r['agent']=="00000000" else "AI " print(f" seq={r['seq']} pid={r['pid']:4d} {kind} words={r['delta']:3d} (obs={r['obs']} calls={r['calls']} resid={r['resid']} foreign={r['foreign']}) idx {r['idx_in']}->{r['idx_out']}{life} pin={r['pin']}") dbl = sum(w for va,_,_,w in sites.get(r['seq'],[]) if nested(va)) if dbl: print(f" [nested-entry double count: {dbl} word(s); true site sum = {r['obs']-dbl} = left_delta {r['delta']}]") for va,entry,calls,words in sites.get(r['seq'],[]): mark = " (nested)" if nested(va) else "" print(f" 0x{va:08x} {entry:10s} calls={calls} words={words}{mark} {SITE.get(va,'')}") if gens: last=max(g[0] for g in gens) print(" -- generator census at last bracket --") for seq,rng,w,c in gens: if seq==last: print(f" rng=0x{rng} lifetime_words={w} calls={c}")