Files
huajishe-tts/docs/mahjong/02-verification-checklist.md
e2hang d02d718237 docs(mahjong): 依据实跑重核测试清单(v4)
- 头部测试概要改为准确值:麻将单测 78(tiles 16 / yaku 33 / game 29)、引擎 329、E2E ✅
- 新增「Q. 测试清单」:逐组列出 78 条用例 + 联机 E2E + 未纳入自动化项
- 修正过时项:O4 由「未做 4 端 E2E」→ ✅ E2E;移除已完成的后续优先项(仅剩 R-04 观战/AI)
2026-10-03 20:15:11 +08:00

20 KiB
Raw Permalink Blame History

立直麻将(Mahjong)实现验证清单

依据:docs/mahjong/00-rules.md(雀魂标准,四麻+三麻,半庄战)。 目的:供人工审核实现与规则的一致性。逐条列出期望行为 / 实现状态 / 证据 / 风险。 版本:v4(2026-10-03,P0+P1+P2 主要项修复完成;测试清单据实重核)。v1~v3 快照见 git 历史。 代码:packages/engine/src/games/mahjong*.ts、apps/web/src/MahjongBoard.tsx。

图例

级别(重要度):P0 核心(缺失/错误会导致对局不成立或算错分)|P1 重要|P2 次要/边角

实现状态:✅ 已实现且有单测 | 🟩 已实现(无专门单测/间接覆盖)| 🧪 已实现且浏览器冒烟实测 | ⚠️ 部分/简化/有边界问题 | ❌ 未实现

测试:麻将单测 78 条(mahjong-tiles.test.ts 16 · mahjong-yaku.test.ts 33 · mahjong.test.ts 29),引擎全量 329 通过(13 文件);联机 E2E e2e/mahjong-multiplayer.spec.ts(4 端)✅。逐条清单见文末 Q. 测试清单。


本轮(v2)修复总览:P0 + P1 全部完成

编号 级别 修复内容 状态
P-01 P0 立直后强制摸切(宣言回合可打任意牌,之后只能打摸到的牌) ✅ 单测
P-02 P0 鸣牌响应超时:新增 claimDeadline + claimTimeout move + UI 全局倒计时,掉线也不卡死 ✅ 单测 + 🧪 冒烟
P-03 P1 途中流局:四风连打 / 四家立直 / 三家和了 / 四杠散了 ✅ 单测
P-04 P1 流局满贯(弃牌全幺九且未被鸣 → 按满贯自摸,不收供托/本场) ✅ 单测
P-05 P1 和了终局/听牌终局(All Last 亲家一位且达返点 + 连庄 → 终局) ✅ 单测
P-06 P1 立直后暗杠听牌不变校验 ✅ 单测
P-07 P1 三麻抢北(对拔北开荣和窗口,按抢杠计) ✅ 单测
P-08 P1 里宝牌/杠宝 UI 揭示(和了后 uraIndicators 公开并在结算面板展示) ✅ 单测 + UI
P-09 P1 食替「筋」规则核对(现物 + 筋,含 123/789 边界) ✅ 已覆盖
P-10 P1 振听边界:被鸣走的自弃牌仍计入舍张振听(furitenKinds 只增不减) ✅ 单测

另修正 waitingKinds:扣除已锁定在副露中的牌(某牌种自占 4 张时不再作为听牌)。


v3(P2)修复总览

编号 级别 修复内容 状态
R-01 P2 三麻:碰/明杠后未打牌前不可拔北(justCalledBy) ✅ 单测
R-02 P2 加杠被抢后撤销为碰时,扣回第 4 张牌的赤宝牌计数 ✅ 单测
R-05 P2 终局顺位马 + 头名赏(四麻 +15/+5/-5/-15 且赏+20;三麻 +15/0/-15 且赏+15) ✅ 单测
R-06 P2 听牌 / 振听提示(playerView 暴露 myWaits/myFuriten,UI 显示) ✅ 单测 + UI
D9/N8 P2 立直棒可视化(顶栏按供托数画棍)+ 自己立直提示 ✅ UI
R-07 P1 联机 4 端 E2E(建房→开局→隐藏信息→轮转打牌) ✅ Playwright

本轮有意保持不变(合理简化,非遗漏):R-03 开牌/宝牌物理位置、C6 自摸牌显示、E15 杠宝时机、F14 人和(雀魂不采用)、O5 掉线超时(客户端驱动已够用)。 独立阶段(未做):R-04 观战 + AI 补位。


A. 牌具与牌组

编号 级别 检查项 期望 状态
A1 P0 四麻牌组 34 种 × 4 = 136 张 ✅
A2 P0 三麻牌组 去 2m~8m,108 张 ✅
A3 P0 四麻赤宝牌 红 5m/5p/5s 各 1(3 枚) ✅
A4 P0 三麻赤宝牌 红 5p/5s 各 1(2 枚) ✅
A5 P0 牌种索引与赤牌归一 0m→5m;red 单独保留 ✅
A6 P1 宝牌指示映射 数牌 9→1;风循环;三元循环 ✅
A7 P2 手牌排序 m→p→s→z,各 1..9 ✅

B. 牌山 / 王牌 / 指示牌 / 岭上 / 里宝

编号 级别 检查项 期望 状态
B1 P0 王牌 14 张不参与摸牌 ✅
B2 P0 开局表宝牌指示牌 开局翻 1 张 🧪
B3 P0 杠宝牌 每次杠翻新指示牌(≤5) 🟩
B4 P0 岭上牌 4 张,杠/拔北后补摸 🟩
B5 P1 里宝牌 立直和了按里指示加番;和了后 UI 揭示 ✅ 计分 + UI
B6 P0 牌山摸尽 荒牌流局 ✅
B7 P2 指示牌/岭上物理位置 与真实砌墙一致 ⚠️ 简化布局
B8 P1 杠宝翻开时机 抢杠则不翻 🟩

C. 配牌与摸打

编号 级别 检查项 期望 状态
C1 P0 发牌 每人 13 张,原子完成 ✅
C2 P0 庄家先摸 起手 14 张 ✅
C3 P0 摸打循环 打牌 → 下家摸牌 ✅
C4 P0 出牌合法性 只能打自己手牌 🟩
C5 P1 摸牌并入 摸到的牌进手牌 ✅
C6 P2 自摸牌提示 显示「摸到」 🧪(手牌行内亦有,UX 小事)

D. 立直

编号 级别 检查项 期望 状态
D1 P0 立直条件 门清 + 听牌 + ≥1000 🟩
D2 P0 立直供托 支付 1000 计供托 🟩
D3 P0 立直后摸切 立直后只能打刚摸的牌 ✅
D4 P0 一发 立直后一巡内和了且无鸣牌 🟩
D5 P1 双立直 第一巡立直(2 番) 🟩
D6 P1 立直后暗杠 仅在不改变听牌时可暗杠 ✅
D7 P1 四家立直流局 → 途中流局 ✅
D8 P1 里宝牌 立直和了加算里宝 ✅
D9 P2 立直棒可视化 桌面立直棒 ✅ 顶栏棍形显示 + 自己立直提示

E. 鸣牌

编号 级别 检查项 期望 状态
E1 P0 吃 仅下家(四麻) ✅🧪
E2 P0 碰 任意家 🟩
E3 P0 大明杠 3 手牌 + 弃牌 🟩
E4 P0 暗杠 手持 4 张 🟩
E5 P0 加杠 已碰 + 第 4 张,含抢杠窗口 🟩
E6 P0 三麻不可吃 无吃选项 🟩
E7 P0 食替禁止 现物 + 筋 ✅
E8 P0 鸣牌优先级 荣 > 碰/杠 > 吃 ✅
E9 P0 一炮多响(头跳) 最近者 ✅
E10 P1 三家和了 → 流局 ✅
E11 P1 四杠散了 → 流局 ✅
E12 P1 三麻拔北 亮北 +1 宝、岭上 🟩
E13 P1 抢北 对拔北荣和 ✅
E14 P2 碰后不可立即拔北 拔北近似鸣牌 ✅
E15 P2 明杠/加杠的宝牌与岭上顺序 符合规则 🟩

F. 和了判定

编号 级别 检查项 期望 状态
F1 P0 自摸 三家付点 ✅
F2 P0 荣和 放铳者付 🟩
F3 P0 无役不能和 番=0 不能和(宝牌不算役) ✅
F4 P0 宝牌不计入役 无役即使有宝牌也不能和 ✅
F5 P0 振听·舍张 含被鸣走的自弃牌 ✅
F6 P0 振听·同巡 本轮放过 → 本轮不能再荣和 🟩
F7 P0 振听·立直后 永久振听 🟩
F8 P1 一发和了 +1 番 🟩
F9 P0 岭上开花 杠/拔北后岭上自摸 🟩
F10 P0 抢杠 抢加杠荣和 🟩
F11 P0 海底摸月 牌山最后自摸 🟩
F12 P0 河底捞鱼 牌山最后弃牌荣和 🟩
F13 P0 天和 / 地和 役满 🟩
F14 P2 人和 古役 ❌ 有意不做
F15 P1 振听边界(被鸣走牌) 仍计入 ✅

G. 宝牌

编号 级别 检查项 期望 状态
G1 P0 表宝牌 每张 +1 番 ✅
G2 P0 赤宝牌 按实际持有(含副露) ✅
G3 P1 里宝牌 立直和了 + 里指示 ✅ 计分 + UI
G4 P0 杠宝牌 每杠翻新 🟩
G5 P1 拔北宝牌 每张拔出北 +1 🟩
G6 P0 宝牌不是役 — ✅

H. 役种(按番数)

✅ 有单测;🟩 已实现未单测;❌ 未实现。

番 役种 状态
1 番 立直 / 一发 / 门前清自摸和 / 平和 / 断幺九(有食断)/ 一杯口 / 役牌(含连风)/ 岭上 / 抢杠 / 海底 / 河底 ✅ 平和·断幺·一杯口·役牌·门前清自摸;🟩 立直·一发·岭上·抢杠·海底·河底
2 番 双立直 / 三色同顺 / 一气通贯 / 混全带 / 七对子 / 对对和 / 三暗刻 / 三色同刻 / 三杠子 / 混老头 / 小三元 ✅ 除双立直·三杠子为 🟩
3 番 二杯口 / 纯全带 / 混一色 ✅
6 番 清一色 ✅
役满 国士 / 四暗刻 / 大三元 / 小四喜 / 大四喜(双) / 字一色 / 绿一色 / 清老头 / 九莲 / 纯正九莲(双) / 四杠子 / 天和 / 地和 / 国士十三面(双) / 四暗刻单骑(双) / 累计役满 ✅ 国士·四暗刻·大三元·字一色·九莲·纯正·国士十三面·四暗刻单骑;🟩 其余
未实现 人和 / 古役(有意不做)/ 抢北已补 ❌ 仅人和·古役

I. 符计算

编号 级别 检查项 期望 状态
I1 P0 副底 20 符 ✅
I2 P0 门前清荣和 +10 ✅
I3 P0 自摸 +2 🟩
I4 P0 雀头 三元/自风/场风 +2,连风 +4 🟩
I5 P0 刻/杠符 分档(暗杠幺九 +32) ✅
I6 P0 听牌形 嵌张/单骑/边张 +2 🟩
I7 P0 平和自摸 20 符 ✅
I8 P0 七对子 25 符 🟩
I9 P0 最低符 不足 30 进位 🟩
I10 P0 进位 到 10 位 🟩

J. 点数计算

编号 级别 检查项 期望 状态
J1 P0 基本点 符 × 2^(2+番) ✅
J2 P0 满贯判定 5 番 / 4番40符 / 3番70符 ✅
J3 P0 跳满/倍满/三倍满/役满 6/8/11/13 档 ✅
J4 P0 切上满贯 不采用 ✅
J5 P0 亲/子荣和 ×6 / ×4 ✅
J6 P0 自摸分配 亲平摊;子庄2倍闲1倍 ✅
J7 P0 本场 300 / 200 ✅
J8 P0 供托 1000 归和了者 🟩
J9 P0 三麻自摸损 少收一名闲家 ✅
J10 P0 取整 向上到 100 ✅
J11 P0 点数守恒 无供托零和 ✅
J12 P1 击飞 < 0 终局(=0 继续) 🟩

K. 流局

编号 级别 检查项 期望 状态
K1 P0 荒牌流局 牌山摸尽 ✅
K2 P0 听牌罚符 3000 / 2000 按听牌家数 ✅
K3 P0 连庄 亲听牌连庄 ✅
K4 P1 九种九牌 首巡 ≥9 种幺九字牌可宣言 🟩
K5 P1 四风连打 → 流局 ✅
K6 P1 四家立直 → 流局 ✅
K7 P1 三家和了 → 流局 ✅
K8 P2 四杠散了 → 流局 ✅
K9 P1 流局满贯 弃牌全幺九未被鸣 → 满贯自摸 ✅

L. 连庄 / 进庄 / 终局 / 西入

编号 级别 检查项 期望 状态
L1 P0 和了连庄/下庄 亲和了连庄 🟩
L2 P0 听牌连庄 — ✅
L3 P0 场次推进 东南半庄 4 局一场风 🟩
L4 P1 和了终局/听牌终局 All Last 亲一位+达返点+连庄 → 终局 ✅
L5 P1 西入 南 4 无人达返点 → 西入 🟩
L6 P0 击飞终局 — 🟩
L7 P2 顺位马 / 头名赏 段位点结算 ✅ 终局计算 + 结算面板显示

M. 三麻特有

编号 级别 检查项 期望 状态
M1 P0 去牌 / 无吃 — ✅
M2 P0 起点/返点 35000 / 40000 🟩
M3 P0 自摸损 — ✅
M4 P1 拔北 — 🟩
M5 P1 拔北后岭上 算岭上开花 🟩
M6 P1 北作面子=客风 非役 🟩
M7 P1 无四风连打/四家立直 自动不适用 🟩
M8 P2 抢北 对拔北荣和 ✅

N. UI / 交互

编号 级别 检查项 期望 状态
N1 P0 牌桌渲染 手牌/牌河/副露/宝牌/剩余/点数/场况 🧪
N2 P0 出牌 点击手牌、轮转 🧪
N3 P0 鸣牌窗口 荣/碰/杠/吃/跳过 + 倒计时 🧪
N4 P1 立直操作 宣言 + 选牌打出 🟩
N5 P1 操作按钮 自摸/暗杠/加杠/拔北/九种九牌 🟩
N6 P1 结算面板 役种/番符/点数/顺位/下一局 🟩
N7 P2 里宝牌展示 和了时揭开 ✅
N8 P2 立直棒/自己立直提示 — ✅ 棍形供托 + 自己立直提示
N9 P2 听牌/振听提示 — ✅
N10 P1 鸣牌超时对“非本人”推进 掉线也能推进 ✅🧪(任一客户端在线即可驱动)

O. 联机 / 隐藏信息

编号 级别 检查项 期望 状态
O1 P0 playerView 隐藏 他家手牌/牌山/里宝隐藏 🟩
O2 P0 公开信息 handCounts/wallCount/doraIndicators 🧪
O3 P1 本地换座位不重置 updatePlayerID 原地切换 🧪
O4 P1 联机 4 端对局 SocketIO 正常同步 ✅ E2E(mahjong-multiplayer.spec.ts)
O5 P2 掉线玩家鸣牌超时 服务端推进 🟩(客户端驱动;全部离线则仍会停)

P. 已修复与剩余风险

已修复(P0/P1/P2 主要项):

  • P0/P1:P-01 摸切、P-02 鸣牌超时、P-03 途中流局×4、P-04 流局满贯、P-05 和了终局、P-06 立直后暗杠校验、P-07 抢北、P-08 里宝/杠宝展示、P-09 食替筋、P-10 振听边界。
  • P2:R-01 碰后不可立即拔北、R-02 抢杠撤销红牌计数、R-05 顺位马/头名赏、R-06 听牌/振听提示、D9/N8 立直棒可视化、R-07 联机 4 端 E2E。

仍存在(均为“有意简化”或“独立阶段”,非缺陷):

编号 级别 项目 说明
R-03 P2 掷骰/开牌位置、宝牌物理位置 采用简化布局,不影响玩法
R-04 — 观战 + AI 补位 独立阶段(已确认 AI 后续再做)
S-01 P2 自摸牌在“摸到”与手牌行各显示一次 纯 UX,可后续去重
S-02 P2 杠宝翻开时机为“杠成立即翻” 与真实时序略有差异,无算分影响
S-03 P2 人和(古役)不实现 雀魂不采用(有意)
S-04 P2 鸣牌超时由客户端驱动 任一客户端在线即可推进;全部离线则停

本轮浏览器冒烟实测(v2,均已通过、无 JS 异常)

  • 开局(修复后正常)→ 打牌 → 轮转
  • 切换座位不重置
  • 鸣牌窗口出现;故意不响应,等待 >12s 后自动跳过并推进(验证 P0-02 / N10)
  • 连续多回合打牌正常(牌河累加、宝牌指示显示、点数显示)
  • 注入 window.onerror 捕获:全程 []

建议的后续优先项

  1. R-04 观战 + AI 补位(唯一剩余的大阶段,已确认 AI 后续再做)。
  2. 观战/补位完成后再补「观战者不可见手牌」的 E2E。

Q. 测试清单(Test Inventory)

数据来源:packages/engine 实跑(2026-10-03)。命令:pnpm --filter @tts-like/engine test。 引擎全量 329 / 13 文件全通过;下表为麻将相关的 78 条逐组明细。

Q1. mahjong-tiles.test.ts — 16 条(牌具/牌组)

# 分组 用例
1-3 四麻牌组 136 张 id 唯一 · 每种牌种恰好 4 张 · 红 5m/5p/5s 各 1
4-5 三麻牌组 108 张且不含 2m~8m · 红 5p/5s 各 1(无红 5m)
6-8 索引↔牌种 34 种索引 0..33 一一对应 · 赤牌归一到普通 5 · 幺九/字牌判定
9-12 宝牌指示映射 数牌 9→1 循环 · 赤牌按普通 5 计算 · 风牌 東→南→西→北→東 · 三元牌 白→發→中→白
13-15 排序与展示 sortTiles 按 m→p→s→z 且各花色 1..9 · tileLabel 中文/数字 · 计数↔牌种互转
16 洗牌 注入固定随机源可复现,且不改原数组

Q2. mahjong-yaku.test.ts — 33 条(役种/符/点/听牌)

# 分组 用例
1-2 和牌型分解 标准型分解数量正确 · 七对子/国士判定
3-15 普通役种(13) 平和 · 平和+断幺九 · 一杯口 · 二杯口 · 役牌(三元)与副露 · 混一色/清一色 · 混全带/纯全带 · 无役→null · 三色同顺/一气通贯/三色同刻 · 对对和+三暗刻 · 混老头/小三元 · 立直/一发/门清自摸/平和 · 海底/河底/岭上/抢杠 旗标
16 符细节 幺九暗杠 +32 符、门清荣和 70 符
17-22 役满(6) 国士无双 · 国士十三面(双倍) · 大三元 · 四暗刻/四暗刻单骑(双倍) · 字一色 · 九莲宝灯/纯正九莲(双倍)
23 宝牌计数 指示 3m→宝牌 4m;赤牌归一
24-28 符与点数(5) 点数基本表(子/亲×荣/自摸) · 满贯封顶且不采用切上满贯 · 役满/双倍役满 · 本场加算(+300/本场) · 三麻自摸损
29-33 听牌/振听/食替 单骑听 · 两面听 · 不成和牌型→false · 食替(碰现物 / 吃现物+筋 / 嵌张) · 舍张振听

Q3. mahjong.test.ts — 29 条(对局流程 / P0-P1-P2)

# 分组 用例
1-2 配牌与开局 四麻:庄 14/闲 13/牌山 69/赤初始化 · 三麻:108 张/每家 35000/无 2m~8m
3 打牌推进 无人鸣牌时推进下家并摸牌
4-6 鸣牌窗口 buildClaims 检出碰/杠/吃/荣 · 双响头跳取最近 · 荣和优先于碰
7-10 和了计算 平和+门清自摸点数 · 无役不能和 · 赤宝牌按实际持有重算 · applyWin 点数守恒
11-12 荒牌流局 听牌罚符 3000 与庄家轮转 · 庄家听牌则连庄
13 整手模拟(冒烟) 随机打至荒牌流局:不抛异常、点数守恒、可开下一局
14 P0-01 立直后不能打非摸到的牌,只能摸切
15 P0-02 鸣牌超时:未到点不生效;到点自动跳过并推进
16-18 P1-03 四家立直/四风连打 · 三家和了→流局 · 四杠散了判定
19 P1-04 流局满贯:弃牌全幺九未被鸣→满贯自摸,不收供托
20 P1-05 All Last 亲家一位、达返点且连庄→终局
21 P1-06 立直后暗杠:改变听牌被拒 / 不变则允许
22 P1-07 三麻抢北:对拔北开启荣和窗口
23 P1-08 里宝牌:和了前隐藏,和了后公开 uraIndicators
24 P1-10 被鸣走的自弃牌仍计入舍张振听
25 R-01 碰后 justCalledBy 期间拔北被拒,打牌后恢复
26 R-02 加杠被抢撤销为碰并扣回赤宝牌计数
27-28 R-05 四麻ウマ +15/+5/−5/−15 且头名赏 +20 · 三麻 +15/0/−15 且 +15
29 R-06 playerView 暴露 myWaits / myFuriten

Q4. 联机 E2E(Playwright)

用例 覆盖
e2e/mahjong-multiplayer.spec.ts 4 客户端注册/建房/加入/开局 · 各自仅见己方手牌 · 对手座位无真实牌泄漏 · 连续轮转打牌 · 牌河累积

Q5. 未纳入自动化(人工/简化,非缺陷)

项 说明
三麻自摸损全表 仅抽样(满贯)验证,未穷举
浏览器冒烟(人工) 开局/打牌/换座/鸣牌/超时自动跳过,见上节记录
R-03 / S-01~S-04 有意简化项,见 P 节
R-04 观战/AI 未实现,无需测试