Files
ngzz-mc/internal/worldgen/gen_test.go

151 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
}
}
}
}
}
// TestVillage 村庄生成:存在村庄中心的区块应有木板/圆石建筑(村民系统.md §2)。
func TestVillage(t *testing.T) {
reg := loadReg(t)
planks, _ := reg.ID("oak_planks")
cobble, _ := reg.ID("cobblestone")
g, err := New(reg, 424242, 0.005, 0.01, 0.1)
if err != nil {
t.Fatalf("创建生成器失败: %v", err)
}
// 扫描网格点找村庄
found := 0
for gx := int32(-2); gx <= 2; gx++ {
for gz := int32(-2); gz <= 2; gz++ {
vx, vz := g.villageCenter(gx, gz)
b := g.biomeAt(vx, vz)
if b != biomePlains && b != biomeForest {
continue
}
cx, cz := floorDiv32(vx, 16), floorDiv32(vz, 16)
// 建筑跨区块(世界生成.md §4):统计中心 3×3 区块的木板/圆石总量
plankCnt, cobbleCnt := 0, 0
for dcx := int32(-1); dcx <= 1; dcx++ {
for dcz := int32(-1); dcz <= 1; dcz++ {
ch := g.Generate(cx+dcx, cz+dcz)
snap := ch.Snapshot()
for i := 0; i < 16*16*256; i++ {
switch snap[i].ID() {
case planks:
plankCnt++
case cobble:
cobbleCnt++
}
}
}
}
// 3 间房屋(每间约 70+ 木板)+ 水井(8 圆石)
if plankCnt < 150 || cobbleCnt < 8 {
t.Fatalf("村庄 (%d,%d) 建筑过少: 木板 %d 圆石 %d", cx, cz, plankCnt, cobbleCnt)
}
found++
}
}
if found == 0 {
t.Fatal("5×5 网格点内未发现村庄")
}
}
// 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)
}
}
}