mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-09-27 22:45:39 -04:00
8057709407
Two fixes to the pool derivation: - the previously-attempted glob was `s*_wave.json`, which misses `w6_wave.json` — so wave 6's own failed targets would have been silently re-offered to wave 7; - but widening it to `*_wave.json` introduced the opposite defect: a STAGED manifest counted as attempted, so deriving a 16-target wave and then a 64-target wave made the second exclude the first's 16 best entries (pool 2,928 -> 2,913 -> 2,893 across three derivations, each poisoned by the last). Now a manifest counts only if its drafts directory exists: `<tag>_wave.json` ran iff `.run/<tag>/*/*.c` is non-empty. A manifest with no drafts is a PLAN, not a result. Wave 7 staged in two sizes off one clean pool (2,913 fresh families / 432,020 open templ ins): .run/w7_wave.json 16 targets / 19,088 templ ins .run/w7_wave64.json 64 targets / 52,483 templ ins <- the ~50k shape NOTE the 50k now costs 64 targets, not 16: wave 6 harvested the head of the distribution (its top single target was 15,152 ins), so the frontier is now a long tail of ~1,000-1,500-ins families.
132 lines
5.5 KiB
Python
132 lines
5.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Derive the wave pool from the family map the corpus.stubs way (§138 rule 4).
|
|
|
|
WHY THIS EXISTS (and why it is not a transcription):
|
|
The family map's `exemplar` field is an IN-FAMILY pointer and can name an ALREADY-BANKED
|
|
instance — ranking off it hides whole families (measured: 16,696 ins -> 41,023 on the same map).
|
|
So every number here is DERIVED: for each family we ask corpus.stubs which members are still
|
|
open, and rank by (open members) x nins. The map supplies structure; the corpus supplies truth.
|
|
|
|
Also §138 rule 2: two targets in ONE TU means the first to bank puts its type tags in the
|
|
second's way (wave 4's only failure). So we allow at most one target per (overlay, TU).
|
|
|
|
Usage: .venv/bin/python .run/w6_pool.py <N> <out.json> [--exclude-attempted]
|
|
"""
|
|
import sys, os, json, glob, collections
|
|
sys.path.insert(0, 'tools')
|
|
import corpus
|
|
|
|
N = int(sys.argv[1]) if len(sys.argv) > 1 else 16
|
|
OUT = sys.argv[2] if len(sys.argv) > 2 else '.run/w6_wave.json'
|
|
|
|
# --- the standing exclusion ledger (CURRENT_PHASE.md ▶ RESUME, item 1) -----------------------
|
|
WALLS = {0x801412a8, 0x80178004, 0x80144b9c} # the 3 permanent walls
|
|
LEDGERED = {0x8017c294, 0x8017f7b4, 0x801898e4, 0x80186e24, # ledgered residuals
|
|
0x801758fc, 0x80132018, 0x8018a808} # + probed-not-templatable
|
|
EXCLUDE = WALLS | LEDGERED
|
|
|
|
# --- previously-attempted targets that are STILL open == they failed their gate ---------------
|
|
attempted = set()
|
|
# NB the glob must be `*_wave.json`, not `s*_wave.json`: wave 6's own manifest is `w6_wave.json`,
|
|
# so an s-only glob would silently re-offer every target wave 6 already failed.
|
|
#
|
|
# BUT "attempted" must mean A WAVE THAT RAN, not A MANIFEST THAT EXISTS. Staging a manifest and then
|
|
# re-deriving a bigger one made the second run exclude the first's targets — the 64-target pool came
|
|
# back MISSING its 16 best entries (pool 2,928 -> 2,913 -> 2,893 across three derivations, each one
|
|
# poisoned by the last). So require the wave's drafts directory to exist: `<tag>_wave.json` ran iff
|
|
# `.run/<tag>/` holds drafts. A manifest with no drafts dir is a PLAN, not a result.
|
|
for p in sorted(glob.glob('.run/*_wave.json')) + ['.run/s10.json']:
|
|
if not os.path.exists(p):
|
|
continue
|
|
tag = os.path.basename(p).replace('_wave.json', '').replace('.json', '')
|
|
if not glob.glob(f'.run/{tag}/*/*.c'):
|
|
print(f" (skip {p}: staged but never run — no drafts under .run/{tag}/)", file=sys.stderr)
|
|
continue
|
|
try:
|
|
for t in json.load(open(p)):
|
|
fn = t.get('fn') or t.get('func') or ''
|
|
if fn.startswith('func_'):
|
|
attempted.add(int(fn[5:], 16))
|
|
except Exception as e:
|
|
print(f" (skip {p}: {e})", file=sys.stderr)
|
|
|
|
fam = json.load(open('.run/family_hseq.json'))['families']
|
|
|
|
# --- stubs per overlay, fetched once ----------------------------------------------------------
|
|
stub_cache = {}
|
|
def stubs(ov):
|
|
if ov not in stub_cache:
|
|
try:
|
|
stub_cache[ov] = corpus.stubs(ov)
|
|
except Exception:
|
|
stub_cache[ov] = {}
|
|
return stub_cache[ov]
|
|
|
|
rows = []
|
|
for f in fam:
|
|
nins = f['nins']
|
|
if nins < 40: # below this the head is not worth an agent
|
|
continue
|
|
open_sites = []
|
|
for ov, addr_s in f['members']:
|
|
addr = int(addr_s, 16)
|
|
if addr in EXCLUDE:
|
|
open_sites = [] # a walled/ledgered address disqualifies the family
|
|
break
|
|
st = stubs(ov)
|
|
if addr in st:
|
|
open_sites.append((ov, addr, st[addr]))
|
|
if not open_sites:
|
|
continue
|
|
if any(a in attempted for _, a, _ in open_sites):
|
|
continue # tried before and still open => it failed; not fresh fuel
|
|
rows.append({
|
|
'h_seq' : f['h_seq'][:12],
|
|
'nins' : nins,
|
|
'open' : len(open_sites),
|
|
'members' : f['n_members'],
|
|
'ti' : len(open_sites) * nins, # OPEN templatable instructions — the ranking key
|
|
'diff' : f['diff_class'],
|
|
'addr_tag' : f['addr_tag'],
|
|
'mid_jr' : f['has_mid_jr'],
|
|
'sites' : open_sites,
|
|
})
|
|
|
|
rows.sort(key=lambda r: -r['ti'])
|
|
|
|
# --- pick N, at most one per (overlay, TU) ----------------------------------------------------
|
|
picked, used_tu = [], set()
|
|
for r in rows:
|
|
if len(picked) >= N:
|
|
break
|
|
for ov, addr, st in r['sites']:
|
|
key = (ov, st.path)
|
|
if key in used_tu:
|
|
continue
|
|
used_tu.add(key)
|
|
picked.append({
|
|
'fn' : f'func_{addr:08X}',
|
|
'ov' : ov,
|
|
'sub' : st.asm_dir,
|
|
'tu' : st.path,
|
|
'n' : r['nins'],
|
|
'm' : r['open'],
|
|
'ti' : r['ti'],
|
|
'diff' : r['diff'],
|
|
'tag' : r['addr_tag'],
|
|
'model': 'sonnet',
|
|
'seed' : len(picked) % 2,
|
|
})
|
|
break
|
|
|
|
json.dump(picked, open(OUT, 'w'), indent=1)
|
|
|
|
tot_pool = sum(r['ti'] for r in rows)
|
|
print(f"POOL : {len(rows)} fresh families / {tot_pool:,} OPEN templatable ins "
|
|
f"(excluded {len(EXCLUDE)} ledgered addrs, {len(attempted)} previously-attempted)")
|
|
print(f"PICKED : {len(picked)} targets / {sum(p['ti'] for p in picked):,} templatable ins -> {OUT}\n")
|
|
print(f"{'fn':<16}{'overlay':<15}{'nins':>5}{'open':>6}{'templ ins':>11} {'class':<7}{'tag':<14}TU")
|
|
for p in picked:
|
|
print(f"{p['fn']:<16}{p['ov']:<15}{p['n']:>5}{p['m']:>6}{p['ti']:>11,} "
|
|
f"{p['diff']:<7}{p['tag']:<14}{os.path.basename(p['tu'])}")
|