summaryrefslogtreecommitdiff
path: root/docs/api-reference/map-and-mapgen.md
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-29 10:05:58 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-29 10:05:58 +0800
commit6c68927c7466dfa988a52933a29d487222cf3211 (patch)
tree3c1e3351d832c3f8ea97d53de0ccc8ece6e49e8e /docs/api-reference/map-and-mapgen.md
parent8159519a81b3c6fd2a066623db1b660347963806 (diff)
downloadliving-village-6c68927c7466dfa988a52933a29d487222cf3211.tar.gz
p71: 中文 API/模块说明(docs/api-reference)
- 新增 docs/api-reference/:README + 6 模块(Sim/地图生成/桌面渲染/交互交易/职业/存读档) 每节含 职责·对外 API 概览·单位与量纲·不变量·失败分支·算法与性能取舍 - 口径以当前源码为准;性能引用实测 p70 基准(不含未测宣称) - P71ApiDocsTests(3 例,红先→绿):文件齐备/章节齐备/关键接口与单位锚点 - Desktop 341/341、Kernel 120/120;未改生产 .fs/.fsproj、未 push
Diffstat (limited to 'docs/api-reference/map-and-mapgen.md')
-rw-r--r--docs/api-reference/map-and-mapgen.md64
1 files changed, 64 insertions, 0 deletions
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=<dir> dotnet src/LivingVillage.Desktop/bin/Release/net8.0/LivingVillage.Desktop.dll`,
+ 再 `python3 scripts/analyze-p68.py <dir> docs/evidence/p68-analysis.txt`。