RegionIO/internal/world/structure_placement_test.go
Daniar Mannanov c488ce274f worldgen: parse structure sets and place them on the vanilla grid
Loads the 26.1.2 structure datapack: 20 structure sets, 34 structure
definitions, and the worldgen biome tags they reference. Random-spread
placement follows RandomSpreadStructurePlacement byte for byte — region
floorDiv by spacing, one Legacy stream salted per region, two
spread-type draws for the offset — and the four legacy frequency
reducers keep their exact draws, including the hardcoded buried-treasure
salt and the outpost's discarded full-range int.

Verified against the fixture: on seed 12345 the ruined_portals set
claims exactly chunk (1,0), the chunk whose captured blocks carry the
ruined portal's obsidian, gold, and crying obsidian. TestStructure-
PlacementGrid pins that plus village separation invariants.
2026-08-26 10:33:10 +03:00

86 lines
2.4 KiB
Go

package world
import (
"testing"
"regionio/internal/worldgen"
)
// TestStructurePlacementGrid pins the random-spread placement machinery against
// the vanilla capture. The fixture's chunk (1,0) carries a ruined portal —
// obsidian, crying obsidian, and a gold block around y=13-18 — and the grid
// must claim exactly that chunk for the ruined_portals set on seed 12345.
func TestStructurePlacementGrid(t *testing.T) {
sets, err := worldgen.LoadStructureSets()
if err != nil {
t.Fatal(err)
}
const seed = 12345
if sets.Sets["minecraft:ruined_portals"] == nil {
t.Fatal("ruined_portals set missing")
}
set := sets.Sets["minecraft:ruined_portals"]
var claimed []string
for cx := int32(-8); cx <= 8; cx++ {
for cz := int32(-8); cz <= 8; cz++ {
if set.IsStartChunk(seed, cx, cz) {
claimed = append(claimed, "("+structCoord(cx)+","+structCoord(cz)+")")
}
}
}
if len(claimed) != 1 || claimed[0] != "(1,0)" {
t.Fatalf("ruined portal starts %v, want exactly [(1,0)]", claimed)
}
// Mineshaft corridors from the starts around the fixture reach its deep
// oak-plank cells; the starts themselves sit a few chunks out.
mineshafts := sets.Sets["minecraft:mineshafts"]
near := 0
for cx := int32(-6); cx <= 6; cx++ {
for cz := int32(-6); cz <= 6; cz++ {
if mineshafts.IsStartChunk(seed, cx, cz) {
near++
}
}
}
if near == 0 {
t.Fatal("no mineshaft starts anywhere near the fixture")
}
// Villages use spacing 34/separation 8: two starts can never share a
// region row or column window closer than separation allows.
villages := sets.Sets["minecraft:villages"]
vp := villages.Placement.RandomSpread
pick := func(cx, cz int32) [2]int32 { return vp.PotentialChunk(seed, cx, cz) }
a := pick(0, 0)
b := pick(int32(vp.Spacing), 0)
span := int32(vp.Spacing - vp.Separation)
localA := a[0]
localB := b[0] - int32(vp.Spacing)
if localA < 0 || localA >= span || localB < 0 || localB >= span {
t.Fatalf("village offsets %v -> %v fall outside [0,%d)", a, b, span)
}
if gap := b[0] - a[0]; gap < int32(vp.Separation)+1 {
t.Fatalf("adjacent village regions %d apart, want >= %d", gap, vp.Separation+1)
}
}
func structCoord(v int32) string {
digits := ""
neg := v < 0
if neg {
v = -v
}
for v > 0 {
digits = string(rune('0'+v%10)) + digits
v /= 10
}
if digits == "" {
digits = "0"
}
if neg {
return "-" + digits
}
return digits
}