14 KiB
成都麻将功能整理
本文基于当前项目 mahjong-server 的实际代码实现整理,目的是给前端或测试同学做逐项对照测试。这里写的是“当前代码已经实现/暴露出来的功能”,不是传统成都麻将的完整规则说明。
1. 当前玩法范围
1.1 当前后端只支持成都麻将
- 创建房间时如果不传
game_type,默认就是chengdu - 如果传入其他玩法,服务端直接返回错误:
only chengdu mahjong is supported currently - 虽然引擎里预留了
xueliu、hongzhong的规则工厂,但房间服务层目前只允许创建chengdu
1.2 当前牌集范围
- 只使用三门牌:万(
W)、条(T)、筒(B) - 每门
1-9各 4 张,共108张 - 当前不带字牌、风牌、箭牌、花牌
- 成都玩法配置里明确
HasHongZhong() == false
2. 房间功能
2.1 创建房间
HTTP 接口:
POST /api/v1/game/mahjong/room/create
请求体:
{
"name": "房间名",
"game_type": "chengdu",
"max_players": 4
}
实现规则:
- 必须登录鉴权
- 创建者自动成为房主
- 创建者自动进入房间,座位索引固定为
0 max_players为空时,使用配置默认值- 当前只允许
4人房 - 房间名为空时,自动生成类似
成都麻将-xxxxxxxx - 初始房间状态为
waiting
返回重点字段:
room_idnamegame_typeowner_idmax_playersplayer_countplayersstatus
2.2 房间列表
HTTP 接口:
GET /api/v1/game/mahjong/room/list
查询参数:
statusgame_typepagesize
实现规则:
- 需要登录
- 支持按房间状态筛选
- 支持按玩法类型筛选
- 支持分页
size超过配置上限会被裁剪
2.3 加入房间
HTTP 接口:
POST /api/v1/game/mahjong/room/join
请求体:
{
"room_id": "xxx"
}
实现规则:
- 需要登录
- 只能加入
waiting状态房间 - 人数满 4 人后不可继续加入
- 已在房间内再次加入,不会重复加人
- 如果玩家名之前为空,再次进入时会补写玩家名
加入房间成功后,除了 HTTP 返回房间摘要,还会额外推送 WS 事件:
room_joinedroom_member_joined(仅首次加入触发)room_player_update
2.4 查询房间详情
HTTP 接口:
GET /api/v1/game/mahjong/room/:room_id
实现规则:
- 需要登录
- 必须是房间内玩家才能看该房间详情
- 返回
summary + public
其中 public 可用于前端牌桌展示,包含:
- 当前阶段
phase - 当前轮到谁
current_turn_player - 是否必须先摸牌
need_draw - 最近弃牌人
last_discard_by - 最近弃牌
last_discard_tile - 牌墙剩余数
wall_count - 本局胡牌玩家
winners - 本局分数
scores - 当前响应窗口
pending_claim - 每个玩家的公开状态
2.5 离开房间
离开房间是走 WS 动作,不是 HTTP 接口。
实现规则:
- 只有
waiting状态下允许离开 playing状态下离开会失败- 普通玩家离开后,剩余玩家座位索引会重新整理
- 房主离开后,房主身份自动转移给当前列表中的第一个玩家
- 如果最后一个玩家离开,房间会直接删除
对应事件:
room_leftroom_member_leftroom_owner_changed(房主变更时)room_player_updateroom_closed(最后一人离开时)
3. 开局和基础对局流程
3.1 开始游戏
开始游戏走 WS 动作:
type = "start_game"
实现规则:
- 只有房主能开始
- 只有满 4 人时才能开始
- 房间状态从
waiting变成playing - 内部固定按当前房间玩家顺序创建 4 个玩家
- 庄家固定为座位
0 - 发牌后庄家 14 张,其余玩家 13 张
开始后会推送:
- 广播
room_state - 给每个玩家单独推送
my_hand
3.2 基础轮转规则
- 庄家开局先打牌,不需要先摸
- 非当前操作者不能操作
- 非庄家进入自己回合后,必须先
draw才能discard - 每次打牌后,系统检查其他三家是否可
hu / peng / gang - 如果没人可响应,则轮到下家摸牌
- 如果所有可响应玩家都
pass,也轮到出牌者下家摸牌
3.3 牌墙耗尽
- 当需要摸牌但牌墙为空时,直接流局结束
- 打完牌后如果后续无人响应,且牌墙为空,也直接结束
- 摸到最后一张牌后,如果牌墙已空且无人可胡,也会结束
- 结束后阶段
phase = over - 房间状态同步变为
finished
4. 对局动作功能
当前对局动作都通过 WS -> ws-gateway -> grpc -> mahjong-game 转发执行。
支持的动作类型:
drawdiscardpengganghupass
4.1 摸牌 draw
触发条件:
- 必须轮到当前玩家
- 当前状态必须
need_draw = true
效果:
- 从牌墙头部摸 1 张
- 手牌加 1
need_draw = false- 记录最近摸牌玩家
- 如果是杠后补牌,会记录
LastDrawFromGang = true - 如果摸到的是最后一张,会记录
LastDrawIsLastTile = true
4.2 出牌 discard
触发条件:
- 必须轮到当前玩家
- 当前不能处于“必须先摸牌”的状态
- 必须指定要打出的牌,并且该牌确实在玩家手里
效果:
- 从手牌移除该张牌
- 追加到玩家弃牌区
out_tiles - 记录
last_discard_by - 记录
last_discard_tile - 然后生成一个
pending_claim响应窗口,给其他玩家判断能否hu / peng / gang
4.3 碰牌 peng
触发条件:
- 当前必须存在
pending_claim - 该玩家必须在响应窗口中,并且
CanPeng = true - 玩家手里要有与目标弃牌同牌面的 2 张牌
效果:
- 手牌移除 2 张同牌
- 明牌区新增一组 3 张碰牌
- 当前回合转给碰牌玩家
- 碰牌后不需要先摸,直接由碰牌玩家出牌
4.4 杠牌 gang
当前只实现两种杠:
- 明杠:别人打出的牌,你手里正好有 3 张相同牌
- 暗杠:当前轮到自己时,手里本来就有 4 张相同牌
实现效果:
- 组成 4 张杠牌放入明牌区
- 杠后立即补 1 张牌
- 杠后补牌来自牌墙头部
- 补牌后当前回合仍归杠牌玩家
当前未见实现:
- 碰后补杠
- 抢杠流程本身
说明:
- 代码里有
QiangGangHu计分位,但当前动作流程没有真正把“抢杠胡窗口”建出来
4.5 胡牌 hu
支持两类:
- 自摸胡:当前轮到自己时胡自己的手牌
- 点炮胡:在
pending_claim窗口里胡别人刚打出的牌
共同前提:
- 必须满足成都规则的胡牌校验
- 必须满足“缺一门”
胡牌后效果:
- 玩家
HasHu = true winners增加该玩家- 按规则计算本次番数并累计到
scores - 成都玩法当前不是血流玩法,所以有人胡后本局直接结束
phase = over- 房间状态会被服务层更新为
finished
4.6 过牌 pass
触发条件:
- 当前必须有
pending_claim - 该玩家必须在当前响应窗口里
效果:
- 从当前
pending_claim.options删除该玩家 - 如果还有其他玩家待响应,则继续等待
- 如果所有玩家都过,则轮到弃牌者下家摸牌
5. 成都麻将胡牌判定规则
5.1 缺一门
当前代码里,“缺一门”的实现方式是:
- 手牌 + 副露中最多只能出现两种花色
- 如果出现万、条、筒三门同时存在,则不能胡
注意:
- 这里只做了“结果校验”
- 没有单独实现“开局定缺”流程
- 也没有前端/协议层的“选缺门”动作
这意味着当前代码更接近“胡牌时校验是否缺一门”,而不是完整的成都定缺流程。
5.2 支持的胡型
当前规则代码支持以下胡型:
- 平胡:
1番 - 对对胡:
3番 - 七对:
8番 - 龙七对:
16番 - 清一色:
8番 - 清对:
16番 - 清七对:
32番 - 清龙七对:
64番
5.3 支持的附加番
- 杠上开花:
+2番 - 海底捞月:
+2番 - 抢杠胡:
+2番
5.4 胡牌组合方式
代码可识别:
- 标准胡:
4组面子 +1对将 - 七对
- 龙七对
说明:
- 对对胡通过“全部由刻子/杠 + 1 对将”识别
- 清一色通过“所有牌同一花色”识别
6. 分数处理方式
当前代码的分数逻辑比较简单,适合先做基础功能联调,不等同于完整成都麻将结算。
已实现:
- 每次胡牌时,按番型计算一个整数分数
- 这个整数直接累计到
state.scores[winnerID] - 例如平胡就加
1,清龙七对就加64
当前未实现或未体现:
- 输家扣分分摊
- 自摸三家付、点炮单家付
- 杠分结算
- 查花猪
- 查大叫
- 退税
- 荒庄查叫
- 局数/圈风/连庄
- 最终总结算
因此,当前 scores 更准确说是“本局胡牌番数累计”,不是正式货币化结算结果。
7. 前端可收到的核心事件
7.1 房间相关事件
room_joinedroom_member_joinedroom_player_updateroom_leftroom_member_leftroom_owner_changedroom_closed
7.2 对局相关事件
room_statemy_hand
7.3 room_state 关键字段
建议重点校验:
room_idphasestatuscurrent_turn_playerneed_drawlast_discard_bylast_discard_tilewall_countwinnersscorespending_claimplayers
其中每个公开玩家对象包含:
player_idhand_countmeldsout_tileshas_hu
7.4 my_hand 关键字段
room_idhand
说明:
my_hand是定向事件,只发给对应玩家- 前端应该只用它渲染自己的真实手牌
- 其他玩家只看
hand_count
8. 建议的 WebSocket 动作格式
从网关协议看,客户端 WS 消息结构大致如下:
{
"type": "start_game",
"roomId": "room-xxx",
"requestId": "req-001",
"trace_id": "trace-001",
"payload": {}
}
通用字段:
typeroomIdrequestIdtrace_idpayload
房间相关动作:
join_roomleave_roomstart_game
对局动作:
drawdiscardpengganghupass
9. 当前代码中的已知限制/缺口
这一段非常重要,测试时请单独对照。
9.1 没有“定缺”流程
- 成都麻将通常有“定缺”
- 当前代码没有开局选缺门动作
- 只有胡牌时做“最多两门花色”的校验
9.2 没有“换三张”
- 项目内未看到换三张流程
- 没有对应状态、动作、事件
9.3 不是血战到底,也不是血流成河
- 成都规则实现里
IsBloodFlow() == false - 一旦有人胡牌,本局直接结束
- 不支持“一炮多响后继续”或“胡后继续打”
9.4 结算远未完整
- 当前只给赢家加一个番数值
- 没有完整输赢结算模型
- 没有杠分、查叫、花猪、退税等成都特色结算
9.5 discard 动作当前存在接线缺口
这一点从代码看是高风险问题:
- 引擎层
discard必须拿到具体tile - 但服务层
toEngineAction(msg)当前只设置了Type和PlayerID - 没有把
msg.Payload里的牌对象解析到types.Action.Tile
这意味着:
- 如果前端通过现有 WS 通道发送
discard,很可能会因为缺少牌对象而失败 - 报错大概率是
discard tile is required
建议测试时优先验证这个点。
9.6 抢杠胡只有计分标志,没有完整流程
- 规则层支持
QiangGangHu加番 - 但当前核心流程没有看到“补杠被抢”的动作分支和响应窗口
- 所以这个番目前更像预留能力,不一定能在真实流程里触发
9.7 优先级仲裁较简化
项目内 engine/README.md 还明确写了后续建议:
- 需要补充更明确的优先级处理
hu > gang > peng
当前代码行为是:
- 打牌后把所有可操作选项放进
pending_claim - 谁先发起动作、且在 options 中合法,谁就能执行
因此如果多个玩家同时都可响应,前端和测试要特别注意是否存在“先到先得”而不是严格优先级裁决。
10. 建议测试清单
10.1 房间层
- 创建房间成功,默认玩法为
chengdu - 非
chengdu玩法创建失败 - 非 4 人房创建失败
- 房主自动入房
- 房间列表分页正常
- 房间未满时可加入
- 房间满员后加入失败
- 对局开始后不能再加入
- 房间内玩家可查看详情
- 非房间内玩家查询详情失败
- 普通玩家可离开等待中的房间
- 房主离开后房主转移
- 最后一人离开后房间关闭
- 游戏进行中离开房间失败
10.2 开局与轮转
- 房主才能开始游戏
- 必须满 4 人才能开始
- 开局庄家 14 张,其余 13 张
- 开始后收到
room_state - 每位玩家都能收到自己的
my_hand - 庄家首回合直接出牌
- 非庄家必须先摸再打
- 轮转顺序按座位顺延
10.3 响应动作
- 弃牌后正确生成
pending_claim - 可碰玩家能执行
peng - 碰后当前回合归碰牌玩家
- 手牌与副露数量变化正确
- 明杠成功后会补牌
- 暗杠成功后会补牌
- 所有可响应玩家都
pass后轮到下家摸牌
10.4 胡牌与流局
- 平胡可胡且加
1分 - 对对胡可胡且加
3分 - 七对可胡且加
8分 - 龙七对可胡且加
16分 - 清一色可胡且加
8分 - 清对可胡且加
16分 - 清七对可胡且加
32分 - 清龙七对可胡且加
64分 - 杠上开花附加
2分 - 海底捞月附加
2分 - 三门齐全时不能胡
- 点炮胡后本局直接结束
- 自摸后本局直接结束
- 牌墙为空时本局流局结束
10.5 高风险专项
- WS
discard是否因未传/未解析tile失败 - 多家同时可响应时,是否符合预期优先级
QiangGangHu是否实际上无法走通
11. 结论
当前项目里的“成都麻将”更准确地说,是一个:
- 已具备房间管理
- 已具备 4 人基础发牌和回合流转
- 已具备碰/杠/胡/过等核心动作
- 已具备部分成都胡型与番数计算
- 但尚未实现完整成都特色流程与结算
如果你是拿它和“标准成都麻将产品需求”对照,当前更像“成都麻将基础可玩内核 + 房间联机骨架”,还不是完整商用品规。