- 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 通过
52 lines
3.6 KiB
Markdown
52 lines
3.6 KiB
Markdown
# 立直麻将(Mahjong)实现计划
|
||
|
||
> 规则依据:[`00-rules.md`](./00-rules.md)(雀魂标准,四麻 + 三麻,半庄战)。
|
||
> 目标文件与集成点见 00-rules §14。本文只描述**工程拆分与实施顺序**。
|
||
|
||
---
|
||
|
||
## 一、模块拆分(均放 `packages/engine/src/games/`)
|
||
|
||
| 文件 | 职责 | 是否纯函数 |
|
||
| --- | --- | --- |
|
||
| `mahjong-tiles.ts` | 牌定义、牌组生成(四麻/三麻)、赤宝牌、排序、宝牌指示映射、索引/牌种互转 | ✅ |
|
||
| `mahjong-yaku.ts` | 和牌型分解(标准型/七对/国士)、役种判定、番数、符计算、点数计算、振听/听牌判定辅助 | ✅ |
|
||
| `mahjong.ts` | boardgame.io `Game` 定义:阶段机、moves、playerView、本局/终局结算、连庄/进庄 | ❌(含状态机) |
|
||
|
||
对应测试:`mahjong-tiles.test.ts`、`mahjong-yaku.test.ts`、`mahjong.test.ts`。
|
||
|
||
## 二、牌与状态数据模型
|
||
|
||
- **牌种索引**(34 种):`0-8 = 1m..9m`,`9-17 = 1p..9p`,`18-26 = 1s..9s`,`27-33 = 東南西北白發中`。
|
||
- **物理牌**:`{ id: string; kind: string; red?: boolean }`,其中 `kind` 用 `'1m'..'9m'/'1p'../'1s'../'1z'..'7z'`;赤 5 用 `'0m'/'0p'/'0s'` 表示,`normalizeKind` 归一到 `'5m'/'5p'/'5s'` 参与牌型判定。
|
||
- **G 关键字段**:`players/hands/deck/wall/deadWall`、`melds[pid]`、`discards[pid]`、`scores[pid]`、`riichi[pid]`、`doraIndicators`、`honba`、`kyoutaku`(供托)、`bakaze`(场风)、`dealer`、`round`、`phase` 相关标记、`pendingClaims`(鸣牌/荣和响应)、`turn`、`lastDiscard`、`furiten` 标记等。
|
||
|
||
## 三、阶段机与主循环
|
||
|
||
```
|
||
waiting ──startGame──▶ playing ──(和了/流局)──▶ roundEnd ──nextRound──▶ playing ...
|
||
└──(半庄终局)──▶ gameEnd
|
||
```
|
||
|
||
- `playing` 内部用 `turnStep` 区分:`draw`(摸牌)→ `discard`(打牌)→ `claim`(鸣牌响应窗口)。
|
||
- 鸣牌响应:逆时针依次询问有资格者(吃/碰/杠/荣/拔北),`pass` 或超时继续;优先级 荣和 > 碰/杠 > 吃。
|
||
- **发牌必须一次性原子完成**(踩坑记忆:单客户端 flood move 会破坏其他客户端同步)。
|
||
|
||
## 四、实施顺序(每步附验证)
|
||
|
||
1. **P1 牌与宝牌**:`mahjong-tiles.ts` + 测试(牌组张数、赤 5、宝牌指示循环、三麻去牌)。
|
||
2. **P2 和牌与役**:`mahjong-yaku.ts` + 测试(和牌分解、全部役种表、双倍役满、宝牌加算)。
|
||
3. **P3 符与点数**:符计算 + 点数表 + 测试(对拍 §9.3 表、满贯封顶、切上满贯不采用、亲/子/自摸/荣和、本场)。
|
||
4. **P4 听牌/振听/食替**:听牌判定、舍张/同巡/立直后振听、现物+筋食替校验 + 测试。
|
||
5. **P5 Game 骨架**:阶段机 + 配牌/摸打/立直/鸣牌/和了/流局 moves + playerView + 本局/终局结算 + 测试(用 boardgame.io 的 `Client`/`Game.T` 驱动)。
|
||
6. **P6 UI**:`apps/web/src/MahjongBoard.tsx`(参照 `BridgeBoard.tsx` 重设计为立式麻将桌)。
|
||
7. **P7 接入**:注册 server 两数组 + `App.tsx`(GAMES/GAME_ORDER/GameKey/Local/Online View)。
|
||
8. **P8 验证**:`pnpm -r ts && pnpm --filter @tts-like/engine test`,必要时补 E2E。
|
||
|
||
## 五、风险与注意
|
||
|
||
- 役种与符计算是最大复杂度来源:**先写穷尽单测对拍标准表**,再写 Game 逻辑。
|
||
- 三麻自摸损、拔北、北客风等需在点数与役判定中分支处理。
|
||
- 鸣牌并发在 boardgame.io 下单线程,需用状态窗口 + 超时模拟;避免多客户端抢发同一 move。
|
||
- 隐藏信息严格走 `playerView`,UI 不得二次实现。
|