做过本地麻将项目的人大概都有这种体验:规则文档翻了三遍,真到写胡牌判定的时候,还是会被“十三烂算不算胡”“杠上开花能不能叠抢杠”这类细节卡住。

九江麻将算法列表这类整理工作的价值,不在于给你一套万能代码,而在于把不同玩法里的判定分支摊开对照,让开发者在落地前先看清分歧点在哪。

先分清三个层次:和牌型、胡牌条件、番型算分

很多人把这三件事混在一起写,结果一个函数里塞了几百行 if。实战里拆开更稳:

  • 和牌型判定:只回答“这 14 张能不能组成合法结构”,比如四组面子加一对将、七对、十三幺等。
  • 胡牌条件校验:查规则层,比如是否缺门、是否必须自摸、门前清限制、报听状态。
  • 番型与算分:在已确认胡牌的前提下,匹配番种、计倍数、封顶处理。

分开之后,测试用例也好写。牌型层用纯组合测试,规则层用状态机测试,算分层用表驱动,互不干扰。

常见判定算法清单对照

1. 递归拆解法(面子优先)

最经典的思路:取数牌里最小的一张,尝试当刻子拆、当顺子拆、当将牌拆,递归回溯。实现简单,可读性好,缺点是遇到大量对子或特殊牌型时分支多。实战中加个记忆化,把剩余牌的张量做 key 缓存结果,性能基本够用。

2. 计数表 + 位置扫描

把 34 种牌映射成计数数组,从左往右扫,遇到非零就按刻子/顺子消耗。它比递归少了函数调用开销,但要注意:扫描顺序固定时,某些牌型会漏判,得配合将牌枚举跑一遍。

九江麻将算法列表里常见的实现多是这一路。

3. 位运算 / 状态压缩

用几个 64 位整数表示花色牌分布,胡牌判定转成位模式匹配。适合高频计算场景,比如 AI 搜索里一次要判断上万手牌。可维护性一般,改规则时容易出错,建议只在性能瓶颈处用。

4. 查表法(预生成胡牌表)

提前把所有合法和牌型枚举出来,存成哈希集合或前缀树,判定时直接查。优点是判定 O(1) 级别,缺点是对带癞子、带特殊花色的玩法要额外处理,表会膨胀。癞子牌通常先做“替换枚举”,再查表。

几个容易踩的规则分歧

算法写得再漂亮,规则对不上照样白搭。这些点,不同本地玩法差异很大,落地前最好逐条和需求方确认:

  1. 七对是否允许四张同牌:有的算豪华七对加番,有的直接判不胡。
  2. 癞子能否当将、能否组成对子:这一条直接影响替换枚举的范围。
  3. 抢杠与杠上花的优先级:谁先结算,涉及状态衔接。
  4. 报听后的手牌约束:报听后能不能换牌、放炮是否必须胡,规则文档里常写得含糊。
  5. 封顶与番数上限:算分层要单独处理,别混进胡牌判定。

顺便说一句,别指望一次把规则谈死。有些细节是上线后玩家反馈才暴露的,所以判定层最好留一个可配置的规则开关,别把分支硬编码死。

怎么把清单变成可用的对照表

建议按“牌型 × 规则项 × 实现方式”做一张三维对照表。纵向列玩法(九江本地、周边县市变体),横向列判定点和算分点,单元格里填该玩法的取值和对应代码位置。

这张表比一堆散落的规则文档实用得多,新人接手也能快速定位。

再补一层自动化:把每个玩法的典型牌例做成 JSON 用例集,跑回归时同时验证胡牌与否和番数结果。改规则只需加用例,不用重读全部代码。这套做法在九江麻将算法列表这类多玩法整理场景里,收益相当明显——规则变更频繁,靠人肉记忆迟早出岔子。

最后提醒一点:胡牌判定算法的正确性,靠的是边界用例覆盖,不是代码技巧。先把“不胡”的边界想全,比优化那几毫秒更有意义。