From 09310370365220bc9b9c560d8947cd7abc36d14f Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 22 Sep 2026 08:35:11 +0800 Subject: feat(mapgen): 512x384 沿河民居加密到 18 座且门全部可达(P24) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Params.ExtraRiversideHouses(默认 0);paramsForSize 仅对 >256x192 设为 10, 从既有路网泊松散布 3x2 民居并接门入网(structureRng 确定性),512x384 共 18 座。 - 64x48/256x192 仍 4 座、checksum 6042052/1284656962 不变(新增用例锁定)。 - 新增 512 民居数量/可达/确定性用例;Desktop 130 -> 134。 --- src/LivingVillage.Desktop/MapGen.fs | 55 +++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 3 deletions(-) (limited to 'src/LivingVillage.Desktop/MapGen.fs') diff --git a/src/LivingVillage.Desktop/MapGen.fs b/src/LivingVillage.Desktop/MapGen.fs index ce9bccf..1e63ed2 100644 --- a/src/LivingVillage.Desktop/MapGen.fs +++ b/src/LivingVillage.Desktop/MapGen.fs @@ -30,7 +30,9 @@ module MapGen = RiverWidth: int CoreSide: int SpawnColumns: int - SpawnRows: int } + SpawnRows: int + /// P24:大图额外沿河民居数量(沿现有路网泊松散布)。默认 0,64x48/256x192 不受影响。 + ExtraRiversideHouses: int } /// P20 第二步:一条贯穿全图的东西向河道(每列恰好一段,宽度下限 RiverWidth)。 type River = @@ -70,16 +72,20 @@ module MapGen = RiverWidth = 3 CoreSide = 14 SpawnColumns = 5 - SpawnRows = 6 } + SpawnRows = 6 + ExtraRiversideHouses = 0 } /// P23 尺寸自适应参数:河道数量随高度增长(约每 96 行一条),其余沿用默认。 /// 64x48 / 256x192 仍得到 2 条河道,与既有输出逐字节一致;512x384 得到 4 条。 + /// P24:仅对大于 256x192 的图追加沿河民居(泊松散布),保证既有 checksum 不变。 let paramsForSize (width: int) (height: int) (seed: uint64) : Params = let base_ = defaultParams seed + let large = width > 256 || height > 192 { base_ with Width = width Height = height - RiverCount = max base_.RiverCount (max 1 (height / 96)) } + RiverCount = max base_.RiverCount (max 1 (height / 96)) + ExtraRiversideHouses = (if large then 10 else 0) } // ---- splitmix64 与 value noise(无外部依赖、无时钟) ---- @@ -275,6 +281,49 @@ module MapGen = buildings <- building :: buildings addPath building.DoorX building.DoorY + // 5b) P24:大图沿河民居加密。从既有路网瓦片出发,向四邻定向落位 3x2 白墙黛瓦民居, + // 门开向相邻路格并接入路网(BFS 可达);仅在 ExtraRiversideHouses>0 时执行, + // 且只在核心区外的草地落位,故 64x48/256x192 输出逐字节不变。 + if p.ExtraRiversideHouses > 0 then + let footprintFree (left: int) (top: int) = + left >= 1 && left + 2 <= w - 2 && top >= 1 && top + 1 <= h - 2 + && [ for dx in 0 .. 2 do + for dy in 0 .. 1 -> (left + dx, top + dy) ] + |> List.forall (fun (x, y) -> + tiles.[idx x y] = int GroundTile.Grass + && not (inCore x y)) + let overlaps (left: int) (top: int) = + buildings + |> List.exists (fun b -> + left < b.Left + b.Width && b.Left < left + 3 + && top < b.Top + b.Height && b.Top < top + 2) + let directions = [ (0, -1); (0, 1); (-1, 0); (1, 0) ] + let mutable extraPlaced = 0 + let mutable extraAttempts = 0 + let maxAttempts = p.ExtraRiversideHouses * 400 + while extraPlaced < p.ExtraRiversideHouses && extraAttempts < maxAttempts && paths.Length > 0 do + extraAttempts <- extraAttempts + 1 + let nextState, raw = splitmix structureRng + structureRng <- nextState + if paths.Length > 0 then + let pathX, pathY = List.item (int (raw % uint64 paths.Length)) paths + let dirX, dirY = directions.[int ((raw >>> 32) % 4UL)] + let doorX = pathX + dirX + let doorY = pathY + dirY + let left = doorX - 1 + let top = doorY - 1 + if footprintFree left top && not (overlaps left top) then + let building = + { Left = left + Top = top + Width = 3 + Height = 2 + DoorX = doorX + DoorY = doorY } + buildings <- building :: buildings + addPath building.DoorX building.DoorY + extraPlaced <- extraPlaced + 1 + // 6) 30 个 spawn:核心区内的规则网格。 let sc = max 2 p.SpawnColumns let sr = max 2 p.SpawnRows -- cgit v1.2.3