# 玩法三 · 六边一笔 本文只描述**玩法三**。玩法一、玩法二有自己的目录和 GDD,见仓库 [`docs/PRODUCT.md`](../../../docs/PRODUCT.md)。 在六边形拼板上画一条哈密顿路径。格子之间的边上可以有墙,墙挡住那一条邻接。预先写好的数字,必须刚好在那一步走到。 本玩法是**独立玩法**:六边形 + 边墙,不用矩形大格。墙的价值是把“第一步往哪走”收成推理——看起来相邻的格子,有墙就走不过去。关卡、资源、文案全部原创。 --- ## 1. 一句话 在六边形盘上走遍每一格;黄黑条纹的墙贴在两条格子的共用边上,那条路走不通;线索数字必须踩在对应步号上。 ## 2. 产品定位 | 项 | 决定 | |---|---| | 类型 | 单机、离线、无体力、无计时的逻辑 puzzle | | 平台 | 手机竖屏。本玩法 V1 先在 `modes/03-hex-walls/web/` 可玩 | | 单局 | 20–120 秒想,5–12 秒画完 | | 会话 | 通勤 3 关,睡前 10 关 | | 对象 | 已经能接受一笔画的人;墙是给“矩形走腻了”的第二层 | | 付费 | 竖直切面免费无广告 | 和玩法一同属“顺序一笔画”,但几何不同: | | 玩法一 | 玩法三 | |---|---|---| | 格子 | 矩形,可 2×2 只计一步 | 正六边形,一格一步 | | 邻接 | 四向共边 | 六向共边 | | 障碍 | 空洞 | 空洞 + **边墙** | | 代码 | `modes/01-number-stroke` | `modes/03-hex-walls`,禁止互相 import | 不要把墙补进玩法一的后期关。墙是本玩法的主旋钮。 ## 3. 核心循环 1. 看盘:线索在哪,墙封了哪几条边,哪一格看起来近其实过不去。 2. 推:从线索往外挤,尤其是被墙封死的第一步。 3. 画:滑到相邻且无墙的未走格,自动填步号。 4. 错了沿原路退,或重开。 5. 全填满且线索全部对上 → 过关。 没有分数。爽感来自“墙把表面的近路掐掉,只剩一条”。 --- ## 4. 规则(权威) ### 4.1 盘面 - 格子是点顶(pointy-top)正六边形,用轴坐标 `(q, r)`。 - 每个格是图上一个节点,路径里计 1 步。没有大格。 - 单位格要么属于恰好一个六边形,要么是空洞。 - 盘面可不规则。 轴坐标邻接(未考虑墙): ``` (q+1, r), (q-1, r), (q, r+1), (q, r-1), (q+1, r-1), (q-1, r+1) ``` ### 4.2 墙 墙是**无向边**,贴在两个相邻格的共用边上,画成黄黑斜纹警示条。 - 有墙的两格视为**不相邻**。其它邻接不受影响。 - 墙不占格,不能走在墙上。 - 同一对格最多一堵墙。 - 墙必须画在真实共用边上。不能“对角墙”。 这是本玩法相对玩法一多出来的唯一新原语。 ### 4.3 路径 目标:在「去墙之后的格图」上找一条哈密顿路径。 - 每个格恰好一次。 - 只能走到相邻、无墙、尚未走过的格。 - 第 k 个格显示数字 k(从 1 起)。 - 不能分叉、不能跳、不能穿墙。 ### 4.4 线索格 部分格预先写了正整数 `c`,带描边。 - 走到该格时步号必须等于 `c`。 - 可以没有 `1`(起点未知)。教学前 8 关必须有 `1`。 - 线索互不相同,`1 ≤ c ≤ N`。 - 可以没有 `N`。 ### 4.5 对错 允许暂时画错: - 踩到线索但步号 ≠ `c`:该格冲突态(红底),显示当前步号。 - 冲突不打断手势。 - **过关**:所有格走过,且所有线索步号正确。 - 无失败弹窗。穿墙或对角滑:忽略。 ### 4.6 内容铁律 发布前求解器必须断言: 1. 去墙后的图连通。 2. 恰好 1 条哈密顿路径满足全部线索。 3. 线索尽量少。教学关可以多给。 若去掉全部墙后解变得不唯一,说明墙在干活,这关才算合格的“墙关”。第 3 关起,每关至少 1 堵墙,且去掉墙会破坏唯一性。 --- ## 5. 交互 单指。和玩法一同一套手感,只是命中的是六边形、且要检查墙。 | 手势 | 行为 | |---|---| | 点合法起点 | 该格成为第 1 步 | | 滑向相邻、无墙、未走格 | 追加 | | 滑向有墙的邻居 | 忽略(可在墙上闪一下) | | 滑回上一格 | 撤销 | | 滑到不相邻 / 已走且非上一格 | 忽略 | | 松手 | 保留路径,可从末端继续 | 合法起点:有线索 `1` 则只能从它起手;否则任意格。 按钮:重开、提示、菜单(本玩法选关 / 回壳)。顶栏:`{pack}-{index}`、剩余格、菜单。 输入状态机与玩法一相同:Empty → Drawing → Idle → Cleared。实现时不要 import 玩法一,允许各写一份。 --- ## 6. 反馈与手感 | 事件 | 反馈 | |---|---| | 追加一步 | 六边形 80ms 弹,共用边拉一条短线 | | 撞墙 | 墙闪白 100ms,短振动,不追加 | | 线索步号正确 | 描边闪白 | | 冲突 | 红底 | | 过关 | 路径色提高,短线顺次扫过 | 未走:中灰。已走:蓝紫。墙:黄黑斜纹,对比足够,不靠颜色当唯一信息(色弱可把墙改成粗黑 + 缺口)。背景白。 --- ## 7. 关卡语言 一次只加一个旋钮。 | 旋钮 | 作用 | 何时引入 | |---|---|---| | 格数 N | 6–8 教学,9–14 常规,15–20 后期 | 全程 | | 墙的条数 | 1 堵教规则,2–4 堵做推理 | 第 3 关 | | 墙贴在线索旁 | 第一步往往只剩一个方向 | 第 4 关 | | 空洞 | 和墙叠加,近路更假 | 第 6 关 | | 中段线索 | 不只靠起点 | 第 5 关 | | 无线索 1 | 起点未知 | 第 13 关之后 | 不做:矩形大格、斜穿、传送、限时。大格是玩法一的旋钮,不要混进来。 ### 7.1 教学 8 关(必须手做) 1-1 到 1-4 的坐标在 `levels/pack-1-tutorial.json`。 | 关 | 教什么 | |---|---| | 1-1 | 六边形、滑动、自动出数字。无墙。3 格 | | 1-2 | 必须走完。5 格,标 1 和 5 | | 1-3 | 第一堵墙:看起来相邻,滑不过去,必须绕 | | 1-4 | 墙贴在 1 旁边,第一步不是“随便挑邻居” | | 1-5 | 中段线索 | | 1-6 | 两堵墙 + 一个空洞 | | 1-7 | 先想再画,错了要退 | | 1-8 | 综合:约 12 格、3 堵墙、3 个线索 | 1-3 是关键:过不了“墙是边不是格”,后面全废。 ### 7.2 结构 一包 8 关。V1:教学 8 + 3 包 × 8 = **32 关**。线性解锁。 ### 7.3 规则样例 规则样例直接用 `levels/pack-1-tutorial.json`:1-3 演示“墙是边不是格”(两格共边却被墙封住,看起来相邻,滑不过去,必须绕),1-4 演示“墙贴在线索旁,第一步只剩一个方向”。全部关卡由求解器断言唯一解且墙参与唯一性(§4.6)。 --- ## 8. 生成器与求解器 教学关手做。第 9 关起可生成,人要过一眼。 求解器: - 输入:六边形列表 + 墙边列表 + 线索。 - 邻接 = 轴坐标六邻接减去墙。 - 回溯哈密顿路径,线索处剪枝。 - `N ≤ 20`。发布条件 `paths.length === 1`。 - 额外检查:删掉所有墙之后 `paths.length !== 1`(墙必须改变解集合)。 生成: 1. 铺一块连通的六边形聚落,可挖洞。 2. 随机一条哈密顿路径当作者解。 3. 在**不是作者解所用的边**上插 1~3 堵墙。 4. 加 2~3 个线索直到唯一。 5. 跑“去墙不唯一”检查。失败则换墙。 关卡文件: ```json { "id": "1-3", "pack": 1, "index": 3, "orientation": "pointy", "cells": [ { "id": "a", "q": 0, "r": 0, "clue": 1 }, { "id": "b", "q": 1, "r": 0 }, { "id": "c", "q": 0, "r": 1 } ], "walls": [ ["a", "b"] ] } ``` `walls` 是格 id 对。求解器不写进 JSON。 --- ## 9. 界面与流程 ``` (壳:选玩法)→ 本玩法选关 → 对局 → 过关 → 下一关 ↘ 菜单:关卡列表 / 回壳 ``` 对局层:顶栏关号与剩余格,中间六边形盘(按短边缩放),底栏重开 / 提示。过关短动画后自动下一关。 1-1 一行:“从一个数字滑到旁边的格子”。1-3 一行:“条纹是墙,绕开它”。之后无文案。 --- ## 10. 提示、存档 提示: - 无路径:指出合法起点。 - 路径是唯一解前缀:指出下一步。 - 已偏离:高亮分叉点,不继续给错下一步。 - 竖直切面无限次。 存档键前缀 `mode:03-hex-walls`。`clearedId[]`、`currentId`。不存半途路径。 --- ## 11. 设计边界 V1 只做主规则(§4)。明确不做: - 矩形大格(玩法一的旋钮) - 矩形 + 墙、斜穿、传送、限时 - 计分 几何锁定为六边形,墙是边不是格。关卡、资源、文案全部原创,不使用第三方题库或素材。 --- ## 12. 技术 全部落在 `modes/03-hex-walls/`。 禁止: - import 玩法一的 Cell / 四向邻接 / 矩形碰撞 - 把墙做成“第三种格子” | 项 | 决定 | |---|---| | 竖直切面 | `web/` 单页 | | 坐标 | 轴坐标 pointy-top | | 求解器 | `tools/solver.ts`,只服务本玩法 | | 关卡 | `levels/*.json` | 模块:`model`(Hex、Wall、Path)、`rules`、`input`、`solver`、`view`。 --- ## 13. 竖直切面 1. 1-1、1-2 无墙可过。 2. 1-3 直滑进墙无效,绕开后能过。 3. 1-4 有线索 1 时点别的格起手无效;墙让第一步只剩一个方向。 4. 冲突、撤销、重开与玩法一同样稳。 5. 求解器对 VS 关断言唯一解,且去墙后不唯一(1-3 起)。 6. 提示在偏离时拉回分叉。 --- ## 14. 里程碑 | 阶段 | 交付 | |---|---| | M0 | 本 GDD 锁定 | | M1 | 规则 + 求解器 + 1-1~1-4 可玩 | | M2 | 1-1~1-8 + 存档 | | M3 | 再 24 关 + 选关 | | M4 | 迁 Cocos 本玩法场景 | --- ## 15. 关键决定 1. **独立玩法,不是玩法一的 DLC。** 六边形模型和边墙都住在本目录。 2. **墙是边,不是格。** 穿墙 = 那条邻接不存在。 3. **解唯一,且墙必须参与唯一性。** 4. **允许画错,随时撤回。** 5. **无大格。** 大格留给玩法一。 6. **先 Web,再 Cocos。** --- ## 16. 验收 - 没玩过的人在 1-3 结束前理解墙。 - 1-4 开始出现停顿,不是闭眼滑。 - 任意关关闭提示仍可人解。 - 撞墙 10 次不误走、不穿模。 --- ## 17. 已关闭的问题 - 要不要合并进玩法一?不要。 - 要不要矩形 + 墙?不要。本玩法锁六边。 - 要不要计分?不要。 ## 18. 留给实现的缺口 - 对外名称是否就用「六边一笔 / Hex Stroke」。 - 墙的最终画法(斜纹宽度、是否要缺口给色弱)。 - M3 关是手做还是生成后人工改,看 M2。 --- ## 19. 附录 A · 转墙(实验机制,2026-09-15 加入) ### A.1 设计铁律:手指就是笔尖 路径的末端永远在指尖下面;手指走过哪些格,路径就是哪些格。任何让末端离开指尖的机制(滑行、传送、跳格、自动延伸)一律不做。新机制只能改三样东西:哪一步合法、数字怎么数、过关条件是什么。 ### A.2 规则 - 六边形网格上每个顶点交汇三条边,互成 120°。转墙固定在一个顶点上,任意时刻占其中一条边,画法与静态墙相同,只在轴心那一端加一个圆点。 - 每走一格(含起手那一格),所有转墙同拍顺时针转 120°。已走 t 格时,墙在第 `(phase + t) mod 3` 条边上。 - 判定能否从末端走到邻格,用的是**出发前**的位置;走完再转。撤销一步则倒转回去。状态完全由步号推出,不需要额外存档。 - 转到盘外或空洞的边上时,墙暂时不挡任何东西。 - 转墙不得与静态墙重叠;周围至少要有两条盘上的边。 ### A.3 玩家体验 玩家不仅要估计路线,还要估计到达某条边时墙在哪。线索数字给出步号,步号 mod 3 就是墙的位置,所以线索和转墙天然互相配合:为了让某一步“刚好开着”,路线要多绕或少绕一格。 ### A.4 内容铁律(在 §4.6 之上追加) 每个转墙必须同时满足: 1. 去掉它,解不再唯一。 2. 把它换成钉在任意一条候选边上的静态墙,解都不再唯一(保证“会转”参与推理,而不是随便一堵墙就能替代)。 同一顶点只能放一个转墙;两个转墙可以共用一条候选边。实验包里最多放到 4 个转墙(19 格),正式包按试玩结果定上限。 ### A.5 关卡文件 ```json "spinners": [{ "cell": "c", "corner": 0, "phase": 1 }] ``` `corner` 是该格第几个角(从 30° 起顺时针 0..5),`phase` 是空盘时墙在第几条候选边。候选边顺时针编号:0 = 背离该格的那条边,1 = 该格与顺时针下一邻格,2 = 该格与上一邻格。 ### A.6 实验包 `levels/pack-2-spinner.json`,9 关:2-1 教规则(起手后墙正好挡住直觉的第一步),2-2 用中段线索算拍子,2-3 静态墙 + 转墙综合;2-4 起转墙数逐步加到 4,格数 7→19,让“满盘都在动”的感觉逐关加强。