package worldgen import ( "bytes" "path/filepath" "testing" "mc/internal/block" ) // loadReg 加载共享注册表(blocks.json)。 func loadReg(t *testing.T) *block.Registry { t.Helper() r, err := block.Load(filepath.Join("..", "..", "assets", "config", "blocks.json")) if err != nil { t.Fatalf("加载方块注册表失败: %v", err) } return r } // TestDeterminism 验证生成确定性:同 seed 两次生成逐字节一致(世界生成.md §2.2)。 func TestDeterminism(t *testing.T) { g, err := New(loadReg(t), 12345, 0.005, 0.01, 0.1) if err != nil { t.Fatalf("创建生成器失败: %v", err) } a := g.Generate(-1, 3) b := g.Generate(-1, 3) sa, sb := a.Snapshot(), b.Snapshot() if !bytes.Equal(stateBytes(sa), stateBytes(sb)) { t.Fatal("同 seed 生成结果不一致") } } // stateBytes 把方块状态切片序列化为字节(比较用)。 func stateBytes(s []block.State) []byte { out := make([]byte, len(s)*4) for i, v := range s { out[i*4] = byte(v >> 24) out[i*4+1] = byte(v >> 16) out[i*4+2] = byte(v >> 8) out[i*4+3] = byte(v) } return out } // TestTerrainStructure 验证地形结构:基岩层完整、地表为群系方块、海平面下灌水。 func TestTerrainStructure(t *testing.T) { reg := loadReg(t) bedrock, _ := reg.ID("bedrock") water, _ := reg.ID("water") air := uint16(0) // caveDensity=1.0 关闭洞穴,便于验证灌水逻辑 g, err := New(reg, 777, 0.005, 0.01, 1.0) if err != nil { t.Fatalf("创建生成器失败: %v", err) } ch := g.Generate(0, 0) // 基岩层:y=0 全部为基岩(世界生成.md §6:基岩层保留) for lx := 0; lx < 16; lx++ { for lz := 0; lz < 16; lz++ { if id := ch.Block(lx, 0, lz).ID(); id != bedrock { t.Fatalf("(%d,0,%d) 非基岩: ID=%d", lx, lz, id) } } } // 每个地形柱:海平面以下要么是水要么是地形方块,不允许空气 for lx := 0; lx < 16; lx++ { for lz := 0; lz < 16; lz++ { h := g.heightAt(int32(lx), int32(lz)) for y := 1; y < seaLevel; y++ { id := ch.Block(lx, y, lz).ID() if y > h { if id != water { t.Fatalf("(%d,%d,%d) 海平面下应为水,实际 ID=%d", lx, y, lz, id) } } else if id == air { t.Fatalf("(%d,%d,%d) 地形柱内出现空气", lx, y, lz) } } } } } // TestOres 验证矿物只出现在规定深度区间(挖矿与矿物.md §2:y=100 只允许石头/煤矿)。 func TestOres(t *testing.T) { reg := loadReg(t) g, err := New(reg, 4242, 0.005, 0.01, 0.1) if err != nil { t.Fatalf("创建生成器失败: %v", err) } stone, _ := reg.ID("stone") coal, _ := reg.ID("coal_ore") for i := 0; i < 2000; i++ { s := g.oreAt(int32(i), 100, int32(i)) // y=100:只允许石头或煤矿 id := s.ID() if id != stone && id != coal { t.Fatalf("y=100 出现异常方块: ID=%d", id) } } }