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

35 lines
3.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 造成状态错乱)。