文章轉(zhuǎn)載于:http://blog.csdn.net/ichiryuu/article/details/77967065
正常的麻將胡牌方式為滿足N * ABC + M *DDD +EE 的形式,及存在一個(gè)對(duì)子(EE),剩余牌均能組成順子(ABC)或者刻子(DDD)。
很容易發(fā)現(xiàn)必須滿足size%3 == 2的形式才可以去計(jì)算胡牌固以。
數(shù)據(jù)結(jié)構(gòu)的選仁镅省:
麻將有萬(wàn)呢袱、餅屡律、條三類各九種掩驱,另外還有東西南北中,春夏秋冬穿剖。
種類不是很多蚤蔓,一個(gè)字節(jié)表示就可以了,前四位代表類型糊余,后四位代表值秀又,東西南北中,春夏秋冬可以集中到一種類型中去贬芥。
普通麻將的計(jì)算方式:
????1.首先找出所有包含一對(duì)的情形吐辙,移除對(duì)子(注意去重),記下剩余牌的所有集合為T(mén)n;
????2.針對(duì)每個(gè)Tn中的數(shù)組嘗試移除一個(gè)順子蘸劈,成功轉(zhuǎn)到2昏苏,失敗到3。
????3.針對(duì)每個(gè)Tn中的數(shù)組嘗試移除一個(gè)刻子(DDD)威沫,成功轉(zhuǎn)到2贤惯。
????4.若當(dāng)前的數(shù)組的數(shù)量變?yōu)?,則表示棒掠,當(dāng)前的方案可以胡牌孵构。
2,3,4可以作為一個(gè)check_3n(檢測(cè)是否滿足N * ABC + M *DDD)的函數(shù),遞歸調(diào)用即可烟很。
針對(duì)有癩子的麻將(百搭):
最簡(jiǎn)單的辦法是嘗試將癩子牌變?yōu)樗信苼?lái)進(jìn)行嘗試颈墅,不過(guò)如果手中有多張癩子牌的話計(jì)算量就相當(dāng)大了,比如3張雾袱,則需要計(jì)算牌的種類的3次方次恤筛,雖然中途可以通過(guò)剪枝減少部分計(jì)算量,但還是太慢了芹橡。
針對(duì)這種情況我們可以在計(jì)算出癩子的數(shù)量毒坛,如果出現(xiàn)找出順子或刻子失敗,我們則可以用癩子去補(bǔ)僻族,如果失敗了粘驰,那么當(dāng)前的方案就不通過(guò)屡谐。
????1.同樣找出所有包含一對(duì)的情形述么,移除對(duì)子,移除的時(shí)候需要注意更新癩子的數(shù)量這里需要注意的是對(duì)子是怎么產(chǎn)生的:
????????????原有的對(duì)子
????????????一個(gè)癩子和一普通的組成的對(duì)子
????????????一對(duì)癩子
????2.針對(duì)每個(gè)數(shù)組嘗試移除一個(gè)順子愕掏,成功轉(zhuǎn)到2度秘,如果失敗嘗試用癩子去補(bǔ),癩子也不夠,轉(zhuǎn)到3剑梳。
????3.針對(duì)每個(gè)數(shù)組嘗試移除一個(gè)刻子(DDD)唆貌,成功轉(zhuǎn)到2,如果失敗嘗試用癩子去補(bǔ)垢乙,癩子也不夠锨咙,當(dāng)前的方案就不通過(guò)。
????4.若當(dāng)前的數(shù)組的數(shù)量變?yōu)?追逮,則表示酪刀,當(dāng)前的方案可以胡牌。
算法理解之后就不難了 , 下面開(kāi)始詳細(xì)的闡述了.
1. 將麻將抽象為數(shù)字
數(shù)字 {01 ~ 09} 表示? {1 ~ 9}?筒
數(shù)字 {11 ~ 19} 表示 ?{1 ~ 9} 條
數(shù)字 {21 ~ 29} 表示 ?{1 ~ 9} 萬(wàn)
數(shù)字 {31 33 35 37 } 表示 { 東 南 西 北 }
數(shù)字 {41 43 45} 表示 {中 發(fā) 白}
數(shù)字10 20 30 32 34 36 40 42 44 46 空出來(lái)不代表任何麻將牌 這樣設(shè)計(jì)的好處就是使得能夠形成順子的牌在用數(shù)字表示出來(lái)的時(shí)候剛好也是連著的 , 而不能夠形成順子的牌,在用數(shù)字表示的時(shí)候并不是順子 . 便于以后使用代碼進(jìn)行判斷
2. 算法的核心流程
玩過(guò)麻將的都知道麻將玩家手上一般有兩種牌 一種是手牌 一種是已經(jīng)碰牌或者吃牌或者杠牌之后已經(jīng)明了的牌 . 現(xiàn)在以玩家A為例 , 把牌分的細(xì)一點(diǎn)
a. 玩家A的手牌 ?(數(shù)量1 + 3*n n < N , n < 4? )
b. 其他玩家打出來(lái)的牌 (數(shù)量 1)
c. 玩家A從牌面上取出來(lái)的牌 (數(shù)量 1)
d. 玩家A吃碰杠的牌 (3*n + 4*m)?
能否胡牌主要是看手牌a 和b/c 的組合能否形成一對(duì)加n條順子和m條克子 . 能則能胡 反則不能.
如上圖 用數(shù)字表示為 {1,1,2,2,2,3,4,11,12,12,13,13,14,1} 前13張牌為手牌,最后一張二條為玩家A從牌面上取出的牌?
OK, 現(xiàn)在只需要先取出一對(duì)將,然后判斷剩下的牌能否全部形成順子或者克子,現(xiàn)在對(duì)牌面按照相對(duì)應(yīng)的數(shù)字進(jìn)行從小到大的排序 . 現(xiàn)在從剩余的牌中最左邊的牌開(kāi)始 , 如果只有一張這樣的牌那么這張牌A就只能當(dāng)作順子的開(kāi)頭 ; 如果有兩張這樣的牌 , 因?yàn)橐呀?jīng)有了一對(duì)將而這兩張也不能組成克子 , 所以這兩張只能當(dāng)作兩個(gè)順子的開(kāi)頭 ; 如果有三張這樣的牌 , 可以組成克子 , 但是如果讓他組成順子則要求為 AAABBBCCC 與后面的三張也能組成克子 所以組成順子或者克子本質(zhì)是相同的 但是組成克子AAA的通用性要高于組成順子AAABBBCCC?所以當(dāng)有三個(gè)及以上這樣牌的時(shí)候優(yōu)先組成克子AAA ; 如果有四張這樣的牌,要能胡牌則需要 AAAABBBBCCCC 或者 AAAABC ,對(duì)于是先組一個(gè)順子還是一個(gè)克子都會(huì)回到上述的情況 .?
(這里沒(méi)有對(duì)七對(duì)等各類大胡作出判斷)
步驟一:從上述數(shù)組中找到一對(duì)做"將",并從數(shù)組中移除 钮孵,
這里共有4對(duì)牌所以要分成4種情況
1. {1,1}(將牌) , {1,2,2,2,3,4,11,12,12,13,13,14}(余牌)
2. {2,2}(將牌) ,?{1,1,1,2,3,4,11,12,12,13,13,14}(余牌)
3. {12,12}(將牌) ,?{1,1,1,2,2,2,3,4,11,13,13,14}(余牌)
4.?{13,13}(將牌) ,?{1,1,1,2,2,2,3,4,11,12,12,14}(余牌)
依次進(jìn)行步驟二的檢查 檢查完最后一種情況而沒(méi)有返回 "能胡牌" 則返回 不能胡牌
步驟二:余牌數(shù)量為0 則返回 "能胡牌" 否則進(jìn)入下一步 .
步驟三:判斷余牌前三張是否相同 相同-> 步驟四?; 不同 -> 步驟五.
步驟四:移除余牌中的前三張牌 , 返回步驟二.
步驟五:若余牌中第一個(gè)數(shù)為N , 則判斷是否有N + 1 與 N + 2 同時(shí)存在與余牌中 , 有將N , n+1 , n+2 從余牌中移除并返回 步驟二 , 否則返回步驟一
演示如下
1. {1,1}(將牌) , {1,2,2,2,3,4,11,12,12,13,13,14}(余牌)
步驟二 >> 步驟三 >> 步驟五 == {2,2,4,11,12,12,13,13,14}(余牌) >>
? ? ?步驟二?>>?步驟三?>>?步驟五?>>?步驟一
2. {2,2}(將牌) ,?{1,1,1,2,3,4,11,12,12,13,13,14}(余牌)
步驟二?>>?步驟三?>>?步驟四 == ?{2,3,4,11,12,12,13,13,14}(余牌)?>>?
步驟二?>>?步驟三?>>?步驟五 ==?{11,12,12,13,13,14}(余牌)?>>?
步驟二?>>?步驟三?>>?步驟五 ==?{12,13,14}(余牌)?>>?
步驟二?>>?步驟三?>>?步驟五 ==?{}(余牌)?>>?
步驟二 ? "能胡牌"
代碼如下: