匹配麻将和牌的正则表达式

匹配麻将和牌的正则表达式

0 Share

匹配麻将和牌的正则表达式

正则表达式一般用来描述字符串,所以我们把麻将和牌的组合先变成字符串。麻将中包含以下牌面: 条:幺鸡、二条、三条、四条、五条、六条、七条、八条、九条 筒:一筒、二筒、三筒、四筒、五筒、六筒、七筒、八筒、九筒 万:幺万、二万、三万、四万、五万、六万、七万、八万、九万 其他:红中 牌的摆放顺序和搭配也很重要,能影响人的判断,决定能否及时上 ...

正则表达式一般用来描述字符串,所以我们把麻将和牌的组合先变成字符串。麻将中包含以下牌面:

  • 条:幺鸡、二条、三条、四条、五条、六条、七条、八条、九条
  • 筒:一筒、二筒、三筒、四筒、五筒、六筒、七筒、八筒、九筒
  • 万:幺万、二万、三万、四万、五万、六万、七万、八万、九万
  • 其他:红中

牌的摆放顺序和搭配也很重要,能影响人的判断,决定能否及时上听、避免漏和;同样,也能够简化正则匹配问题。所以在这里假设已经按搭配成「刻、顺子、对子、杠」,顺子中以上面列表顺序从小到大依次排好。

和牌后共计 14 张,再加上明杠或暗杠 0 到 3 个,所以必然符合正则一——张数

^(?:幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]){14,17}$

牌中必然有至少一个的刻或杠,符合正则二——杠刻

(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3}

牌中也可能有顺子,符合正则三——顺子

(?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k))

另外还必须有一对,考虑到排序后对子前后的牌可能是顺子中的大牌或小牌,所以不做断言,只需符合正则四——对子

(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k

局部地区中不允许清一色,应该符合正则五——两门

(?:[鸡条].*[万筒])|(?:筒.*[万条鸡])|(?:万.*[筒条鸡])

最后,有一个非常严肃的问题,这副牌必须符合正则六——不断幺

[幺一九中]

好,我们现在整体回顾一下,和牌应该:

  1. 张数符合;
  2. 整副牌符合「此处可能有对子,顺子或杠刻,此处可能有对子,顺子或杠刻,此处可能有对子,此处可能有对子,顺子或杠刻,此处可能有对子,顺子或杠刻,此处可能有对子」;
  3. 杠刻至少一个;
  4. 杠最多有三个;
  5. 对子只有一个;
  6. 最多缺一门;
  7. 不能断幺;
  8. 牌面相同的牌不得超过4张(有人会出老千出得这么明显么!)。

下面以之前的六个正则为基础,进行一些加强判断。

整副牌由被「对子」隔开的连续的「顺子、杠刻」组成,正则二三四可以归纳为正则七——结构

^(?:(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k)?(?:(?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k)))|(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3})(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k)?)+$

杠刻前后可以是首尾,或者杠刻顺子对;正则七可以进化为正则八——必有杠刻

^(?:(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3})|(?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k))|(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k))*(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3})(?:(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3})|(?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k))|(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k))*$

局部地区要求上听时手里至少要有四张,所以即使在可和对对碰时允许有两个相同牌面的对子,也只可能有最多三杠;前后可能是首尾或「顺子、刻、对子」的杠,允许有 0 到 3 个。3个杠时,张数17张,除去杠之外的5张牌应该由至少一个对子与一套「顺子、刻」组成,无论如何组合,都不会发生出现同样四张牌、却不是杠的情况;验证非4杠时也就不用考虑两对的情况,无需为对子再做负断言,正则有所简化;即符合正则九——至多三杠

^(?!(?:.*(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{3}.*){4,}).*$

「幺鸡幺鸡幺鸡三条四条五条五条六条七条幺万幺万幺万九万九万」不应认为包含对五条;但「幺鸡幺鸡幺鸡三条四条五条五条五条五条六条七条二万二万二万」则应算做有对五条,所以对子两侧应该是首尾或顺子杠刻,符合正则十——仅一对

^(?:(?:(?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k)))|((?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3}))*(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k)(?:((?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k)))|((?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3}))*$

综上,以相互独立的条件中任意一个做正则主体语句,其他条件做断言,即可构造出匹配麻将和牌的正则。例,正则十一——和牌

^(?=^(?:幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]){14,17}$)(?=(?:.*[鸡条].*[万筒].*)|(?:.*筒.*[万条鸡].*)|(?:.*万.*[筒条鸡].*))(?=.*[幺一九中].*)(?=^(?:(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3})|(?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k))|(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k))*(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3})(?:(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3})|(?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k))|(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k))*$)(?=^(?!(?:.*(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{3}.*){4,}).*$)(?:(?:(?:(?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k)))|((?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3}))*(?:(?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k)(?:((?:幺鸡二条三条|一筒二筒三筒|幺万二万三万|(?:二(?[条筒万])三\k四\k)|(?:三(?[条筒万])四\k五\k)|(?:四(?[条筒万])五\k六\k)|(?:五(?[条筒万])六\k七\k)|(?:六(?[条筒万])七\k八\k)|(?:七(?[条筒万])八\k九\k)))|((?(幺鸡|一筒|幺万|红中|[二三四五六七八九][条筒万]))\k{2,3}))*)$

目测该表达式会有压缩精简的空间,但恐使其仅存不多的可读性丧失殆尽。那么有人会问了,这样只匹配了哈尔滨麻将,那么「吉津冀秦晋鄂皖沪浙湘蜀粤」各地的麻将和牌正则能不能都给写了呢?对此,我的回答只有一个字:

格稳! : (

嗯,严肃一点,其实基于正则表达式的断言,可以像搭积木一样使用不同的组合来适应各种麻将的规则。适当的捕获分组甚至能解决番数的计算,不过这只是未验证过的猜测。

原发于知乎:http://www.zhihu.com/question/24572110/answer/37185178

Comments ( 5 )

  1. Replyv2ex_tour
    别人没感觉,反正我是真是看不惯你这种sb 见到此话之博主,一年之内代码全是bug无法调通 不通过or删此话着,你就等着一户口吧
  2. Reply王的日常
    笑喷了.... V2EX观光团
  3. ReplyDavid Huang
    路过看看火了的留言

Leave a reply

Your email address will not be published.