Everything the next agent needs to run the Phase 2 optimisation loop on a machine that can launch the client: - analysis/client-perf/: LEVERS (ranked levers + corrections), BENCH-MATRIX (world, scenes, config IDs, blocks B0-B14 with decision rules), PLAYER-TUNING, SPAWN-TUNING, research briefs, the jar scanner and its output, observed default configs, and proposed-configs/ (build.py generates tiers and bench variants and fails on unknown keys; apply_variant.py applies a variant and merges options.txt). - analysis/client-perf/bench/onion-bench/: bench-world datapack (scene teleports, Create grid, mob pen, sound storm, 120 s teleport tour). - analysis/audit/: the static recipe/dependency audit, including gen_fixes.py, which generates the shipped Paxi datapack and was never tracked. - phase0/: per-machine measurement harness (Steam Deck, 7700K/1060, 6700K/1080 Ti). - .pi/journal.md: running work log; read it first. All of it is packwizignored, so nothing here ships to players.
145 lines
6.3 KiB
Python
145 lines
6.3 KiB
Python
import pickle,json,collections,sys
|
|
sys.setrecursionlimit(10000)
|
|
exec(open("recipes.py").read().split("# ---------- active recipes")[0].split("# ---------- resource-level")[0]) # imports only
|
|
A=pickle.load(open("active.pkl","rb")); active=A["active"]; tagcache=A["tagcache"]
|
|
exec(open("recipes.py").read().split("# ---------- ingredients ----------")[1].split("# ---------- active recipes")[0].replace("resolve(","R("))
|
|
def full(x): return x if ":" in x else "minecraft:"+x
|
|
def R(t): return tagcache.get(t,set())
|
|
def ns(x): return x.split(":")[0]
|
|
EMPTY=frozenset(["<empty>"])
|
|
def shaped(j):
|
|
pat=j.get("pattern"); key=j.get("key",{})
|
|
if not pat: return None
|
|
g=[]
|
|
for row in pat:
|
|
r=[]
|
|
for ch in row:
|
|
if ch==" ": r.append(EMPTY)
|
|
else:
|
|
s=ing(key.get(ch))
|
|
if s is None: return None
|
|
r.append(s)
|
|
g.append(tuple(r))
|
|
return tuple(g)
|
|
def mirror(g): return tuple(tuple(reversed(r)) for r in g)
|
|
def inter(a,b): return bool(a&b)
|
|
def grid_match(a,b):
|
|
return len(a)==len(b) and len(a[0])==len(b[0]) and all(inter(x,y) for ra,rb in zip(a,b) for x,y in zip(ra,rb))
|
|
def bip(a,b):
|
|
if len(a)!=len(b): return False
|
|
match={}
|
|
def tr(i,seen):
|
|
for k in range(len(b)):
|
|
if k in seen or not inter(a[i],b[k]): continue
|
|
seen.add(k)
|
|
if k not in match or tr(match[k],seen): match[k]=i; return True
|
|
return False
|
|
return all(tr(i,set()) for i in range(len(a)))
|
|
def out(j):
|
|
try: return result(j)
|
|
except Exception: return str(j.get("result"))[:80]
|
|
groups=collections.defaultdict(list)
|
|
for rid,(jar,j) in active.items(): groups[j.get("type","?")].append((rid,jar,j))
|
|
print({k:len(v) for k,v in sorted(groups.items(),key=lambda x:-len(x[1]))[:40]})
|
|
res=collections.defaultdict(list)
|
|
def report(kind,a,b,same):
|
|
res[(kind,same)].append((a,b))
|
|
# --- crafting: shaped vs shaped, shapeless vs shapeless, shaped vs shapeless ---
|
|
SH=[]; SL=[]
|
|
for rid,jar,j in groups["minecraft:crafting_shaped"]:
|
|
g=shaped(j)
|
|
if g: SH.append((rid,g,out(j)))
|
|
for rid,jar,j in groups["minecraft:crafting_shapeless"]:
|
|
l=[ing(x) for x in j.get("ingredients",[])]
|
|
if None in l or not l: continue
|
|
SL.append((rid,l,out(j)))
|
|
idx=collections.defaultdict(set)
|
|
for i,(rid,g,o) in enumerate(SH):
|
|
for it in g[0][0]: idx[(len(g),len(g[0]),it)].add(i)
|
|
pairs=set()
|
|
for i,(rid,g,o) in enumerate(SH):
|
|
for gg in (g,mirror(g)):
|
|
for it in gg[0][0]:
|
|
for k in idx[(len(g),len(g[0]),it)]:
|
|
if k>i and grid_match(gg,SH[k][1]): pairs.add((i,k))
|
|
for i,k in pairs:
|
|
a,b=SH[i],SH[k]; report("shaped",(a[0],a[2]),(b[0],b[2]),a[2]==b[2])
|
|
idx=collections.defaultdict(set)
|
|
for i,(rid,l,o) in enumerate(SL):
|
|
for it in set().union(*l): idx[(len(l),it)].add(i)
|
|
pairs=set()
|
|
for i,(rid,l,o) in enumerate(SL):
|
|
small=min(l,key=len)
|
|
for it in small:
|
|
for k in idx[(len(l),it)]:
|
|
if k>i and bip(l,SL[k][1]): pairs.add((i,k))
|
|
for i,k in pairs:
|
|
a,b=SL[i],SL[k]; report("shapeless",(a[0],a[2]),(b[0],b[2]),a[2]==b[2])
|
|
# shaped vs shapeless (shapeless matches any arrangement with same nonempty cells)
|
|
for rid,g,o in SH:
|
|
cells=[c for r in g for c in r if c is not EMPTY]
|
|
for it in cells[0]:
|
|
for k in idx[(len(cells),it)]:
|
|
if bip(cells,SL[k][1]): report("shaped~shapeless",(rid,o),(SL[k][0],SL[k][2]),o==SL[k][2])
|
|
# --- single-input processing types ---
|
|
def single(j):
|
|
x=j.get("ingredient")
|
|
if x is None and isinstance(j.get("ingredients"),list) and len(j["ingredients"])==1: x=j["ingredients"][0]
|
|
return ing(x) if x is not None else None
|
|
for t in ["minecraft:smelting","minecraft:blasting","minecraft:smoking","minecraft:campfire_cooking"]:
|
|
L=[(rid,single(j),out(j)) for rid,jar,j in groups[t]]
|
|
L=[x for x in L if x[1]]
|
|
idx=collections.defaultdict(list)
|
|
for i,(rid,s,o) in enumerate(L):
|
|
for it in s: idx[it].append(i)
|
|
seen=set()
|
|
for it,ix in idx.items():
|
|
for a in ix:
|
|
for b in ix:
|
|
if a<b and (a,b) not in seen:
|
|
seen.add((a,b)); report(t,(L[a][0],L[a][2]),(L[b][0],L[b][2]),L[a][2]==L[b][2])
|
|
# cutting board
|
|
def tool(j):
|
|
t=j.get("tool")
|
|
if isinstance(t,dict) and t.get("type","").endswith("ability"): return ("ability",t.get("action") or t.get("item_ability"))
|
|
s=ing(t); return ("items",s)
|
|
def tools_overlap(a,b):
|
|
if a[0]!=b[0]: return False
|
|
return a[1]==b[1] if a[0]=="ability" else inter(a[1] or frozenset(),b[1] or frozenset())
|
|
for t in [k for k in groups if k.endswith(":cutting")]:
|
|
L=[]
|
|
for rid,jar,j in groups[t]:
|
|
s=ing(j.get("ingredients"));
|
|
if s: L.append((rid,s,tool(j),out(j)))
|
|
idx=collections.defaultdict(list)
|
|
for i,x in enumerate(L):
|
|
for it in x[1]: idx[it].append(i)
|
|
seen=set()
|
|
for it,ix in idx.items():
|
|
for a in ix:
|
|
for b in ix:
|
|
if a<b and (a,b) not in seen and tools_overlap(L[a][2],L[b][2]):
|
|
seen.add((a,b)); report(t,(L[a][0],L[a][3],sorted(L[a][1])[:2] if len(L[a][1])>2 else sorted(L[a][1])),(L[b][0],L[b][3]),L[a][3]==L[b][3])
|
|
# multi-ingredient machine types (exact multiset w/ overlap): cooking pots, mixers etc
|
|
GENERIC=[k for k in groups if not k.startswith("minecraft:") and not k.endswith(":cutting")]
|
|
for t in GENERIC:
|
|
L=[]
|
|
for rid,jar,j in groups[t]:
|
|
x=j.get("ingredients") or ([j["ingredient"]] if "ingredient" in j else None)
|
|
if not isinstance(x,list): continue
|
|
l=[ing(y) for y in x]
|
|
if None in l or not l or any(len(s)==0 for s in l): continue
|
|
extra=json.dumps({k:j.get(k) for k in ("container","tool","heatRequirement","heat_requirement","processing_time","fluid","inputFluid") if k in j},sort_keys=True)
|
|
L.append((rid,l,out(j),extra))
|
|
idx=collections.defaultdict(set)
|
|
for i,(rid,l,o,e) in enumerate(L):
|
|
for it in set().union(*l): idx[(len(l),it)].add(i)
|
|
for i,(rid,l,o,e) in enumerate(L):
|
|
for it in min(l,key=len):
|
|
for k in idx[(len(l),it)]:
|
|
if k>i and L[k][3]==e and bip(l,L[k][1]):
|
|
report(t,(rid,o),(L[k][0],L[k][2]),o==L[k][2])
|
|
pickle.dump(dict(res),open("coll.pkl","wb"))
|
|
for (kind,same),l in sorted(res.items()):
|
|
print(f"## {kind} {'SAME-OUTPUT(dup)' if same else 'CONFLICT'}: {len(set(map(str,l)))}")
|