#!/usr/bin/env python3 """Compare numeric material conditions and cache transitions with pinned vanilla.""" import argparse import json import os from pathlib import Path import platform import random import subprocess import zipfile try: from . import lab,test_rust_parity as base,test_worldgen_parity as worldgen,test_biome_temperature_parity as biome except ImportError: import lab import test_rust_parity as base import test_worldgen_parity as worldgen import test_biome_temperature_parity as biome def corpus(): rng=random.Random(0x484f52495a4f4e434f4e444954494f4e) edges=[-2**31,-2032,-64,-21,-20,-5,-4,-3,-1,0,1,3,4,5,20,21,63,2031,2**31-1] conditions=[["hole"],["steep"]] for multiplier in [-2**31,-20,-1,0,1,20,2**31-1]: for add in [False,True]: for kind in range(4):conditions.append(["y",[kind,[-64,0,63,2**31-1][kind]],multiplier,add]) conditions.append(["water",edges[(multiplier+3)%len(edges)],multiplier,add]) for secondary in [-2**31,-100,-1,0,1,2,12,2**31-1]: for add in [False,True]: for ceiling in [False,True]:conditions.append(["stone",edges[(secondary+7)%len(edges)],add,secondary,ceiling]) for name in ["bedrock_floor","minecraft:bedrock_floor",":bedrock_roof","horizon:test",""]: for low,high in [([0,-64],[0,64]),([1,0],[1,5]),([2,5],[2,0]),([3,-5],[3,5]),([0,0],[0,0]),([0,64],[0,-64]),([0,-2**31],[0,2**31-1])]: conditions.append(["gradient",name,low,high]) conditions += [["not",["hole"]],["not",["not",["stone",0,True,12,False]]]] secondary_bits=[0,0x80000000,1,0x80000001,0x3f000000,0xbf000000,0x3f800000,0xbf800000,0x40000000,0xc0000000,0x7f7fffff,0xff7fffff,0x7f800000,0xff800000,0x7fc00000]+[rng.getrandbits(32) for _ in range(81)] seeds=[0,1,-1,-2**63,2**63-1]+[rng.randrange(-2**63,2**63) for _ in range(19)] for fixture,seed in enumerate(seeds): ops=[["eval"],["eval"],["secondary"]] for i,bits in enumerate(secondary_bits): depth=edges[(i+fixture)%len(edges)] x,z=[rng.choice(edges) if i%3==0 else rng.randrange(-30000000,30000001) for _ in range(2)] above,below,water=[rng.choice(edges) for _ in range(3)] y=[-65,-64,-63,-1,0,1,62,63,64,65][i%10] if i%2 else rng.choice(edges) ops += [["input",depth,f"{bits:x}"],["xz",x,z,edges[i%len(edges)],edges[(i+9)%len(edges)]],["y",above,below,water,y],["eval"],["eval"]] # Changing source values alone does not invalidate cached results. ops += [["input",depth^1,f"{secondary_bits[(i+7)%len(secondary_bits)]:x}"],["secondary"],["y",below,above,water,y],["eval"],["secondary"]] if i%12==0: ops += [["add",["not",["gradient","horizon:late",[0,-64],[0,64]]]],["eval"]] for legacy in [False,True]: yield {"seed":seed,"legacy":legacy,"context":[[-64,384,63],[0,256,32],[-2032,4064,0],[2**31-1,-2**31,-2**31]][fixture%4],"conditions":conditions,"ops":ops} def condition_resources(jar): prefix="data/minecraft/worldgen/material_condition/" with zipfile.ZipFile(jar) as archive: return {"minecraft:"+name[len(prefix):-5]:archive.read(name).decode("utf-8") for name in sorted(archive.namelist()) if name.startswith(prefix) and name.endswith(".json")} def codec_corpus(definitions): templates=[ {"type":"hole"},{"type":"steep"},{"type":"temperature"}, {"type":"biome","biome_is":["minecraft:plains"]}, {"type":"noise_threshold","noise":"surface","min_threshold":-1.0,"max_threshold":1.0,"is_3d":False}, {"type":"y_above","anchor":{"absolute":0},"surface_depth_multiplier":0,"add_stone_depth":False}, {"type":"water","offset":0,"surface_depth_multiplier":0,"add_stone_depth":False}, {"type":"stone_depth","offset":0,"add_surface_depth":False,"secondary_depth_range":0,"surface_type":"floor"}, {"type":"vertical_gradient","random_name":"bedrock_floor","true_at_and_below":{"above_bottom":0},"false_at_and_above":{"above_bottom":5}}, {"type":"not","invert":{"type":"hole"}}, ] cases=['null','false','0','[]','{}','"minecraft:unknown"'] cases += list(definitions.values())+[json.dumps(name) for name in definitions] cases += [json.dumps({"type":"not","invert":name}) for name in definitions] tokens=['null','true','false','0','1','-1','-21','-20','20','21','2147483647','-2147483648','4294967296','4294967295','0.99','-0.99','1e100','1e1000','-1e1000','1e-100','""','"floor"','"ceiling"','"FLOOR"','"true"','"a:b:c"','[]','{}','{"absolute":0}','{"above_bottom":0,"below_top":0,"relative_to_sea_level":0}'] for template in templates: cases.append(json.dumps(template,separators=(",",":"))) for key in template: cases.append(json.dumps({k:v for k,v in template.items() if k!=key})) for token in tokens: fields=[json.dumps(k)+":"+(token if k==key else json.dumps(v,separators=(",",":"))) for k,v in template.items()] cases.append("{"+",".join(fields)+"}") for prefix in ["minecraft:",":","other:","Minecraft:"]: cases.append(json.dumps(dict(template,type=prefix+template["type"]))) cases.append(json.dumps(dict(template,unknown=[1,2,3]))) cases += ['{"type":"hole","type":"steep"}','{"type":"hole","type":null}'] base=next(corpus()) for context in [[-64,384,63],[0,256,32],[-2032,4064,0],[2**31-1,-2**31,-2**31]]: for legacy in [False,True]: yield {"seed":-1,"legacy":legacy,"context":context,"conditions":[],"json":cases,"ops":base["ops"][:166]} def rule_corpus(definitions): rng=random.Random(0x484f52495a4f4e52554c45) base=next(corpus()) choices=base["conditions"]+[["json",json.dumps(name)] for name in sorted(definitions)] def tree(depth): kind=rng.randrange(4 if depth else 2) if kind==0:return ["block",rng.randrange(10)] if kind==1:return ["band"] if kind==2:return ["if",rng.choice(choices),tree(depth-1)] return ["sequence",[tree(depth-1) for _ in range(rng.randrange(4))]] rules=[["block",i] for i in range(10)]+[["band"],["sequence",[]],["sequence",[["block",8]]],["sequence",[["sequence",[]]]]] rules += [["if",c,["block",7]] for c in choices] rules += [["sequence",[["block",8],["if",["stone",0,False,12,False],["block",7]],["band"]]], ["sequence",[["if",["hole"],["block",0]],["if",["not",["hole"]],["band"]]]], ["if",["stone",0,True,12,False],["sequence",[["if",["steep"],["block",1]],["block",9]]]]] rules += [tree(3) for _ in range(48)] edges=[-2**31,-1000,-193,-192,-65,-64,-1,0,1,63,64,191,192,319,2**31-1] for fixture in range(12): ops=[["apply",0,0,0],["apply",0,0,0]] for i in range(64): x,y,z=[rng.choice(edges) for _ in range(3)] a,b,w=[rng.choice(edges) for _ in range(3)] ops += [["input",[-1,0,1,4,12,2**31-1][i%6],["0","3f800000","bf800000","7f800000","7fc00000"][i%5]], ["xz",x,z,[-5,-4,-3,0,3,4,5][i%7],[5,4,3,0,-3,-4,-5][i%7]],["y",a,b,w,y], ["apply",x,y,z],["apply",x,y,z],["apply",z,edges[(i+7)%len(edges)],x]] if i%16==0:ops += [["rule",tree(3)]] for legacy in [False,True]: yield {"seed":[0,1,-1,-2**63,2**63-1,987654321][fixture%6],"legacy":legacy,"context":[-64,384,63],"conditions":[],"rules":rules,"ops":ops} def noise_corpus(noises): import struct bits=lambda value:struct.pack('>d',value).hex() bounds=[(-1.,1.),(0.,0.),(-0.,0.),(1.,-1.),(float('-inf'),float('inf')),(float('nan'),1.),(-1.,float('nan'))] conditions=[] for name in ['surface','minecraft:surface',':surface_secondary','minecraft:clay_bands_offset']: for is3d in [False,True]: for low,high in bounds:conditions.append(['noise',name,bits(low),bits(high),is3d]) conditions += [['not',c] for c in conditions[:14]] random=__import__('random').Random(0x484f52495a4f4e4e4f495345) noise_bits=[0,0x80000000,0x3f800000,0xbf800000,0x3f800001,0xbf800001,0x3f7fffff,0xbf7fffff,0x7f800000,0xff800000,0x7fc00000]+[random.getrandbits(32) for _ in range(85)] edges=[-2**31,-1000,-64,-1,0,1,63,319,2**31-1] for real in [False,True]: selected=conditions if not real else [['noise',name,bits(-0.25),bits(0.25),is3d] for name in sorted(noises) for is3d in [False,True]] for seed in [0,1,-1,-2**63,2**63-1,87654321]: operations=[['eval'],['eval']] for i,b in enumerate(noise_bits): x,y,z=[random.choice(edges) for _ in range(3)] operations += [['input',0,f'{b:x}'],['xz',x,z,0,0],['y',0,0,0,y],['eval'],['eval'],['input',0,f'{noise_bits[(i+7)%len(noise_bits)]:x}'],['eval'],['y',0,0,0,y],['eval'],['xz',x,z,0,0],['eval']] if i%16==0:operations += [['add',selected[i%len(selected)]],['eval']] for legacy in [False,True]: yield {'seed':seed,'legacy':legacy,'context':[-64,384,63],'conditions':selected,'real_noise':real,'ops':operations} def biome_corpus(biomes): names=sorted(biomes) conditions=[['temperature'],['not',['temperature']],['biome',[]],['biome',names]] conditions += [['biome',[name]] for name in names] conditions += [['not',['biome',[name]]] for name in names[::7]] conditions += [['biome',['minecraft:plains','minecraft:plains','minecraft:desert']]] choices=[None,[],['minecraft:plains'],['minecraft:desert'],['minecraft:plains','minecraft:desert'],names] ops=[['eval'],['eval']] for i,name in enumerate(names): ops += [['biome',name],['xz',i*31,i*-17,(i%9)-4,4-(i%9)],['eval'],['y',0,0,0,[-64,0,63,80,81,120,319][i%7]],['eval'],['eval']] if i%9==0:ops += [['add',['biome',[name]]],['eval']] # Errors after a previous result preserve that result on a same-revision # retry. Null biome results are queried again by uncached conditions. ops += [['biome','#throw'],['y',0,0,0,0],['eval'],['eval'],['add',['biome',['minecraft:plains']]],['eval'],['eval'], ['biome','minecraft:plains'],['eval'],['y',0,0,0,0],['eval'],['biome',None],['y',0,0,0,81],['eval'],['eval'], ['biome','minecraft:desert'],['xz',7,9,0,0],['eval'],['y',0,0,0,81],['eval']] for possible in choices: for sea in [63,-1000,2**31-1]: yield {'seed':0,'legacy':False,'context':[-64,384,sea],'possible_biomes':possible,'conditions':conditions,'ops':ops} def biome_tag_resources(jar): prefix='data/minecraft/tags/worldgen/biome/' with zipfile.ZipFile(jar) as archive: return {'minecraft:'+name[len(prefix):-5]:archive.read(name).decode('utf-8') for name in sorted(archive.namelist()) if name.startswith(prefix) and name.endswith('.json')} def biome_tag_corpus(biomes,tags): conditions=[['json',json.dumps({'type':'biome','biome_is':'#'+name})] for name in sorted(tags)] conditions += [['not',c] for c in conditions[:8]] invalid=[{'type':'biome','biome_is':value} for value in ['#minecraft:unknown','#',['#minecraft:is_badlands'],'##minecraft:is_badlands']] aliases=[{'type':'biome','biome_is':'#'+name.removeprefix('minecraft:')} for name in sorted(tags)] rules=[['sequence',[['if',c,['block',7]],['block',8]]] for c in conditions] for possible in [None,[],['minecraft:plains'],['minecraft:badlands'],sorted(biomes)]: ops=[['eval'],['eval']] for i,name in enumerate(sorted(biomes)): ops += [['biome',name],['xz',i,i*-3,0,0],['y',0,0,0,i],['eval'],['apply',i,i,i*-3]] ops += [['biome','#throw'],['y',0,0,0,80],['eval'],['eval'],['biome',None],['y',0,0,0,81],['eval'],['eval'],['biome','minecraft:plains'],['eval']] yield {'seed':0,'legacy':False,'context':[-64,384,63],'possible_biomes':possible,'conditions':conditions,'json':[json.dumps(v) for v in [*invalid,*aliases]],'rules':rules,'ops':ops} def preliminary_corpus(): def wrap(v): return (v+2**31)%2**32-2**31 volumes=[[4,3,5,0,-64,0,1,1,1],[5,7,3,-17,32,19,4,8,4], [1,1,1,1024,0,-1024,1,1,1],[4,2,4,2**31-2,1,-2**31,3,2,5]] bit_values=['00000000','80000000','3f000000','bf000000','3f7fffff','bf800001','42810000', 'c2810000','4effffff','4f000000','cf000000','cf000001','7f800000','ff800000','7fc00000','00000001','80000001'] conditions=[['above'],['not',['above']],['not',['not',['above']]]] rules=[['if',['above'],['block',7]],['sequence',[['if',['not',['above']],['block',8]],['block',9]]]] for volume in volumes: sx,_,sz,x,_,z,_,_,_=volume for failure in [-1,0,1,3,19]: for first in ['minimum','eval','apply']: # Inside-plane allocation and failed retries, or initial scalar # lookup before the first allocation, vary independently. initial=[first] if first!='apply' else ['apply',0,200,0] ops=[['density','42810000',failure],['input',3,'0'],['xz',x,z,0,0],initial,['minimum'],['eval'],['apply',7,11,13]] ops += [['density','43400000',-1],['minimum'],['eval'],['y',0,0,0,80],['minimum'],['eval']] for iz in range(sz): for ix in range(sx): ops += [['xz',wrap(x+ix),wrap(z+iz),0,0],['minimum'],['y',0,0,0,(ix+iz)*31-8],['eval'],['apply',-17,90,31]] for i,bits in enumerate(bit_values): depth=[-2**31,-9,0,3,8,2**31-1][i%6] # Scalar fallback: floor/conversion, wrapping, cache reuse, # failure after a previous successful minimum, and recovery. ox,oz=wrap(x-1-i),wrap(z-1-i) ops += [['density',bits,-1],['input',depth,'0'],['xz',ox,oz,0,0],['minimum']] for y in [-2**31,-65,-8,-1,0,56,64,65,2**31-1]: ops += [['y',0,0,0,y],['eval'],['apply',0,0,0]] ops += [['density','0',0],['minimum'],['xz',ox,oz,0,0],['eval'],['minimum'],['eval'],['density',bits,-1],['minimum'],['xz',ox,oz,0,0],['minimum']] ops += [['add',['above']],['eval']] yield {'seed':failure,'legacy':first=='eval','context':[-64,384,63], 'volume':volume,'conditions':conditions,'rules':rules, 'json':['{"type":"above_preliminary_surface"}','{"type":"minecraft:above_preliminary_surface","ignored":null}'], 'ops':ops} # A scalar lookup outside the plane must not allocate or populate it. yield {'seed':0,'legacy':False,'context':[-64,384,63],'volume':[2,1,2,10,0,10,1,1,1], 'conditions':conditions,'rules':rules,'ops':[['density','42810000',-1],['minimum'],['density','43400000',-1],['xz',10,10,0,0],['minimum'],['xz',0,0,0,0],['minimum']]} def ore_corpus(): def wrap(v): return (v+2**31)%2**32-2**31 rng=random.Random(0x484f52495a4f4e4f5245) bits=['0','80000000','00000001','3e4ccccd','3f000000','3f333333','3f7fffff','3f800000', '3f800001','bf800000','7f800000','ff800000','7fc00000'] volumes=[[3,4,2,-1,-2,5,1,1,1],[3,2,3,-10,-5,-15,4,8,4],[2,2,2,2**31-1,-2**31,2**31-1,2,3,2]] chances=['0','3ca3d70a','3f000000','3f800000','bf800000','7f800000','7fc00000'] rules=[['ore',chance] for chance in chances] rules += [['sequence',[['block',0],['ore','3f800000']]],['if',['not',['hole']],['ore','0']]] for volume in volumes: sx,sy,sz,x,y,z,dx,dy,dz=volume positions=[[wrap(x+ix*dx),wrap(y+iy*dy),wrap(z+iz*dz)] for iz in range(sz) for ix in range(sx) for iy in range(sy)] positions += [[wrap(x-1),y,z],[x,wrap(y-1),z],[x,y,wrap(z-1)],[wrap(x+1),wrap(y+1),wrap(z+1)]] ops=[['apply',0,0,0]] for i in range(180): if i