九江麻将的算法,就三道题:能不能胡、胡了算几番、算完怎么结。听着简单,真落到代码里,这三件事互相咬着——改胡牌判定可能带偏听牌列表,改番型配置又会影响结算对账。
按开发的实际顺序整理一份清单,能直接对着用。
胡牌判定:先把牌面压成计数数组
前端画的可能是万条筒加字牌的花哨图案,服务端只该认一套编码。
常见做法是用 34 位计数数组(万条筒各 9 张,字牌 7 种),索引即牌型 ID,值是该牌的剩余张数。这么做的好处是判重、判听、判碰杠全在同一套结构上跑,不用来回转格式。
判定主体是递归拆牌:先取一对将牌,再从剩下的牌里依次抠刻子和顺子,全部抠空就是胡。字牌不能参与顺子,这个分支一定要单独拎出来写,不然字牌凑顺子的脏数据会混进结果。
带癞子(混牌)的牌局麻烦一层。通行的思路是把癞子先抽出来当通配符,用剩余实牌去匹配缺张,匹配成功再回填。
有些地方的规则限制癞子不能做将、不能单独成刻,这些限制别硬编进判定函数,做成参数传进去更稳妥——同一个引擎往往要同时跑几种本地玩法。
特殊牌型与听牌枚举
常规胡之外,还得给特殊牌型留口子:七小对(含豪华七对)、十三烂、全求人、清一色平胡等。
这些牌型用同一套计数数组就能判,但最好独立成函数,别塞进递归主流程,否则一旦改动,普通胡的回归测试要重跑一遍。
听牌枚举比较直白:34 种牌逐个试探加一张,命中即写入听牌表。
坑在癞子上——枚举时必须把“手里已有几张癞子”作为状态一起传进去,只算实牌会漏掉不少听口,玩家一眼就能看出来。
番型计算:配置表比 if-else 好维护
番型这块最容易写成一大坨条件分支,后期加一条规则就得翻半天。比较省事的做法是配置化,每条记录至少包含:名称、判定条件、番数或倍数、是否可叠加、是否计入封顶。
- 平胡 1 番,自摸另加 1 番
- 门清、碰碰胡、七小对各按本地习惯定番
- 清一色通常单独计,与碰碰胡是否复合要看牌桌约定
- 杠上开花、抢杠胡、海底捞月属于事件型加番,与牌型番分开记
- 天胡、地胡多数牌局会设封顶
叠加规则是分歧最大的地方。清一色加碰碰胡,有的地方番数相加再乘底分,有的地方直接取高不叠加,还有的设了总番上限。这点没法一刀切,做成可配置项,运营调参数比改代码便宜得多。
另外别漏了胡牌优先级:一炮多响还是一炮单响、多家同时能胡时按什么顺序截胡,结算前要先走一遍仲裁,否则同一局会算出两套账。
结算与几个容易翻车的细节
算分公式本身不复杂,底分乘番数再乘人数,杠分往往单独即时结算、不参与最终胡牌结算,这两笔账要分开记,别混在一个流水里。
工程侧还有几件事值得提前定:洗牌用可追溯的随机种子并落库,牌局能回放复现,客诉才有得查;杠后补牌、牌墙摸完荒庄、癞子回填后的牌型合法性交叉校验,都属于必须在测试用例里覆盖的边界。
最后留一句保留意见:九江各地牌馆的细则并不统一,癞子能不能做将、豪华七对算几倍、封顶多少,问三个老玩家可能给你三个答案。
算法写得再干净,上线前也建议找本地玩家实打实对几把,把差异项落到配置里,比在代码里猜要靠谱。