# 麻将消一消 · 关卡搭建工具包

> **版本日期：2026-08-17** —— 发压缩包给对方时，双方以此核对是否同一版；改动过工具后记得更新此行日期。

一套自包含的关卡搭建工具：**编辑器搭形状 → 网页一键生成 + 填色 → 交付成品 JSON**。
整个工具包只需安装 **Node.js**，无任何第三方依赖，复制到任意电脑即可使用。

**2026-08-15 补充：** 编辑器现在自带浏览器内生成（`generator.js`，内嵌模板 + 填色算法），
不用起 Node 服务器就能在网页里画完形状→一键出可玩关卡。原来的 Node 脚本
`generate_fill_std.js` 仍保留，用于一次批量出 24+ 关。

---

## 一、目录结构

```
关卡搭建工具包/
├── 使用说明.md                     ← 本说明
├── serve_editor.js                 ← 本地服务器（纯 Node，同时服务两个网页）
├── 启动编辑器.bat                  ← 双击打开「关卡编辑器」（搭形状）
├── 批量生成与填色.bat              ← 双击打开「批量工具」网页（一键生成+填色）
├── 批量工具.html                   ← 批量工具页面（自动打开，一般不用手动点）
├── shape_editor_std.html           ← 关卡编辑器页面（std 坐标，内置浏览器生成）
├── generator.js                    ← 浏览器版生成器（编辑器依赖，勿删）
├── shape_generator_v17_std.js      ← 形状生成核心模块（Node + 浏览器共用逻辑）
│
├── generate_fill_std.js            ← 一键生成+填色脚本（模板名当参数，换模板不用改脚本）
├── 难度分说明.md                    ← 难度分计算原理 + 设计理由（独立文档）
│
├── 模板/
│   └── T2_gong.json                ← 示例模板（只有 L0 底子）
```

> **一步到位：** 整套管线只有 1 个脚本 `generate_fill_std.js`——生成布局 → 倒推填花色 → 直接写成品，
> 中间不产生 `batch_<TAG>` 之类的临时目录。暗牌 / 普通两种填色由参数控制。

---

## 一点五、浏览器内一键生成（不开 Node 也能出关卡）★

如果**只想出 1 关、不想启 Node 服务器**，编辑器自带浏览器内生成：

1. 双击 `shape_editor_std.html` 直接打开（需要同目录下有 `generator.js`）
2. 画好形状
3. 侧栏找到 **「生成关卡」** 区域
   - **「生成完整关卡（当前形状）」**：把你画好的形状直接填色出可玩关卡，浏览器自动下载 JSON
   - **「从模板随机生成」**：从内嵌模板自动生成一个随机形状 + 填色，下载 JSON
   - 「含暗牌」开关控制是否按 10–20% 比例补暗牌（默认开）
4. 下载的 JSON 拖进 demo（`outputs/游戏Demo/`）就能玩

底层逻辑和 Node 版一致（`generator.js` 是 `shape_generator + generate_fill` 去 Node 依赖后的浏览器版，模板 `T2_gong.json` 内嵌），出关质量相同。

批量出 24+ 关还是用 Node 版（`generate_fill_std.js`），那个快得多。

---

---

## 二、环境要求

- **Node.js**（版本 ≥ 16 即可，脚本只用标准库；建议装官网最新 LTS，如 20/22）
- 不需要 `npm install`、不需要任何包

---

## 三、完整流程（两个网页）

整套流程只有两步，全程在浏览器里点选，不用碰命令行、不用改 JS。

### 第 1 步 · 搭形状（关卡编辑器）

Windows 双击 **`启动编辑器.bat`**，浏览器自动打开 `http://localhost:8997` 的编辑器页面。

- 编辑器是 **std 坐标**（row=上下/纵，col=左右/横），支持普通牌、暗牌（背面）、喷牌三类
- 常用操作：左键拖拽绘制、**右键拖拽擦除**、`Ctrl+Z/Y` 撤销重做、`Ctrl+滚轮` 缩放、
  空格/中键拖拽平移、`1/2/3` 换画笔、`Q/E` 切层、`S` 对称、`A` 全部层（点顶栏「？」看完整列表）
- 编辑内容会**自动保存在浏览器里**，刷新或误关页面后重开会自动恢复；换电脑需重新导入 JSON
- 搭好形状后点顶栏 **「导出」**，把文件放进 `模板/`，命名 `模板/<TAG>_<形状名>.json`
- 侧栏「关卡工作台（共享）」用于把关卡存到共享仓库（需部署版 + 共享口令）。**本地启动时连不上属正常**，连接按钮会提示失败，不影响其他功能
  - 示例：`模板/T2_gong.json` —— TAG 是 `T2`（系列名，下划线前那段）
- 两种模板类型都可以：
  - **只有 L0 底子**（如 T2_gong）：生成时会自动向上堆成 **6 层**塔
  - **自带多层**：生成时按模板自己的层数来，不堆高

### 第 2 步 · 一键生成 + 填色（批量工具）

Windows 双击 **`批量生成与填色.bat`**，浏览器自动打开批量工具网页（`http://localhost:8997/批量工具.html`），然后：

1. **① 选择模板** —— 点一个模板卡片（系列名自动识别）2. **② 选择填色方式** —— 暗牌 / 普通（区别见下节）
3. **③ 设置数量与覆盖** —— 「每批数量」填要生成的关数（1~200，默认 24）；
   「重跑前清空旧输出」默认勾选，重跑时先清掉上次同系列同填色方式的成品，避免旧文件堆积
4. **④ 点「▶ 开始生成并填色」** —— 自动跑完「生成 N 关布局 → 填花色」，日志实时滚动
5. **⑤ 成品统计表** —— 每关的难度分 / 可点数 / 钩子（暗钩子）/ 暗牌数 / 层数，以及难度最小-最大-平均

成品 JSON 自动输出到：
- 暗牌版 → `<TAG>_暗/`（如 `T2_暗/`）
- 普通版 → `<TAG>_填色/`（如 `T2_填色/`）

一步到位，不会产生中间目录——点完「开始生成」就只多出一个成品文件夹。

> **重复点击 / 换数量：** 每次点开始都会重新随机布局，内容不会重复；但文件名固定 `level_001~XXX`，
> 同名会被覆盖。所以默认「重跑前清空旧输出」会先清空再生成，保证结果始终是干净的整数份。
> 若取消勾选，旧文件中「难度分没变」的会被同名覆盖，变了名的会残留（可能出现同编号两份）。

> **命令行进阶（可选）：** 批量工具网页之外，bat 也保留了一条无界面命令：
> ```
> 批量生成与填色.bat auto <模板名> [dark|plain] [数量]
> ```
> 例：`批量生成与填色.bat auto T2_gong dark 50`（50 关，暗牌填色）。适合脚本化调用。
> 也可以直接跑生成脚本，自己控制数量与填色方式：
> ```
> node generate_fill_std.js <模板名> [数量] [dark|plain]
> ```

---

## 四、暗牌 vs 普通（两种填色方式的区别）

| | 普通填色 | 暗牌填色（推荐） |
|---|---|---|
| 牌面 | 全部翻开，直接可见花色 | 约 10%–20% 的牌初始为**背面**，点开才知道花色 |
| 暗牌藏哪儿 | — | 优先埋在「钩子对」底下（这种叫**暗钩子**），增加寻找成本 |
| 难度分 | 基础公式 | 基础公式 + 暗钩子占比加成 |
| 可解性 | 100% 可解 | 100% 可解（暗牌只隐藏牌面，不影响消除序列） |

**怎么选：** 普通版适合早期测试；正式关卡推荐暗牌版，背面牌让对局更有记忆和策略感。

---

## 五、难度分是怎么算的

成品文件名里的数字就是**难度分**（`level_001_T2_114.json` 里的 114），生成时自动评估，**越大越难**，程序侧按文件名排序就是难度梯度。

```
难度分 = (1 − 可点率) × 40 + 最高层序号 × 8 + min(钩子数 / 5, 1) × 30 + min(暗钩子数 / 5, 1) × 20 + 10
```

| 术语 | 含义 |
|---|---|
| 可点率 | 开局可直接点击的牌数 ÷ 总牌数 |
| 最高层序号 | = **显示层数 − 1**（6 层塔 = 序号 5） |
| 钩子 | 某花色恰好 2 张、且两张**层差 ≥ 2** 的对 |
| 暗钩子 | 钩子对里「深埋那张」恰好是暗牌（仅暗牌版） |

> 钩子每 1 个 +6 分、5 个拿满 30；暗钩子每 1 个 +4 分、5 个拿满 20（v2 按数量计分）；末尾 `+10` 是保底分。
> **每项为什么这么设计、权重怎么定的、想调怎么调** → 见同目录独立文档《难度分说明.md》。

## 六、命名规范（务必遵守）

| 内容 | 命名 | 示例 |
|---|---|---|
| 关卡成品 | `level_编号_TAG_难度分.json`（编号三位补零） | `level_001_T2_114.json` |
| 模板 | `模板/<TAG>_<形状名>.json` | `模板/T2_gong.json` |
| 成品目录 | `<TAG>_暗/`（暗牌版）/ `<TAG>_填色/`（普通版） | `T2_暗/` |

> 难度分写进文件名，程序侧可直接按文件名排序出难度梯度。

---

## 七、常见坑（两处坐标相关，务必注意）

1. **模板坐标**：编辑器导出的是 std 坐标（row=纵/col=横），生成器内部运算约定 row=横/col=纵。
   模板喂给生成器前脚本会做一次 `row/col` 对调，生成完再换回 std 导出。**手工改模板 JSON 时不要动这条约定**，
   新增 batch 脚本时记得保留这一行：
   ```js
   template.layers.forEach(ld => ld.cells.forEach(c => { const tmp = c.row; c.row = c.col; c.col = tmp; }));
   ```
2. **只有 L0 的模板**：如果模板只有底层（如 `T2_gong.json`），生成器会自动向上堆 **6 层**；
   想要别的塔高，在编辑器里把形状搭成多层再导出即可（自带多层的模板按自身层数生成）
3. **牌数必须为偶数**：二消规则要求总牌数为偶数，填色脚本对奇数会报错。
4. **端口占用**：编辑器与批量工具共用一个端口（8997）。同时开两个 bat 时，第二个会提示
   「端口已在运行」并自动退出——刷新浏览器里已有的页面即可，不用管。
5. **右侧试玩预览**：编辑器右侧的预览加载的是 `游戏Demo`。通过 bat 启动且 `游戏Demo` 与工具包并列
   （仓库默认布局）时自动可用；单独把工具包拷走、旁边没有 `游戏Demo` 时，预览区会提示「未找到本地 Demo」，
   生成和下载不受影响，把 `游戏Demo` 文件夹拷到工具包旁边即可恢复。

---

## 八、数据结构（交付给程序侧）

最终成品 JSON：

```json
{
  "levelId": 1,
  "totalPairs": 43,
  "tiles": [
    { "id": 1, "layer": 0, "row": -8, "col": -5, "typeId": 14, "isDark": false }
  ],
  "specialTiles": [
    { "id": 83, "type": "dark", "layer": 3, "row": -4, "col": 0 }
  ]
}
```

- **tiles**：每张牌的位置（std 坐标）、层级、花色 `typeId`（1-34：1-9万、10-18条、19-27筒、28东29西30南31北32中33发34白）
- **isDark**：`true` 表示初始为背面（运行时直接读该字段即可，无需反查 specialTiles）
- **specialTiles**：特殊牌清单（`type:'dark'` 为暗牌），与 tiles 里的 isDark 一一对应

**保证：** 倒推法生成的关卡 100% 可解；暗牌只隐藏牌面，不改变可点判定与消除序列，任意暗牌数量下仍可解。

---

## 九、交付清单（给程序侧）

交付关卡时，把成品 JSON（如 `T2_暗/level_XXX_T2_难度分.json`）连同本说明第八节的数据结构一起交给程序即可。
