做九江麻将的规则算法,最容易踩的坑往往不是胡牌判定本身,而是“同一个市,隔条街规则就不一样”。同一个功能,A桌能用,B桌就被投诉。
把常见的差异点拆开看,也顺带说说实战里哪些地方最容易出问题。
牌张构成:先把输入定死,再谈算法
九江多数牌桌用108张序数牌,万、条、筒各36张,不带风牌字牌。但也有牌友局保留红中做癞子,或者干脆加回东南西北。
算法层第一步应该把牌墙做成配置:牌种、张数、有没有癞子、癞子能不能当万能牌。这些写死进代码,换一个区县就得重写一遍。
缺门是第二个入口。部分玩法要求胡牌时手牌至少缺一门花色,也有开局定缺的打法。定缺会影响出牌合法性,也会影响胡牌判定,所以在出牌校验那里就得拦一道,别等到结算才发现这手牌根本不该成立。
- 牌墙配置:牌种集合、每种张数、是否有癞子
- 缺门规则:不定缺、开局定缺、胡牌时校验缺门
- 癞子语义:能否当将、能否参与七对
胡牌判定:几条常见路径
标准型(4面子+1将)、碰碰胡、七对是主流,这三类基本跑不掉。
九江一些牌桌还认“十三烂”,也就是字牌不搭、序数牌间隔拉开的那种牌,但定义各桌差别不小——有的要求三门齐全,有的只要求互不相连。
这类地方牌型如果没有明确文档,建议先问清楚再写。
实现上,比较稳的做法是把牌型判断做成匹配表:每种牌型一个判定函数,返回“是否成立”和“基础番数”两部分。别把番型判断直接塞进递归拆牌里,后期加一个牌型会痛苦得想推倒重来。
拆牌顺序的坑
递归拆牌时先拆刻子还是先拆顺子,结果可能不一样,尤其是涉及清一色、一根筋这种要看整体构成的番型。稳妥点就枚举所有拆法,取番数最高的那组。性能上会多花一些,但108张牌的手牌规模,实际跑起来还好。
算番与结算:差异最大的地方
九江常见番型有自摸、杠上开花、抢杠胡、清一色、碰碰胡、七对、海底捞月。杠分通常单独算,明杠、暗杠、补杠价格各不相同。点炮和自摸的赔付结构差别更大:有的牌桌自摸三家付、点炮一家付,也有玩“包三家”的,一家点炮三家赔。
封顶值也别忽略。不设封顶,算法容易算出天文数字,结算时双方都不认。九江不少牌桌按8番或16番封顶,超出按顶算——具体数字各桌自定,做成配置项最省事。
- 番型字典:番型名、判定条件、番数
- 杠分表:明杠/暗杠/补杠分别计分
- 赔付模式:自摸三家付、点炮一家付、包三家
- 封顶值:按番封顶或按分封顶
实战场景:算法得跟得上牌桌节奏
牌桌上最容易出争议的是杠牌时机和抢杠胡。补杠那一瞬间别人能不能胡?谁有优先权?
规则里得写清楚:抢杠胡优先于补杠成立,补杠的牌在确认无人抢胡之后才进杠区。另一个是“过胡不胡”。有的地方规定放过一次胡,同一轮不能胡同一张牌,甚至整局不能再胡。
这个状态要挂在玩家对象上,轮次结束或者牌墙变化时清空,忘了清就会出现该胡不能胡的诡异判定。
还有听牌提示。实战里玩家点一下“听牌”,系统给出剩余可胡张数,这个功能看着简单,实际要求算法能在几百毫秒内把所有待摸牌枚举一遍并做胡牌校验。
如果拆牌是纯递归加剪枝,通常够用;如果还叠了癞子通配,就得提前做缓存。
落地时几点建议
- 规则外置成配置,代码里不留“九江默认值”这种东西。
- 牌型判定和番数计算分两层,方便单独测试。
- 留回放日志,记录每次出牌、碰杠、胡牌判定结果,出争议时能翻。
地方麻将的规则差异本来就没有标准答案,能配置、能回放、能把判定过程解释给玩家听,比追求“绝对正确”更实用。