核心循环里没有决策,只有执行
+这个游戏在数学上不存在「坏棋」,也不存在失败。因此它无法产生解谜游戏赖以为生的那件事:想通了。
+ +我用仓库自己的 src/core.js 写了一个「猴子玩家」——完全不读轨道,随机点一枚在场棋子,连续 3 次点不动就随机转一下底盘。跑完全部 1000 关:
| 关卡版本 | 关数 | 通关率 | 动作 ÷ 棋子(中位数) | 含义 |
|---|---|---|---|---|
| v0.5 · 千段轨迹 | 1000 | 100% | 1.67 | 每枚棋子只浪费 0.67 次点击 |
| v0.4 · 长路与惊喜(手作) | 20 | 100% | 3.42 | 手作版反而更需要试探 |
| v0.3 · 弯轨 48 关(手作) | 48 | 100% | 2.60 | 同上 |
注意最后一列的方向:程序生成的 v0.5 把游戏做得比手作版更空,不是更厚。1000 关的扩产过程中,「需要思考」这件事被稀释掉了。
+ +开局就已经解完了
+ +| 结构指标 | 实测 | 为什么重要 |
|---|---|---|
| 开局即可直接离场的棋子占比 | 中位数 64% | 过半棋子不需要任何判断 |
| 开局 ≥50% 棋子可出的关卡 | 847 关 | 85% 的关卡开局就半解 |
| 开局 0 枚可出(必须先旋转) | 0 关 | 一次都没有出现过 |
| 「一波清空」:所有棋子开局全可出,任意顺序 | 285 关 | 28.5% 的关卡完全没有谜题 |
| 最少旋转数 = 0 | 568 关 | 差异化机制在 57% 关卡用不到 |
| 最少旋转数的全局上限 | 2 次 | 1000 关里没有一关需要转 3 次 |
为什么会这样:三条规则叠加的结果
+ +① 消除是单调的。src/core.js:87 的 trace() 只按占位判定阻挡,移走一枚棋子只会减少障碍、永不增加。所以「能出的就出掉」永远是最优策略,点击顺序不影响任何结果——所有可出棋子塌缩成一个等价类,有效分支因子约等于 1。
② 失败在数学上不可达。全部 1000 关 moveLimit === tiles.length,而 moves 只在成功离场时减 1、同时棋子也少 1,两者永远相等;rotationEnabled 全为 true,canRotate() 全部返回真。Game.lost 的两个分支都永不成立,src/app.js:170 的失败分支是死代码。
③ 游戏替玩家说出了答案。src/app.js:218 在每次旋转后弹 toast「转一转,N 枚棋子的出口打开了!」——而这恰好是 432 个「需要旋转」关卡里唯一需要动脑的那一刻。同时提示按钮免费、无限次,直接把完整路线画出来。
再叠上一层:通关面板那三颗星是 src/app.js:167 里写死的三个 icon('star'),无论你用几步、用不用提示,永远三颗。mistakes 在 core.js:297 老老实实累加,全项目零处读取。系统知道玩家会点错,但点错不产生任何后果,所以没必要显示——这正是问题的自白。
怎么改 · 已修订
+ +本节初版建议「旋转变有限资源」与「relay 停靠点上主线」已撤回。制作人的约束是面向超休闲玩家、难度尽量小且逐步释放——这两条都会引入「卡住 / 弄坏局面」的感受,对这个用户群有害。撤回的理由和替代方案见 §07 修订方案。这里保留的判断是:「难度低」和「没有选择」是两个旋钮——不动第一个旋钮,甚至再往下调,也能把第二个从 0 调到 1。
+ +不要加扣步惩罚。docs/gameplay-direction.md §2 的「免费旋转 + 免费试错 + 无出口不判负」是全项目最正确的设计决策,必须保住。§07 的全部方案都建立在这条之上。
1000 关是一个数量幻觉
+1000 关只有 30 个关卡名、562 块不重复的底盘,和一条完全水平的难度曲线。它不是资产,是同一关的 1000 次随机重排。
+ +同一块底盘,相隔 563 关
+ +我从 levels/v0.5.json 用仓库自己的 path-math.js 把底盘还原成矢量图。下面两张是第 14 关和第 577 关,移除棋子后的轨道布局:
37 格 · 7 枚棋子
37 格 · 9 枚棋子
这不是孤例。对全部 1000 关按「格坐标 + 轨道配对」做精确去重:
+ +| 指标 | 实测 |
|---|---|
| 不重复的轨道底盘 | 562 / 1000 |
| 与别的关共用底盘的关卡 | 591 关(59%) |
| 单块底盘的最高复用次数 | 21 次 |
| 不重复的关卡名 | 30 个 |
| 单个关卡名的最高出现次数 | 63 次(花瓣 · 清挡放行) |
30 个名字 = 6 种形态(花瓣 / 环绕 / 螺旋 / 回折 / 编织 / 双叶跨区)× 5 种体验(长路巡游 / 清挡放行 / 旋转换路 / 分段释放 / 轻巧反差)。整个 1000 关战役,就是这张 6×5 的表格,每格填进去 33 个程序生成的实例。
+ +20 章,没有一章有自己的身份
+ +下表每一行是一章 50 关。注意形态种类和体验种类两列——每一章都均匀地混着全部 6 种形态和全部 5 种体验。章节名写得有情绪(初见弯路 → 花瓣初开 → 千回百转 → 一路盛放),但它们只存在于一个下拉框的标题里。
+ +| 章 | 章节名 | +平均格数 | +平均棋子(↑ 唯一在增长的量) | +开局可出比例(→ 完全水平) | +必旋 % | 形态 | 体验 | +
|---|---|---|---|---|---|---|---|
| 1 | 初见弯路 | 34 | ++ | + | 40% | 6 / 6 | 5 / 5 |
| 2 | 朝内的出口 | 37 | ++ | + | 44% | 6 / 6 | 5 / 5 |
| 3 | 绕行花园 | 44 | ++ | + | 40% | 6 / 6 | 5 / 5 |
| 4 | 轻轻让路 | 48 | ++ | + | 42% | 6 / 6 | 5 / 5 |
| 5 | 环里的风景 | 48 | ++ | + | 46% | 6 / 6 | 5 / 5 |
| 6 | 花瓣初开 | 58 | ++ | + | 40% | 6 / 6 | 5 / 5 |
| 7 | 交织之间 | 58 | ++ | + | 44% | 6 / 6 | 5 / 5 |
| 8 | 两岸来信 | 60 | ++ | + | 42% | 6 / 6 | 5 / 5 |
| 9 | 回旋的发现 | 65 | ++ | + | 46% | 6 / 6 | 5 / 5 |
| 10 | 层层放行 | 68 | ++ | + | 46% | 6 / 6 | 5 / 5 |
| 11 | 走进花心 | 63 | ++ | + | 48% | 6 / 6 | 5 / 5 |
| 12 | 曲径相逢 | 65 | ++ | + | 42% | 6 / 6 | 5 / 5 |
| 13 | 另一侧的出口 | 61 | ++ | + | 40% | 6 / 6 | 5 / 5 |
| 14 | 编织新路 | 61 | ++ | + | 46% | 6 / 6 | 5 / 5 |
| 15 | 旋开的花园 | 64 | ++ | + | 42% | 6 / 6 | 5 / 5 |
| 16 | 从容穿行 | 63 | ++ | + | 42% | 6 / 6 | 5 / 5 |
| 17 | 旧路新发现 | 70 | ++ | + | 42% | 6 / 6 | 5 / 5 |
| 18 | 花与回声 | 69 | ++ | + | 40% | 6 / 6 | 5 / 5 |
| 19 | 千回百转 | 66 | ++ | + | 46% | 6 / 6 | 5 / 5 |
| 20 | 一路盛放 | 58 | ++ | + | 46% | 6 / 6 | 5 / 5 |
从第 1 关到第 1000 关,玩家面对的认知负担没有变化,只是要多点四下。第 20 章的平均格数甚至降回到 58,比第 10 章的 68 还小。
+ +顺便说一句核心卖点的真实密度
+ +「长路巡游:一次点击,享受一整段弯绕离场」是设计准则里的第一条爽点。我模拟了完整通关,用 exit-animation.js 的 exitTiming() 统计玩家实际看到的每一段动画:
| 观看内容 | 实测 | 判断 |
|---|---|---|
| 一关中最长那一段离场动画 | 中位数 4.24 秒 | 卖点成立 |
| 一关中全部离场动画总时长 | 中位数 11.0 秒 | 可接受 |
| 单次离场动画时长 | 中位数 0.57 秒 | — |
| 时长 < 0.5 秒(一闪而过)的点击占比 | 43% | 密度不足 |
| 时长 ≥ 2 秒(真正的巡游)的点击占比 | 15.2% | — |
结论要说公道:长路的高光每关确实会出现一次(95% 的关卡至少有一段 ≥2 秒),这一条设计目标是兑现了的。问题是其余 43% 的点击换来的是不到半秒的一闪而过——爽点存在,但被大量无内容的点击稀释了。
+ +怎么改
+停止关卡生产,把 1000 关砍到 200–300 关。排序键从「棋子数」换成三元组 (必须旋转次数, 解法波次, 首波可点比例),并在章内强制单调:第 1 章开局可出比例上限 0.80,第 6 章 0.55,第 12 章 0.35。「0 次旋转即可通关」的全局比例从 56.8% 压到每章 ≤10%——v0.1 的 24 关做到过 0%。同时把 levels/v0.5-analysis.json 移出发行包,src/levels-v05.js(单文件 9.6 MB,首屏静态 import)改成按章 dynamic import,levels/v0.5/chapter-01..20.json 已经切好了只是没接上。
核心动词「观察」,在 UI 上根本不存在
+你不肯让玩家免费地「自己看」,却愿意免费无限地「直接告诉他答案」。这是整份评审里性价比最高的一处修改。
+ +这个游戏的核心动词是看懂这枚棋子会走到哪。但正式模式里没有任何「看」的操作——没有长按、没有悬停、没有选中高亮。playTile 的第一行就是 path=null,然后直接执行。唯一能看到一条路线的方式,是把它执行掉。
而能力早就有了:
+ +| 位置 | 做的事 | 谁能用 |
|---|---|---|
| app.js:201 | 编辑器「查看路径」工具:path=trace(draft,tile) | 只有开发者 |
| app.js:244 | 提示按钮:path=trace(...) 画出完整路线,无次数限制、无消耗 | 玩家,但只能看求解器替他选中的那一枚 |
| app.js:179 | playTile 开头 path=null | 玩家想看的那一枚 —— 看不了 |
渲染通道 renderer.js:79 已经在那儿了,提示按钮上甚至标着「免费」。当规则前提是「旋转免费、失败免费」、唯一有成本的动作是成功离场时,不给免费预览是自相矛盾的:你把所有摩擦都去掉了,却在最需要思考的环节强迫玩家用执行代替思考。结果是玩家养成「乱点试试」的习惯,这又回过头来印证了第 01 条。
怎么改
+canvas 的 pointerdown 挂一个 250 ms 计时器,触发时执行 path=trace(game.level,tile,game.tiles); draw(),pointerup 清空且不调用 game.tap()。可离场用 THEME.primary、被挡用 THEME.warning 画到接触点。同时把提示按钮的文案从「提示」改成「找一枚能走的」——让「我自己看」和「帮我找」在语义上彻底分开。另外把 TILE_RADIUS 从 .985 降到 .90,让棋子底下露出一圈轨道端口:规划「移开 A 之后 B 往哪走」所必需的那段轨道,现在恰好被 A 自己完全盖住。
棋盘在目标设备上物理不可读
+54% 的关卡,棋子可点直径低于 iOS 最小触控尺寸。而且瓶颈是宽度不是高度——「把画布加高」这条路是堵死的。
+ +触控尺寸
+ +用 geometry.js 的 layout() 在 390 px 宽手机上逐档实算。下图中每个六角都按真实相对尺寸绘制,虚线圆是 44 px 最小触控参考:
layout() 取 min((width−48)/跨度X, (height−50)/跨度Y, cellLimit);对 91 格棋盘,宽度项 19.18 < 高度项 23.20,宽度胜出。我把画布高度从 492 px 调到 700 px 重算,宽度项仍然是瓶颈——布局回收救不了它。| 底盘 | 六角宽 | 可点直径 | 关卡数 | 判断 |
|---|---|---|---|---|
| 19 格 (r2) | 82 px | 72 px | 7 | 充裕 |
| 37 格 (r3) | 59 px | 52 px | 452 | 合格 |
| 61 格 (r4) | 47 px | 41 px | 268 | 低于 44 px |
| 91 格 (r5) | 38 px | 34 px | 273 | 远低于 44 px |
这是第 500 关的真实棋盘
+ +91 格、14 枚棋子、近 200 段等权重的深蓝曲线。请试着用眼睛从任意一枚棋子追到它的出口:
+ +在手机上,这整块图的宽度是 326 px。
棋盘越大不是「更有内容」,而是「更看不清」。而看不清 + 试错免费 = 乱点——这和第 01 条是同一个洞的两面。
+ +renderer.js:69 其实内置了 trackEmphasis 降权机制,labs/flower 和 labs/campaign-review 都在用,唯独主游戏的 draw() 没传这个参数,所以每一根轨道都以 primary 全权重渲染。
我注意到 docs/v0.5-browser-review.md 记录了「主路线加粗」方案被撤回。撤回本身是对的——加粗 routeDesign.featured 会直接剧透答案。但正确的做法不是回到全部等权重,而是按「与任何棋子当前可达集不相交」来降权:只压掉纯装饰的线,不暗示解法。
竖屏空间分配
+ +在一个只有棋盘值得看的游戏里,棋盘只拿到 43%。还有一处纯浪费:design/components.css:64 用 display:none!important 把缩放工具条藏了——board-viewport.js 的双指缩放和拖动代码完整运行着,但屏幕上没有任何东西告诉玩家这个能力存在。
怎么改
+① 主线棋盘上限降到 61 格,91 格那 273 关移出主线;② 非活跃轨道降到 35% 不透明度;③ 恢复缩放工具条的可见性,或至少在首次进入 61 格以上关卡时提示一次「双指可放大」;④ 回收顶部那 130 px 死区。
+5 套设计 × 5 主题 = 1 套设计 × 25 个色板
+没有一套设计触碰到真正的视觉问题。25 组色板全部是在同一个有缺陷的画面上调色。
+ +我逐张看了 design/index.html 的五套方案。01 柔和原色 / 02 高彩游乐场 / 03 积木轨道屋 / 05 轻线纸境——棋盘造型、棋子形状、箭头画法、轨道渲染方式完全一致,变的只有色相。只有 04「动物弯弯镇」真的换了棋子形象(也是五套里唯一有角色感的),但代价是方向箭头被缩小、可读性下降。
真正的视觉问题——轨道不可读、棋盘没有可辨识结构、没有世界观——五套里一套都没碰。
+ +主题的使用方式也白做了
+ +themeScore 绑在单关难度 tier 上(campaign-browser.js:35,查表 [12,32,48,62,78]),而 tier 在相邻关之间跳来跳去:
| 指标 | 实测 | 后果 |
|---|---|---|
| 999 次关卡切换中背景变色次数 | 696 次(69.7%) | 过一关屏幕闪一次,不承载任何信息 |
| 最长连续同主题 | 6 关 | 建立不起「第 7 章是紫色的」这种空间记忆 |
| apricot / lagoon / lavender / rose | 527 / 319 / 145 / 9 | 一个主题占 52.7%,一个只出现 9 次 |
| midnight「每周挑战」 | 0 关 | themeForScore 只返回索引 0–3,发行路径上永不可达 |
也就是说:投入了 5 套设计 × 5 主题的产能,玩家在发行路径上实质只会看到 2 种颜色,还是以随机顺序闪给他看。
+ +还有一处被绿灯掩盖的问题
+ +design/contrast-report.json 33 项全 pass,但它校验的是 token 语义配对,不是 token 在场景里的实际组合。实测白色旋转箭头压在场景背景上的对比度:
| 设计方案 | 白色旋转箭头 vs 场景背景 | 判断 |
|---|---|---|
| paper · 轻线纸境 | 1.15 – 1.20 : 1 | 基本隐形 |
| calm · 柔和原色 | 1.35 – 1.60 : 1 | 不合格 |
| animals · 动物弯弯镇 | 1.81 – 3.32 : 1 | 多数不合格 |
| toy · 积木轨道屋 | 1.62 – 3.44 : 1 | 多数不合格 |
而旋转恰恰是这个游戏最鼓励玩家做的动作——app.js:76 的 rotationHint 还会主动引导玩家去按它。这就是「文档写得很完备」和「设计做得好」之间的差距:33 项全绿的绿灯,掩盖的是屏幕上最看不见的东西恰好是最该被看见的操作。
怎么改
+主题绑章节,一行改动:app.js:87 的 applyTheme 输入从 production.themeScore 换成 Math.floor(current/50),20 章映射到 5 个主题、每 4 章换一次。背景色切换从 696 次降到 4 次,每次都对应玩家能感知到的章节边界,midnight 也真正进入发行路径(第 17–20 章)。难度信息不要用背景色承载。
旋转按钮加一个实底托盘:用 --color-utility (#52697E) 做 48 px 圆角方形背景,白图标放上面——onUtility/utility 这一对在 CONTRAST_PAIRS 里已经是过审的 3:1 配对,直接复用不引入新色。同时在 CONTRAST_PAIRS 里补一条「控件 / 场景背景」让它进构建门禁。
选关界面是开发者调试面板
+「解锁」是休闲解谜唯一免费的货币,现在被一个 1–1000 的输入框白送掉了。
+ +点开那个入口(按钮的 aria-label 写的是「选择关卡」——命名本身就承认它不是地图),看到的依次是:
| 元素 | 服务于谁 |
|---|---|
版本 <select>,选项文字如「v0.2 · 直线较多版 · 48 关」 | 开发者 — 这是 changelog |
章节 <select>(20 项) | 开发者 |
| 1–1000 数字输入框 + 「前往」 | 开发者 — 「我要验证第 847 关」 |
| 上/下章翻页器 + 50 个方块按钮 | 玩家 |
| 两段「浏览器是否允许保存」的免责说明 | 法务 |
指向 labs/campaign-review/ 内部制作审核页的链接 | 开发者 — 不该发给玩家 |
三个具体伤害
+①「直线较多版」是在让玩家评估你的开发决策,同时告诉他「我们改过 5 次方向」。② 五套独立存档把玩家投入切成五份——打完 v0.4 的 20 关,对总进度零贡献。③ v0.1–v0.4 那 140 关是有设计文档背书的手工内容(v0.4 的 curvedRatio 0.784 对 v0.5 的 0.394),质量密度远高于 1000 关,却被埋在下拉框第二层——新玩家默认 v0.5,永远看不到。
另外 .done(已通关)这个 class 在 style.css 和 design/*.css 里一条规则都没有——通关与未通关的按钮在关卡网格里几乎没有视觉差别。
怎么改
+版本 <select>、1–1000 输入框、审核页链接、存储免责说明四项全部从玩家弹窗移除;版本切换保留 ?campaign= URL 参数即可(campaign-browser.js:2-8 已支持),审核链接移到 labs/ 自己的入口。把 v0.1–v0.4 的 140 关择优混编进主线前 240 关、统一存档——一条 1140 关的线,远胜五条互不相干的线。给 .level-btn.done 写实际样式(填充色 + 星标),并按章加锁。
不动难度旋钮,把「选择」从 0 调到 1
+制作人的约束:超休闲、难度尽量小、逐步释放。修订后的方案没有一条引入失败态、卡住感或资源限制;其中四条比现状更容易。它们改的是「有没有的选」,不是「要想多久」。
+ +修订过程
+初版六条方案交给五路独立视角攻击——超休闲留存制作人、仓库维护者、关卡数据分析、UX 一致性、以及最初写这份尖锐评审的人。结果:六条没有一条被判「保留原样」,每条都被指出具体漏洞;五路一致同意撤回旋转限额与 relay。下面是核对到代码和数据之后的最终版,凡是批评者的数字,我都用 core.js 自己重算过。
先修我自己漏掉的:自动提示不是两处,是三处
+初版只删了 app.js:185(离场后高亮下一枚)和 app.js:218(旋转后高亮)。五路全部指出我漏了 app.js:103–110:只要场上没有任何棋子能离场,旋转按钮立刻被点亮、指令条直接写出该转哪边。我重算了这一处的影响:
| 首次旋转时剩余棋子 | 关卡数 | 占比 |
|---|---|---|
| 2 枚 | 223 | 52% |
| 3 枚 | 173 | 40% |
| ≥4 枚 | 36 | 8% |
92% 的旋转发生在「剩 2–3 枚、无路可走」的状态——而 103–110 恰好在那一刻把方向点亮。也就是说,「转哪边」这个全游戏唯一的真决策点,在 432 关里全部由机器代答。任何关卡曲线里的「需旋转 %」在这条提示下都是装饰。
最终方案
+ +三处自动提示全部改为闲置门控,三套「看路」机制并成一套
+删 app.js:185 / 218 的 hint=opened[0].id;103–110 的旋转方向高亮同样进闲置计时。阈值不是初版的 3 秒——从最后一个动画结束起算(不是从点击),T = clamp(4 s + 0.6 s × 剩余棋子, 5 s, 10 s),无出口需旋转时用 T/2;先出文字、再过 T 才画环。toast 保留但去掉数量(「有出口打开了」)——N = 1 时等于点名。闲置提示不置 helpUsed。提示按钮改为「立即触发闲置提示」,删掉 244 行直接画完整路线——长按预览、闲置提示、免费按钮三套重叠机制减为一套,代码反而少。教学关保留即时高亮。
当天
按下看路,松手发车——不是 250 ms 长按
+被挡点击只花 800 ms 而且免费,「直接点、看它弹回来」比 250 ms 长按更快,所以长按永远不会被用(这一档没有长按交互)。改成 Parking Jam 手感:pointerdown 立即画该棋子路线(可出 primary / 被挡 warning 画到接触点,core.js:84 已给 collisionPoint),pointerup 在同格 = 发车、滑出 = 取消。手势仲裁:单指才启动,移动 > 8 px 取消,预览中松手不 tap 不 drag,拦 contextmenu(Android)。预览路线要画在棋子之上(renderer.js:77 现在在棋子之下)。
初版的「非可达轨道降权」作废:动态可达集 = 每次离场后棋盘自动亮出「谁能走远了」,是比 185 行更强的剧透;静态六朝向并集我实算覆盖 93–99% 的轨道段,降不了什么。替代:全盘轨道统一软化到 55–60%(用 renderer.js:69/74 已有的 secondary 通道,不用 alpha,保住 3:1 校验),只有按下的路线满色。
再加两条 always-on 的观察教学:离场棋子拖一条 200–300 ms 淡出尾迹;被挡时用 warning 色把路线画到接触点停 600 ms。每一次点击都在免费示范「它是这样走的 / 这样断的」。
1–2 天
连击:档位由「这一下打开了几枚」驱动
+初版说连击「让看一眼再点有理由」——不成立:开局 64% 可出,285 关永不断,配上免费预览更不会断。连击是反馈,不是选择。但有一处能让顺序第一次有可感知的后果:app.js:184 已经算出每次点击解锁了几枚(opened.length),这是整个引擎里唯一随顺序变化的量。档位由它驱动:一下打开 N 枚就跳 N 档,opened = 0 只维持不升。「先移走闸门棋子」在 715 关里第一次被感知,零惩罚。
实现要点:计数在 game.tap 返回处(route.status==='exit'),不等动画;归零延到撞击音同帧(app.js:122 progress ≥ 0.42),否则玩家先看到连击消失再看到撞击,因果倒置;断裂静默,无红无音;旋转、预览、闲置提示都不归零;undo 用平行栈恢复;教学关不计;音高封顶 +5 半音;标签画在离场点,不占 board-toast;时间窗 2.5 s,短于闲置阈值,防「等提示续连」。
1 天
两枚只做加法的印章,不进关卡格
+这一档没有按关评级——Hexa Sort、Screw Jam、Parking Jam 的通关面板都是无条件庆祝。所以印章只做加法:「完美」= 本局 mistakes === 0,达成时多放一次印章动效 + 彩带;没拿到时面板和现在完全一样(三颗硬编码的星改成无条件彩带,别再叫「星」)。不进关卡格、不进任何持久列表——地图上「3 金 40 灰」就是超休闲眼里的失败。
「一转到位」= game.rotations === par,par 取 app.js:67 已缓存的参考解旋转数;只在达成且 par > 0 时出现,缺席时什么都不显示,不显示数字。这是旋转限额的「只影响印章、不影响通关」软版本——432 个旋转关第一次有了「这一转是不是多余」的判断,零新状态。
三条明确规则:撤销洗白(core.js:278 snapshot 含 mistakes)——接受并写明;helpUsed 不影响印章;285 个全可出关不发印章、显示「畅通」、不计聚合。存档 normalizeProgress 白名单加 perfect,改 6 处测试断言。上线顺序硬依赖:第 2 条先于本条——没有零成本看路手段之前,不能让撞墙有任何记账。
2 天
重排发布为 v0.6,用分带配额,不用全局排序
+已被 §08 取代。制作人随后定下「旋转是主解法,零旋转关 ≤10%」——池子核对后,「首转时剩余 ≥50% 且一转释放 ≥2 枚」的关现有只有 12 关,重排做不出旋转为主的战役,必须重新生成约一半。下文保留作为数据层的约束记录(存档索引、验证脚本按 index 断言等仍然成立)。
+「不重做任何关」在数据层不成立。v0.5.json 顺序 = chapter-XX.json = levels-v05.js,验证脚本和测试按 index 断言,每关内嵌章名,存档 completed 是索引数组——原地重排会把已通关标记静默映射到别的关。做法:campaign-versions.js 加 v0.6 并设为默认,v0.5 与其存档原样保留。
初版需求分公式错了:(1−可出比例) 与波次共线(r = 0.84),对猴子动作数做回归后三项权重是 0.08 / 0.48 / 0.43——我给了最弱项 0.6。改用分带配额:R 全可出 · G1 零旋转 ≤2 波 · G2 零旋转 ≥3 波 · T1 一转且首转剩 ≤3 枚 · T2 两转,每章配额 R 递减、T 递增。
「温柔旋转关只有 12」是我判据错——旋转的温柔度是「首转时剩几枚」不是「开局可出比例」:T1 且剩 ≤2 枚有 223 关(≤37 格 117 关),第 1 章第 2 关就能放,接住教学第 3 课。
初版前 4 章 200 关零决策 = 超休闲 D1–D3 的全部时长,流失原因会是无聊而不是难;这一档对零决策的容忍度是 5–10 关。第 1 章缩到 20 关;全可出关从第 3 章起每章 ≤15%,当喘气关。同底盘间隔 ≥100 关(562 块底盘、最大一组 21 关)。6 形态 × 5 体验改为按章解锁式亮相——这才是这一档的「坡度」。
1 周
91 格:第 2 条落地后真机重测再定,且不是一刀切
+初版用 44 px 门槛砍 91 格自相矛盾——61 格 41 px 同样不过线而第 3 章就在用。改用「底盘半径 ≤ 4」。不整体删:91 格里 98 关全可出 / 一挡放行是全库最强的观赏内容(参考解最大弯数中位 16,37 格只有 10),保留作第 7 章起的「长局回报」槽,每章 ≤6 关、不连续;175 关需要决策的 91 格移到主线外的可选「大棋盘」章,不进进度分母。
+随 v0.6
分带配额后的曲线
+对抗核查中由关卡数据一路实算(移出需决策的 91 格、14 章 × 52 关;其底层「首转剩余棋子」分布与我的重算逐位一致)。对比初版全局排序:前 200 关需旋转 0%、全可出 100%。
+ +| 章 | 开局可出比例 | 需旋转 % | 猴子 APT | 第 1 章体验分布 |
|---|---|---|---|---|
| 1 | 1.20 | 巡游 29 · 清挡 17 · 反差 3 · 换路 3 | ||
| 3 | 1.32 | |||
| 5 | 1.81 | 分段释放从本章起 | ||
| 7 | 2.13 | 91 格长局回报从本章起 | ||
| 10 | 2.21 | |||
| 12 | 2.53 | |||
| 14 | 2.44 |
第 14 章的 0.49 仍然比 v0.3 / v0.4 手作版宽松(它们的猴子 APT 是 2.60 / 3.42)。整条曲线的最高点,都没有超过项目自己两年前手作关的难度。
+ +批评者补上的、我漏掉的(全部零难度)
+ +教学改覆盖层:三个弹窗才进第 1 关,这一档是 5 秒进真关
+三课教学每课通关后 app.js:164 都开一个结果弹窗——玩家要关 3 个弹窗才见到第 1 关。Screw Jam / Hexa Sort 的做法是手指覆盖层引导、无弹窗。把三课并进正式第 1–3 关做覆盖层。超休闲留存一路认为这可能是 D1 最大的单一杠杆。
2 天
用朝向,不用顺序:让旋转变成关于「奖励」的选择
+实算:3,294 枚棋子(928 关)换一个朝向能走更长的路;285 个全可出关里有 263 关旋转后有更长的巡游。可选目标「本关里程 N / M」或「让 X 号跑完最长路」(读 trace().steps.length,core.js:87 已给),让 568 个零旋转关的旋转按钮第一次有用途,且永远不影响通关。这是核查中找到的最强的「零难度的选择」——旋转不再是关于「能不能出」,而是关于「看多长」。
验证后定
主题绑章节 · 关名按章生成 · 手作关作章末加餐
+主题绑章节(app.js:87,2 行)在初版评审里有、修订时丢了,补回。30 个关名配 1000 关,改为家族 × 体验 × 章名生成(production-plan.js 已有表,10 行)。v0.3 / v0.4 那 68 关手作关(猴子 APT 2.60 / 3.42)作各章末尾的加餐,零风险现成内容。
半天
「旋转作为可选加速器」的内容现在几乎不存在——下一版生成器的事
+568 个零旋转关里,开局转 ±60° 能多开 ≥1 枚的只有 38 关,≥2 枚的 3 关。「转一下更快」这种不必需但有回报的旋转,靠重排造不出来,要在下一批生成加 rotGain ≥ 2 的候选带。记在这里,不进当期。
+下一版
撤回的两条,以及它们的软版本
+旋转限额:撤回维持。五路一致。这一档所有「有限资源」都靠混合休闲经济(金币买道具)兜底,本项目没有经济系统,限额只剩惩罚;而且 3 次额度恰好只会咬在 140 关两转关上——那已经是全库最难的关。软版本就是第 4 条的「一转到位」印章。
relay 停靠点:不上主线。它是阻塞态(labs/variants/README.md「P 被占用时不能进入」),与定位正面冲突。但作为第 8–10 章之后的章节专属滴灌机制(每章只出 1 次、19 格 3–5 枚样本)是这一档 D7 的常规手段,可以留作后续。
诚实的结算
+最初写那份尖锐评审的视角给修订方案打了分:「没有选择」这个根本问题,六条加起来解决了 3 / 10。因为没有一条改动状态空间——移走棋子仍然只减障碍,285 关仍然任意顺序清空。剩下的分数要靠三条真正触及「后果」的东西:朝向里程目标(把旋转变成关于奖励的选择)、opened.length 驱动的连击档位(顺序第一次有可感知后果)、「一转到位」印章(旋转次数第一次有判断)。这三条合起来,是「不动难度旋钮」这个约束下的天花板。再往上就要动规则,那是验证过这一轮之后的事。
曲线必须上升,而且只有两个旋钮能让它上升
+制作人定下三条:零旋转关 ≤10% 只作补偿;难度是双刃剑;曲线一定增长、增长要合理——因为老玩家熟练后同一关越玩越简单。这三条在项目自己的调研里都有出处,而且项目自己的 1000 关计划表本来就是一条上升曲线。v0.5 没有执行它。
+ +原则对应的原文
+| 制作人原则 | 原文依据 | 原文给的数字 | 不能迁移的部分 |
|---|---|---|---|
| 老玩家越玩越简单 → 曲线要涨 | 《一箭又一箭》:「其实我们的难度并没有变,只是他们更熟练了」「到了一定的关卡,就需要增加困难关卡的频次」;3 档扩到 5 档,多次 A/B | 无阈值、无频率 | 该游戏有失败态与时限,「困难」口径不同 |
| 增长要合理 = 边际新意恒定 | Refraction CHI 2015:已掌握的概念代价趋近 0,编排目标是让下一关的新负担保持在目标值附近;概念耗尽后系统会连续选相近关 | 2,377 名玩家实验;无频率数字 | 其生成器保证所有解共享解法图,本项目有多种释放顺序 |
| 太低无聊、太高流失 | Lily's Garden arXiv 2401.17436 §1、§3:前 ~100 关教学与容易关,之后存活老玩家的体感稳定在均值 3.2 次尝试、难关 ≥7 次 | 3.2 / 7 / 1 次 | 次数全部不可用(本项目无失败态);前 100 关的长度(本项目规则少得多) |
| 新机关低频 | 《一箭又一箭》:新机制「平均一天只遇到 1–2 次」;日均 30–40 关,核心用户 200–300 关 | 1–2 次/日;30–40 关/日 | 旋转不是新机关,是主解法——它的新组合频率应远高于此 |
项目自己的计划表 vs. 实际产出
+docs/1000-level-production-plan.md §3 的预算表是上升的:挑战关(D4+D5)从 1–50 关的 0% 升到 801–1000 关的 22.5%,D1 从 50% 降到 12.5%,D5 从第 501 关起。研究报告 §7 已把同文件里「后半程保持难度平台」修正为「体感校准目标,不能固定结构难度而忽略熟练度」——这与制作人的原则③一致。实际 v0.5:
| 范围 | 预算 D4+D5 | 实际 D4+D5 | 预算 D1 | 实际 D1 | 判断 |
|---|---|---|---|---|---|
| 1–50 | 0% | 6% | 50% | 42% | 第 32 关已出现 D4 |
| 51–100 | 7% | 20% | 20% | 38% | 第 2 章 = 第 20 章 |
| 201–500 | 13% | 16% | 12% | 30% | D1 是预算的 2.5 倍 |
| 801–1000 | 22.5% | 15% | 12.5% | 30% | 末段比预算更容易 |
而且 D 档本身退化成了旋转次数的换名:campaign-evidence.js:224 每次必需旋转记 15 分,结果零旋转关从不进 D4、两转关几乎都是 D4+。档位数不是问题,档位的定义才是。
规则层的一个事实:只有两个旋钮能拉曲线
+消除单调(移走棋子只减障碍)+ 旋转免费可逆,意味着「先清完能清的、再转」永远不吃亏——「什么时候转」在这套规则下永远构不成难度。能拉的只剩两个量,而 v0.5 把它们全程按平了:
+ +| 范围 | 卡住时有效朝向(均值 / 5) | 最近有效朝向距离 = 1 | 被旋转锁住的棋子(均值) | 只有一侧能开路 | 含擦碰阻挡关 | 91 格关 |
|---|---|---|---|---|---|---|
| 1–50 | 3.65 | 100% | 2.30 | 45% | 28% | 0% |
| 51–200 | 3.90 | 100% | 2.67 | 25% | 35% | 1% |
| 201–500 | 3.89 | 100% | 2.58 | 22% | 39% | 14% |
| 501–800 | 3.84 | 100% | 2.74 | 29% | 39% | 15% |
| 801–1000 | 3.75 | 100% | 2.63 | 28% | 41% | 17% |
三件事一起看:① 卡住时平均 3.8 个朝向都能开路、最近的永远只差一格、锁住的永远只是最后 2–3 枚——搜索广度和锁住占比全程为零坡度;② 真正在涨的是擦碰阻挡(28% → 41%)和 91 格大盘(0% → 17%)——这是阅读负担,不是推理,对超休闲玩家是最危险的轴;③ 一个反直觉的发现:第 1 章反而是全程最挑方向的一段(45% 单向关,之后 22–29%)——新手最先碰到的旋转最挑剔,老手碰到的反而随便转,和技能习得顺序正好相反。
+ +还有一处结构性陷阱:production-plan.js:41 把第 3 章起每个章末(第 150、200、…、1000 关)强制成「分段释放 · 91 格 · 14 枚 · 转 2 次」。我逐关核对:18 个章末全部如此。结果是可预测的「第 50 关一定难」加上 16 对相邻双峰(149–150、249–250……),直接违反计划自己写的「不能让玩家猜到每第七关一定难」和「第 1000 关可以是庆祝关」。按日均 30–40 关,这个峰大约每 1.5 天落一次,多半落在当日会话末尾——最坏的流失位置。
v0.6 曲线:分段目标
+以计划表的形状为最低坡度,按「旋转是主解法」重定义各档,把「后半程平台」改为持续缓升。数字全部是待 A/B 的起点,不是验证过的最优——原文没有给任何频率或阈值。
+ +| 范围 | 零旋转关 | 平均旋转 | 锁住占比 | 卡住时有效朝向 | 单向关 | D4+D5 | 本段新引入的旋转关系 |
|---|---|---|---|---|---|---|---|
| 1–50 | 10% | 0.9 | 25–30% | ≥2,两侧都行 | <15% | 0% | 第 2 关就转;一转收尾;一转放一组 |
| 51–200 | 10% | 1.0 | 30–35% | ≥2 | 15–25% | ≈7% | 开局先转;往返双转;同向双转 |
| 201–500 | 10% | 1.1–1.2 | 35–40% | 1–2 | 25–40% | ≈13% | 三波释放;单向双转;一转放一组升级 |
| 501–800 | 8% | 1.3 | 40% | 1;出现需转 2 格 | 40–50% | ≈18% | 首个三转(D5);早转且单向 |
| 801–1000 | 8% | 1.4–1.5 | 40–45% | 1 | ≈50% | 22–25% | 不再引入新关系,只做跨章重组 |
「增长要合理」的量化(推断,依据标注):困难关间隔从前 100 关的每 12–15 关一个,到 601–1000 关的每 5 关一个;D5 之后一律立刻接喘气关,D4 之后在 300 关前立刻、之后允许隔一关,间隔无规律。新旋转关系前 10 章每章引入 1 个、首次出现放在 D3 教学位、10 关内复习 2–3 次;11–20 章不再引入新关系而做跨章重组——Refraction 的教训是概念耗尽后系统必然退化成相近关反复,v0.5 后段 489 关旋转完全无用正是这个样子。可读性两项从难度里剔除并封顶:擦碰阻挡关 ≤40%,91 格按 §07 处理。
+ +10% 喘气关怎么放
+先解决一道算术:现行 scorer 一次必需旋转 = 15 分 → 最低 D2,所以「零旋转 ≤10%」意味着 D1 ≤100 关,低于计划表的 150。D1 必须按「决策数 ≤1」定义,不按旋转次数。喘气有两种合法形态:(a) 零旋转开放巡游——占 10% 配额;(b) 一转即全清——卡住后任一方向转一次、转完全部可出、主角走长路。这是招牌爽点,旋转是兑现不是负担,应计入喘气但不占配额。而 v0.5 里「旋转是开局第一步、转完全盘打开」的关一个都没有。
+100 个零旋转关的绑定顺序:每个 D5 之后 +1;每个「新关系首次出现」的 D4 之后 +1;第 1 章教学位约 20 关;章末庆祝位(替换掉强制的分段释放峰)。第 100 关以后不允许两个零旋转关相邻——v0.5 有 27 串四连、20 串三连。
+ +池子现实:不是重排,是重做一半
+| 档 | 可算定义 | 现有池 | v0.6 需要 | 缺口 |
|---|---|---|---|---|
| D0 喘气 | 零旋转,开局 ≥75% 可出,≤1 波 | 325 | ≈90 | 富余 |
| D1 一转收尾 | 1 转,锁住 ≤2 枚,两侧都行 | 185 | ≈280 | ≈100(且 223 关同一互锁套路,去重后更大) |
| D2 一转换路 | 1 转,锁住 ≤50%,放 ≥3 枚或单向 | 102 | ≈280 | ≈180 |
| D3 早转 / 双转收尾 | 1 转且锁住 >50%,或 2 转收尾 | 64 | ≈190 | ≈125 |
| D4 双转组合 | 2 转且(锁住 >35% 或 单向 或 ≥3 波) | 81 | ≈135 | ≈55 |
| D5 深旋转 | ≥3 转 | 0 | ≈25 | 25 — 构造原型已完成:src/deep-lock.js 四子链式锁,6 形态 × r3/r4 × 8 种子产出 55 关(锁住占比中位 78%,每转释放 ≥2 枚 38/55),可在 labs/d5/ 试玩 |
最关键的一个数:「首次旋转时剩余 ≥50% 且一转释放 ≥2 枚」的关,现有 1000 关里只有 12 关。旋转为主的战役靠重排做不出来。最少新生成约 480 关;D1 池去重后约 510 关。现有 140 个两转关清一色是「首转放 1 枚、二转放 2 枚」的同一节奏,v0.1 的 24 关里有 4 关需要 3 转——规则允许,生成器没做。
+ +生成器要改的地方
+编排:beats 按档位重写为 8 种体验,5–8 拍锯齿乐句
breath / finish / group / choice / early / return / stages / deep。每句喘气 ≤1 拍且紧跟该句最高档之后;前 4 章不用 deep;11 章起启用;峰值位置按章打乱。introduction 前 10 关改为第 2 关就转、零旋转 2 关。删掉 :35 的 stages→turn 改写和 :41 的章末强制 stages。
1 天
生成:三个新构造参数
turn/contrast 加 lockedShare(D1 0.2 / D2 0.35 / D3 0.6)——前 N 枚按朝向 1 摆放并要求在朝向 0 被挡;stage-seed 加 stagesCount=3 与「首转释放 ≥2」;contrast 允许 home=2(两转才接上长路)。这是 D5 池从 0 到 25 的唯一路径。
3–4 天
筛选:按档位拒绝,用 core.js 可算的量
inspectCandidate 新增 band-rotations / band-dependency / band-direction / breath-too-deep / stage-rhythm-repeat 五个拒绝码;质量分加 −3·|锁住占比 − 目标|;旋转次数只作诊断不再进分;maxDryRotations 对 D5 放宽到 3。同底盘间隔 ≥100 关。
1–2 天
档位:从「旋转次数 × 15 分」改为三元组
(锁住占比, 卡住时有效朝向数, 卡住点数)。擦碰阻挡与 91 格记为可读性负担,单独封顶,不进难度分。计划 §2 本来就要求四维分开记录。
半天
上线后怎么判断坡度对不对
+本游戏无失败态,重试次数这个仪表不存在。替代指标(先验区间,须用前 100 关真人数据标定):连续空点(点了没动的最长连续次数:1–100 关 p90 ≤2,某关 p50 ≥3 = 可读性缺陷不是难);首次离场耗时(跨章 p50 上升 = 曲线在涨,连续 300 关不变 = 平);卡住停留(无可出 → 下一次旋转的秒数,50 关后 p50 <3 s;→0 且无效旋转也 →0 = 旋转已反射化);旋转效率(有效/总,55–80% 健康,≥90% 连续 200 关 = 无决策,<40% = 瞎转);逐关会话终止率(对滚动基线,>2× 的关标出——唯一能直接看到章末双峰伤害的指标);玩家用时 / 结构估算按档分组(D4 与 D2 比值相同 = 档位没在玩家身上兑现)。
+ +原文没回答、要试玩才能定的
+熟练度在本项目里多快累积(同一批玩家第 2 章和第 15 章各玩一次同结构关,测首次有效动作前时长的下降);「太难」在无失败态下的表现(退出位置而非重试);零旋转关放在困难关之后是否被感知为奖励;「中途旋转」比「收尾旋转」难多少;11–20 章只做重组会不会陷入 Refraction 的概念耗尽。这五条决定坡度涨多快,原文只说「到一定关数」。
+做对了的(少而实)
+这个项目的技术底子是好的。上面每一条建议的代价都在十行到百行量级,而不是重构——这本身就是这套架构的功劳。
+ +计数语义
+旋转免费、失败试路免费、无出口不判负、撤销全量恢复棋盘与朝向。这消除了休闲解谜里最大的焦虑源。core.js:281–298
+扫掠碰撞与绘制同源
+画出来的线和判定的线是同一份数据,用 Minkowski 六边形做精确扫掠裁剪,出界射线还额外延长 24 单位防穿模。同规模项目几乎都在这里翻车。path-math.js
+离场动画按真实弧长计时
+exitTiming() 用 routePoints 累加实际路径长度算时长,不是固定补间。靠近出口的棋子不会被强行拖成同样长的等待。exit-animation.js:5–12
连点队列存 id 不存坐标
+全文 7 行。队列只存 id,取出时对最新棋盘重新查找——旋转 240 ms 期间连点会按新朝向重判。这个细节多数项目做错。tap-queue.js
+教学三课共用同一块棋盘
+19 格盘不变,只把棋子从 1 → 2 → 2 加上去,第 3 课才打开旋转。玩家从教学到正式第 1 关没有任何视觉断层。方法论完全正确。tutorial-levels.js
+旋转底盘这个机制本身
+底盘转 60° 而棋子位置与箭头不动,把「改变路线」和「不改变起点」干净地分离开。Railbound、Cosmic Express 都是局部操作,没有直接对应物。这是全项目唯一值得押注的原创资产——而它现在有 568 关根本用不到。
+如果是我,会这样排
+本清单已按 §07 修订。排序原则:先把机器代答的那三处收回来,再给玩家看路的手段,然后才谈奖励与内容——顺序反了,印章会惩罚一个还没有零成本看路手段的玩家。
+ +三处自动提示改闲置门控,三套「看路」机制并成一套
+app.js:185 / 218 / 103–110。阈值从最后一个动画结束起算、随剩余棋子伸缩;toast 去掉数量;提示按钮改为触发闲置提示。当天可做,是全部方案的前提。
当天
按下看路、松手发车 + 尾迹 + 碰撞路线 + 全盘软化
+0 ms 预览,不是 250 ms 长按;预览画在棋子之上;离场留尾迹、被挡画到接触点。「非可达轨道降权」作废,改为全盘统一软化、只有按下的路线满色。
+1–2 天
连击(opened.length 驱动档位)+ 两枚只做加法的印章
+连击是反馈不是选择,但档位由「这一下打开了几枚」驱动,顺序第一次有可感知后果。印章不进关卡格;「一转到位」是旋转限额的软版本。依赖第 2 条先上线。
+3 天
主题绑章节 · 关名按章生成 · 教学改覆盖层 · 清理选关弹窗
+前两项约 15 行。教学覆盖层是 D1 的最大单一杠杆。选关弹窗删版本下拉、1–1000 输入框、审核页链接、免责说明;给已通关按钮实际样式。
+3 天
v0.6:分带配额重排、同底盘间隔、机制按章亮相、手作关加餐
+不原地重排(存档是索引数组)。第 1 章 20 关、第 2 关就有旋转;全可出关当喘气关每章 ≤15%;91 格只留 98 关观赏关作长局回报槽。曲线见 §07。
+1 周
里程目标原型:让旋转成为关于「看多长」的选择
+3,294 枚棋子换朝向能走更长;263 个全可出关旋转后有更长巡游。做 20 关原型验证「玩家会不会为了看更长的路而主动转」,验证过再进主线。
+验证后定
暂缓:25 套主题、Cocos / 微信移植、关卡量产、relay
+直到第 1–3 条上线并看到真人数据。relay 可留作第 8 章后的章节专属机制。下一批生成器要加 rotGain ≥ 2 候选带。
+