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([""]) 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 a2 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)))}")