Files
huajishe-tts/docs/dev-log/15-mahjong-game.md
e2hang 8d5fb3c511 feat(mahjong): 立直麻将(四麻/三麻,半庄战,雀魂规则)
- engine/tiles: 34 种牌、四麻 136/三麻 108(去 2m~8m)、赤 5(3/2 枚)、宝牌指示映射、排序与计数
- engine/yaku: 和牌分解(标准/七对/国士)+ 全役种(含双倍役满、累计役满)+ 符计算 + 番符→点数(满贯封顶、不采用切上满贯、三麻自摸损)+ 听牌/振听/食替
- engine/game: boardgame.io Game(阶段机 + 摸打/立直/鸣牌/和了/流局 moves + 鸣牌窗口“荣>碰/杠>吃/头跳” + playerView 隐藏 + 连庄/进庄/击飞/西入/半庄终局)
- ui: 新增 MahjongBoard(立式麻将桌:手牌/牌河/副露/宝牌/剩余张数/点数/鸣牌倒计时)
- 注册: server 两数组 + App.tsx GAMES/GAME_ORDER/GameKey/本地+联机 View(maxSeats=4, seatChoices=[3,4])
- docs: docs/mahjong/{00-rules,01-implementation-plan}.md + dev-log 15;CODEBUDDY.md 游戏清单更新
- 测试: mahjong-tiles/yaku/game 共 59 条;engine 全量 310 通过;web tsc+vite build、server tsc 通过
2026-10-03 12:43:51 +08:00

3.3 KiB
Raw Permalink Blame History

15 · 立直麻将(Mahjong)游戏

做什么(用户要求)→ 遇到什么问题 → 怎么解决

做什么

在 tts-like 中加入日本立直麻将,规则采用雀魂标准,覆盖四麻 + 三麻、半庄战、纯真人(AI 后续)。 规则文档先行:docs/mahjong/00-rules.md(玩法依据 + 选型)、docs/mahjong/01-implementation-plan.md(工程拆分)。 UI 参照桥牌 BridgeBoard.tsx 的思路二次设计为立式麻将桌。

实现结构

  • packages/engine/src/games/mahjong-tiles.ts — 牌具:34 种牌、四麻 136/三麻 108 去 2m~8m、赤 5(四麻 3 / 三麻 2)、宝牌指示映射、排序与计数。测试 mahjong-tiles.test.ts。
  • packages/engine/src/games/mahjong-yaku.ts — 和牌分解(标准型/七对/国士)、全役种(含双倍役满、累计役满)、符计算、点数(含满贯封顶、不采用切上满贯、亲/子/自摸/荣和、本场、三麻自摸损)、听牌/振听/食替辅助。测试 mahjong-yaku.test.ts。
  • packages/engine/src/games/mahjong.ts — boardgame.io Game:阶段机 waiting→playing→roundEnd→gameEnd,moves(startGame/declareRiichi/discard/tsumo/respond/ankan/kakan/nuki/kyushu9/nextRound/setName),鸣牌窗口(荣>碰/杠>吃、头跳),playerView 剥离手牌/牌山/里宝。测试 mahjong.test.ts。
  • apps/web/src/MahjongBoard.tsx — 麻将桌 UI(手牌/牌河/副露/宝牌指示/剩余牌数/点数/操作按钮 + 鸣牌响应倒计时)。
  • 注册:packages/server/src/index.ts 两数组 + apps/web/src/App.tsx(GAMES/GAME_ORDER/GameKey/本地+联机 View,maxSeats=4、seatChoices=[3,4])。

遇到的问题

  1. 赤宝牌表达:牌型判定用索引(0..33),索引会丢赤牌信息。→ 赤 5 用 '0m'/'0p'/'0s',判定时 normalizeKind 归一到 5m/5p/5s;赤牌数单独按 Tile.red + Meld.redCount 统计。
  2. 隐藏信息与剩余牌数:playerView 把 wall 置空后 UI 无法显示“剩余张数”。→ 额外注入公开字段 wallCount、doraIndicators、handCounts。
  3. boardgame.io 单线程下的鸣牌并发:真实麻将是并发申请+优先级比较。→ 用“响应窗口 + 全部响应后按 荣>碰/杠>吃 结算”建模,UI 端 8 秒倒计时自动跳过来避免卡死。
  4. 立直支付时机:先宣言后打牌,若在宣言与打牌之间自摸会漏付供托。→ 把 1000 点支付与 riichi=true 提前到 declareRiichi,discard 只负责进入一发。

结果

  • pnpm --filter @tts-like/engine test:310 通过(新增麻将 59 条)。
  • apps/web 的 tsc --noEmit && vite build 通过;server 类型检查通过。
  • 已知待补(mahjong.ts 内有 TODO):四风连打/四家立直/三家和了/四杠散了等途中流局、流局满贯、立直后暗杠的听牌不变校验、和了终局/听牌终局的精确 All Last 判定、精确一发计时。九种九牌、荒牌流局听牌罚符、连庄/进庄、击飞、西入已实现。

与 §14 对接要点的落地

规则文档 §14 列的三处注册与 playerView 约束均已按约定实现;发牌为 freshRoundState 内原子完成(避免多客户端抢发同一 move 造成状态错乱)。