Files
last-hearth/tests/diversity_spec.lua
2026-11-05 09:57:11 -05:00

280 lines
15 KiB
Lua
Executable File

-- Focused Lua 5.1+ policy/registration tests; siblings also work flattened.
local target=assert(arg[1],"diversity.lua path required")
local passed=0
local api,hearth,D,season,world,fields,claims,protected,missing,lbms,writes
local p={x=0,y=1,z=0}
local function key(q) return q.x..":"..q.y..":"..q.z end
local function meta(q)
local k=key(q); fields[k]=fields[k] or {}; local f=fields[k]
return {get_string=function(_,n) return f[n] or "" end,
set_string=function(_,n,v) writes=writes+1; f[n]=tostring(v) end,
to_table=function() return {fields=f} end}
end
local function reset()
season,world,fields,claims,protected,missing,lbms,writes="Summer",{},{},{},{},{},{},0
api={registered_nodes={}}
function api.get_meta(q) return meta(q) end
function api.get_node_or_nil(q)
if missing[key(q)] then return nil end
return world[key(q)] or {name=q.y==0 and "hearth:grass" or "air"}
end
function api.swap_node(q,n) world[key(q)]=n end
function api.register_node(n,def) assert(not api.registered_nodes[n],n); api.registered_nodes[n]=def end
function api.register_lbm(def) lbms[def.name]=def end
hearth={climate_at=function() return {season=season} end,
claim_owner_at=function(q) return claims[key(q)] and "owner",claims.ambiguous end,
claim_allowed=function() error("nature must not call claim_allowed") end,
external_protected=function(q) return protected[key(q)] end}
hearth.seasons=dofile(target:gsub("[^/\\]+$","seasons.lua"))(hearth,api)
D=dofile(target)(hearth,api); hearth.diversity=D
end
local function test(label,fn)
reset(); local ok,err=pcall(fn); assert(ok,label..": "..tostring(err))
passed=passed+1; print("ok "..passed.." - "..label)
end
local function habitat(ground,light,moisture)
return {ground=ground,light=light or 15,moisture=moisture or 0.5,canopy=0,loaded=true,nutrients=2,heat=20}
end
local function register()
D.register_nodes(function(n,def) api.register_node("hearth:"..n,def) end)
for _,s in ipairs(D.species_order) do api.register_node("hearth:"..s.."_leaves",{tiles={"hearth_"..s.."_leaves.png"},groups={leaves=1},drop="hearth:sticks"}) end
hearth.seasons.register()
end
test("exact thirteen species and eight legacy understory plants have cube trunks and sprite seedlings",function()
register()
assert(table.concat(D.species_order,",")=="oak,birch,pine,willow,alder,beech,aspen,rowan,hazel,spruce,crab_apple,wild_cherry,field_maple")
assert(#D.plants==8)
for _,s in ipairs(D.species_order) do for _,stage in ipairs({"seedling","young","mature"}) do
local k=s.."_"..stage; local def=assert(api.registered_nodes["hearth:"..k])
assert(not def.mesh and not def.selection_box and not def.collision_box)
if stage=="seedling" then
assert(def.drawtype=="plantlike" and def.tiles[1]=="hearth_botany_"..s..".png")
else
assert(def.drawtype=="normal" and def.paramtype2=="facedir" and def.walkable)
assert(def.tiles[1]=="hearth_"..s.."_log_top.png" and def.tiles[2]==def.tiles[1])
assert(def.tiles[3]=="hearth_"..s.."_log_side.png" and #def.tiles==3)
assert(def.groups.tree==1 and def.groups.choppy==2 and not def.groups.snappy)
assert(def.groups.not_in_creative_inventory==1 and not def.sunlight_propagates)
end
assert(def.groups.hearth_ecosystem==1 and not def.buildable_to)
local a,b=D.tree_info(k); assert(a==s and b==stage)
assert(D.family(k)==({seedling="tree_seedling",young="young_tree",mature="mature_tree"})[stage])
if stage~="seedling" then assert(def.drop=="hearth:"..s.."_log") end
end end
for _,k in ipairs(D.plants) do
local def=assert(api.registered_nodes["hearth:"..k]); assert(def.tiles[1]=="hearth_"..k..".png" and def.drop=="")
end
assert(not D.tree_info("hearth:pine_log") and D.family("hearth:pine_leaves")=="leaves")
assert(D.family("hearth:willow_log")=="log" and not D.is_worldgen_flora("hearth:oak_leaves"))
end)
test("seasonal foliage changes never replace bark with atlases or meshes; both conifers stay evergreen",function()
register()
for _,s in ipairs(D.species_order) do
for _,stage in ipairs({"seedling","young","mature","leaves"}) do
for _,time in ipairs({"Spring","Autumn","Winter"}) do
season=time; local k=s.."_"..stage
local name=hearth.seasons.name_for(k)
assert(hearth.seasons.kind(name)==k)
if s=="pine" or s=="spruce" then
assert(name=="hearth:"..k and not api.registered_nodes[name.."_winter"])
assert(hearth.seasons.available(k))
elseif stage=="young" or stage=="mature" then
assert(name=="hearth:"..k.."_"..time:lower())
assert(api.registered_nodes[name].tiles[1]=="hearth_"..s.."_log_top.png")
assert(api.registered_nodes[name].tiles[3]=="hearth_"..s.."_log_side.png" and not api.registered_nodes[name].mesh)
assert(hearth.seasons.available(k)==(time~="Winter"))
else
local def=assert(api.registered_nodes[name])
assert(def.tiles[1]==(stage=="leaves" and "hearth_"..s.."_leaves_"..time:lower()..".png" or "hearth_botany_"..s..".png"))
assert(not def.mesh)
end
end
end
end
end)
test("all ten species can recruit deterministically on their supported habitat",function()
local found={}
for x=0,30 do for z=0,10 do
for _,g in ipairs({"grass","forest_floor","podzol","alpine_turf","alluvium","peat"}) do
for _,m in ipairs({0.4,0.6,0.9}) do
local h=habitat(g,10,m); h.canopy=g=="forest_floor" and 1 or 0
local q={x=x,y=1,z=z}; local k=D.choose_tree(q,h)
if k then
local s=D.tree_info(k); found[s]=true
assert(D.suitable(k,h) and D.choose_tree(q,h)==k)
end
end
end
end end
for _,s in ipairs(D.species_order) do assert(found[s],s) end
end)
test("tree plans share mapgen geometry with a canonical rooted identity and no world writes",function()
assert(not D.tree_plan("oak","mature")) -- missing biome integration fails closed
hearth.biomes=dofile(target:gsub("[^/\\]+$","biomes.lua"))(hearth,api,function() error("unexpected registration") end,{})
local before=writes
for _,s in ipairs(D.species_order) do for _,stage in ipairs({"young","mature"}) do
local plan=assert(D.tree_plan(s,stage)); local schematic=hearth.biomes.tree_schematic(s,false,stage)
assert(plan.size.x==schematic.size.x and plan.size.y==schematic.size.y and #plan>5)
local roots,leaves,trunks=0,0,0
local cx,cz=math.floor(plan.size.x/2),math.floor(plan.size.z/2)
for _,n in ipairs(plan) do
assert(n.name~="air" and n.param2==0)
local cell=schematic.data[1+n.x+cx+plan.size.x*(n.y+plan.size.y*(n.z+cz))]
if n.x==0 and n.y==0 and n.z==0 then
roots=roots+1; assert(n.name=="hearth:"..s.."_"..stage)
else assert(n.name==cell.name and cell.prob==255) end
if n.name:match("_leaves$") then leaves=leaves+1 else trunks=trunks+1 end
end
assert(roots==1 and leaves>=5 and trunks>=3)
plan[1].name="foreign:node"; plan.size.x=999
assert(D.tree_plan(s,stage)[1].name~="foreign:node" and D.tree_plan(s,stage).size.x<10)
end end
local generic=D.tree_plan("generic","mature"); local root
for _,n in ipairs(generic) do if n.x==0 and n.y==0 and n.z==0 then root=n.name end end
assert(root=="hearth:mature_tree" and writes==before)
assert(not D.tree_plan("unknown","mature") and not D.tree_plan("oak","seedling"))
end)
test("tree recruitment uses soils, cold podzol, pioneer gaps, woodland and river niches",function()
assert(D.choose_tree(p,habitat("podzol",15,0.4))=="pine_seedling")
assert(D.choose_tree(p,habitat("alpine_turf",15,0.35))=="pine_seedling")
local warm=habitat("podzol",15,0.4); warm.heat=60
assert(D.choose_tree(p,warm)=="birch_seedling")
assert(D.choose_tree(p,habitat("grass",15,0.3))=="birch_seedling")
local forest=habitat("forest_floor",10,0.6); forest.canopy=1
assert(D.choose_tree(p,forest)=="oak_seedling")
assert(D.choose_tree(p,habitat("alluvium",12,0.9))=="willow_seedling")
assert(D.choose_tree(p,habitat("peat",12,0.9))=="alder_seedling")
assert(not D.choose_tree(p,habitat("stone",15,0.1)))
end)
test("optional biome species policy is authoritative without requiring the biome module",function()
hearth.biomes={supports_species=function(s,ground) return s=="alder" and ground=="grass" end}
assert(not D.supports_species("oak","grass"))
assert(D.supports_species("alder","grass"))
assert(not D.choose_tree(p,habitat("podzol",15,0.4)))
end)
test("all eight understory niches recruit deterministically on the first establishment cycle",function()
local cases={clover={habitat("grass",15,0.5),{x=3,y=1,z=0},"Summer"},
nettle={habitat("grass",15,0.6),{x=6,y=1,z=0},"Summer"},
heather={habitat("podzol",15,0.4),p,"Summer"},
bluebell={habitat("forest_floor",8,0.6),p,"Spring"},
sedge={habitat("alluvium",15,0.9),p,"Summer"},
bulrush={habitat("peat",15,0.9),{x=3,y=1,z=0},"Summer"},
moss={habitat("forest_floor",8,0.7),p,"Summer"},
lichen={habitat("granite",15,0.1),p,"Summer"}}
for k,c in pairs(cases) do
season=c[3]; local recruit=D.choose_recruit(c[2],c[1],season)
assert(recruit==k,k.." got "..tostring(recruit))
assert(D.choose_recruit(c[2],c[1],season)==recruit)
assert(not D.choose_recruit(c[2],c[1],"Winter"))
end
assert(not D.suitable("clover",habitat("peat",15,1)))
assert(not D.suitable("heather",habitat("forest_floor",15,0.4)))
assert(not D.suitable("nettle",{ground="grass",light=12,moisture=0.6,nutrients=0}))
end)
test("dormancy gates herbivore food and spring bluebells while pollination is exposed",function()
register(); season="Spring"
assert(D.edible("hearth:clover") and D.edible("hearth:sedge"))
assert(not D.edible("hearth:bluebell") and D.pollinator("bluebell",season))
for _,s in ipairs({"Summer","Autumn","Winter"}) do
season=s; assert(not hearth.seasons.available("bluebell"))
assert(api.registered_nodes[hearth.seasons.name_for("bluebell")].tiles[1]=="hearth_bluebell_dormant.png")
end
assert(not D.edible("hearth:clover_winter") and not D.edible("hearth:sedge_winter"))
assert(hearth.seasons.available("moss") and hearth.seasons.available("lichen"))
assert(not D.pollinator("bluebell","Summer") and D.pollinator("heather","Autumn"))
end)
test("load bootstrap and scheduler adoption mark only known unmodified species flora",function()
register(); world[key(p)]={name="hearth:alder_seedling"}
assert(D.bootstrap(p) and meta(p):get_string("succ_natural")=="1")
local before=writes; assert(not D.bootstrap(p) and writes==before)
for _,k in ipairs({"oak_log","oak_leaves","log","leaves","wall","berry_bush"}) do
fields={}; world[key(p)]={name="hearth:"..k}; assert(not D.bootstrap(p),k)
end
fields={}; world[key(p)]={name="hearth:clover"}
lbms["hearth:diversity_natural_flora"].action(p,world[key(p)])
assert(meta(p):get_string("succ_natural")=="1")
end)
test("player markers, claims, ambiguity, support protection and unload prevent adoption",function()
register()
for _,field in ipairs({"owner","planter","player_placed","placed_by","planted_at","third_party:placed"}) do
fields={}; world[key(p)]={name="hearth:oak_mature"}; meta(p):set_string(field,"1")
local before=writes; assert(not D.bootstrap(p) and writes==before,field)
end
for _,mode in ipairs({"claim","ambiguous","protected","support","unloaded"}) do
reset(); world[key(p)]={name="hearth:clover"}
if mode=="claim" then claims[key(p)]=true end
if mode=="ambiguous" then claims.ambiguous=true end
if mode=="protected" then protected[key(p)]=true end
if mode=="support" then protected["0:0:0"]=true end
if mode=="unloaded" then missing["0:0:0"]=true end
assert(not D.bootstrap(p) and writes==0,mode)
end
reset(); register(); world[key(p)]={name="hearth:clover"}
api.registered_nodes["hearth:clover"].after_place_node(p)
assert(not D.bootstrap(p) and meta(p):get_string("player_placed")=="1")
end)
test("deadwood load adoption is idempotent and does not broaden generic flora policy",function()
register(); world[key(p)]={name="hearth:deadwood",param2=2}
local loader=lbms["hearth:diversity_natural_flora"]
local count=0
for _,n in ipairs(loader.nodenames) do if n=="hearth:deadwood" then count=count+1 end end
assert(count==1 and loader.run_at_every_load)
loader.action(p,world[key(p)])
assert(meta(p):get_string("succ_natural")=="1" and writes==1)
loader.action(p,world[key(p)]); assert(writes==1)
assert(world[key(p)].name=="hearth:deadwood" and world[key(p)].param2==2)
assert(meta(p):get_string("succ_age")=="")
for _,k in ipairs({"litter","fungi","log","oak_log","leaves","oak_leaves","planks"}) do
fields={}; world[key(p)]={name="hearth:"..k}
local before=writes; assert(not D.bootstrap(p) and writes==before,k)
end
end)
test("deadwood adoption fails closed for source and support provenance, authority, identity and unload",function()
local below={x=0,y=0,z=0}
for _,q in ipairs({p,below}) do
for _,mode in ipairs({"owner","planter","placed_by","player_placed","hearth_placed","hearth_player_placed","planted_at","third_party:placed",
"claim","ambiguous","protected","unloaded","ignore"}) do
reset(); world[key(p)]={name="hearth:deadwood"}
if mode=="claim" then claims[key(q)]=true
elseif mode=="ambiguous" then claims.ambiguous=true
elseif mode=="protected" then protected[key(q)]=true
elseif mode=="unloaded" then missing[key(q)]=true
elseif mode=="ignore" then world[key(q)]={name="ignore"}
else meta(q):set_string(mode,"1") end
local before=writes
assert(not D.bootstrap(p) and writes==before,mode.." at "..key(q))
end
end
for _,support in ipairs({"air","hearth:stone","hearth:log","hearth:water_source"}) do
reset(); world[key(p)]={name="hearth:deadwood"}; world[key(below)]={name=support}
assert(not D.bootstrap(p) and writes==0,support)
end
reset(); world[key(p)]={name="air"}
assert(not D.bootstrap(p,{name="hearth:deadwood"}) and writes==0)
end)
test("ground policy is separate from crops and lichen never needs to replace its rock",function()
for _,k in ipairs({"grass","soil","forest_floor","podzol","alluvium","peat","alpine_turf"}) do assert(D.is_ground("hearth:"..k)) end
assert(not D.is_ground("hearth:granite") and D.is_rock("hearth:granite"))
assert(not D.supports_ground("clover","hearth:granite"))
assert(D.supports_ground("lichen","hearth:granite"))
assert(not D.is_ground("hearth:crop_4") and not D.is_ground("ignore"))
end)
test("optional missing bases do not panic seasonal registration or install a scheduler",function()
local steps=0; api.register_globalstep=function() steps=steps+1 end
hearth.seasons.register()
assert(steps==0)
D.register_nodes(function(n,def) api.register_node("hearth:"..n,def) end)
hearth.seasons.register()
assert(api.registered_nodes["hearth:oak_seedling_winter"] and api.registered_nodes["hearth:oak_mature"].drawtype=="normal" and steps==0)
end)
test("bootstrap rechecks loaded source identity and explicit recruitment season wins over clock",function()
world[key(p)]={name="air"}
assert(not D.bootstrap(p,{name="hearth:clover"}) and writes==0)
world[key(p)]={name="hearth:clover"}; missing[key(p)]=true
assert(not D.bootstrap(p,{name="hearth:clover"}) and writes==0)
for _,bad in ipairs({{}, {x=0/0,y=1,z=0},{x=math.huge,y=1,z=0}}) do assert(not D.bootstrap(bad)) end
season="Winter"
assert(D.choose_recruit(p,habitat("forest_floor",8,0.6),"Spring")=="bluebell")
end)
print("LAST_HEARTH_DIVERSITY_PASS "..passed)