From 6c68927c7466dfa988a52933a29d487222cf3211 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 29 Sep 2026 10:05:58 +0800 Subject: p71: 中文 API/模块说明(docs/api-reference) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 docs/api-reference/:README + 6 模块(Sim/地图生成/桌面渲染/交互交易/职业/存读档) 每节含 职责·对外 API 概览·单位与量纲·不变量·失败分支·算法与性能取舍 - 口径以当前源码为准;性能引用实测 p70 基准(不含未测宣称) - P71ApiDocsTests(3 例,红先→绿):文件齐备/章节齐备/关键接口与单位锚点 - Desktop 341/341、Kernel 120/120;未改生产 .fs/.fsproj、未 push --- docs/api-reference/map-and-mapgen.md | 64 ++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/api-reference/map-and-mapgen.md (limited to 'docs/api-reference/map-and-mapgen.md') diff --git a/docs/api-reference/map-and-mapgen.md b/docs/api-reference/map-and-mapgen.md new file mode 100644 index 0000000..5943967 --- /dev/null +++ b/docs/api-reference/map-and-mapgen.md @@ -0,0 +1,64 @@ +# 地图与生成(MapGen / ProceduralMap / MapSnapshot) + +源码:`src/LivingVillage.Desktop/MapGen.fs`、`src/LivingVillage.Desktop/ProceduralMap.fs`、 +`src/LivingVillage.Desktop/MapSnapshot.fs`。 + +## 职责 + +确定性生成可玩地图(河道 / 桥 / 石板路 / 民居 / 农田 / 主路),并把生成结果离线渲染为整图 PNG。 +`MapGen` 是默认可玩世界(256×192)与大图(512×384)的生成器;`ProceduralMap` 是遗留 +`LV_MAP_SCALE` 路径;`MapSnapshot` 只做纯 CPU 像素渲染,不参与游戏帧循环。 + +## 对外 API 概览 + +- `MapGen.defaultParams seed`:默认 64×48、`RiverCount = 2`、`RiverWidth = 3`、`CoreSide = 14`、 + `SpawnColumns = 5`、`SpawnRows = 6`。 +- `MapGen.paramsForSize width height seed`:尺寸自适应;`large = width > 256 || height > 192`。 + 大图追加 `ExtraRiversideHouses = 30`、`RiverDecorations = true`、`ClusterSeed = seed ^^^ 0xC1A57E2UL`, + 并关闭 `DecorativeStones`;非大图这些字段保持默认(不消费额外 RNG)。 +- `MapGen.generate params` / `MapGen.generateWithSize width height seed` → `MapGen.Result`。 +- `MapGen.serialize map`:确定性文本(同 seed 逐字节一致),用于回归/跨机核对。 +- `MapGen.tileAt map x y`、`MapGen.isWalkable map x y`、`MapGen.floodFill map x y`、 + `MapGen.visibleTileRange`、`MapGen.visibleTileCount`。 +- `ProceduralMap.generate`:遗留大世界地形(512×384 路径)。 +- `MapSnapshot.renderRgb map scale` / `MapSnapshot.renderPng map scale` / `MapSnapshot.save path map scale`; + `defaultScale = 8`。 + +## 单位与量纲 + +- 地图尺寸以**瓦片**计(`Result.Width` / `Result.Height`);内存瓦片码 `Tiles: int array`(行优先)。 +- `GroundTile`:`Grass=0 / Water=1 / Stone=2 / Peat=3 / PaddyField=4 / VegetablePlot=5`。 +- `River = { CenterY; Width }`(每列一条);`Building = { Left; Top; Width; Height; DoorX; DoorY }`(瓦片)。 +- `MainRoadCenter: int array`(长度 = 宽,逐列主路中心 y);`Paths: (int*int) list`、`Bridges`。 +- `MapSnapshot`:输出像素 = `Width*scale × Height*scale`;8-bit RGB PNG(无 alpha)。 + +## 不变量 + +1. **种子确定性**:同 `(width, height, seed)` 两次 `generateWithSize` 的 `Tiles`、 + `Rivers/Bridges/Paths/Buildings/Farmhouses/Groves/MainRoadCenter` 与 `serialize` 逐字节一致。 +2. **既有尺寸逐字节不变**:64×48 / 256×192 不进入大图分支、不消费额外 RNG(默认 stones 开启、 + decorations 关闭、ClusterSeed = 0)。 +3. **河道连续**:每条河每列恰好一段,宽度 ≥ `RiverWidth`;桥面仍标 `Water` 但 `isWalkable` 为真。 +4. **可达性**:`Result.ReachableTiles / ReachabilityOk / BridgeCrossingsOk` 由 `floodFill` 判定; + 民居/农舍门贴可达地面。 +5. **大图农田**:`PaddyField`/`VegetablePlot` 只在陆地上、避开路径与核心矩形。 +6. **渲染纯函数**:`MapSnapshot` 不依赖图形设备与时钟;同输入 PNG 逐字节一致。 + +## 失败分支 + +- `paramsForSize` 对任意正整数尺寸不抛异常(`large` 判定决定分支)。 +- `MapSnapshot.renderRgb/renderPng` 对越界瓦片写入静默跳过(`fillTile` 有 `tx/ty` 范围检查); + `scale` 经 `max 1` 保护;`save` 的 IO 异常由调用方处理。 +- `MapGen.isWalkable` 对越界坐标返回 `false`。 + +## 算法与性能取舍 + +- 生成原语:`splitmix64`(无外部依赖、无时钟)+ `valueNoise` 多八度 + 泊松布点; + 大图再加聚落组团(`ClusterSeed`)与主路缓弯(相邻列步长 ≤1)。 +- 渲染:`MapSnapshot` 用扁平色块 + 简化几何(屋顶矩形、门、主路/小径)而非纹理, + 目的是**离线、可复跑、无 GPU 依赖**;PNG 用 BCL `ZLibStream` 压缩。 +- 实测(`docs/evidence/p68-analysis.txt`,512×384 / seed=4242 / scale=8 → 4096×3072): + 河 388,608px、水田 742,080px、菜畦 824,128px、主路 32,768px、小径 34,112px、屋顶 19,328px 均 > 0; + 无 24×24 纯黑块 / 64×64 纯白块;`missing_glyph_slots = 0`;确定性 sha256 = `DD23CC2D…0FBF`。 +- 复跑:`LV_P68_MAPSHOT=1 LV_RECORD_DIR= dotnet src/LivingVillage.Desktop/bin/Release/net8.0/LivingVillage.Desktop.dll`, + 再 `python3 scripts/analyze-p68.py docs/evidence/p68-analysis.txt`。 -- cgit v1.2.3