1. 程式人生 > >癩子麻將胡牌以及聽牌演算法實現

癩子麻將胡牌以及聽牌演算法實現

最先實現的就是演算法的實現。

需求:碰槓胡  ,不能吃 ,不能聽 ,只能自摸胡,其中癩子可以做任意牌但是不能碰和槓。

寫的時候還不會玩麻將,還是老闆教的。^_^

最麻煩的是胡牌演算法。之前搜到的都是不包含癩子正常的胡牌,用的是%3餘2,其中餘數2就是餘的將的意思。

但是有癩子就不能這麼用了。只好自己寫一個了。

一個有136張牌,萬,餅,條,東西南北中發白34種牌。

有四個癩子是直接就胡牌的,最壞的情況是有3個癩子,但是如果遍歷一遍不用邏輯判斷就有34X34X34接近4萬次.

想一下如果能胡牌,最壞的情況下是在最後一次判斷能胡牌,那之前的近4萬次的判斷都是浪費的。

這裡轉變一下思維,就是有目的的按需所取成胡牌所需要的癩子個數,而不是盲目遍歷再判斷胡牌。

演算法的正確性:如果想胡牌必然是三撲一將(正常胡牌)。其中撲指的是順子或者三重牌(比如 一餅二餅三餅  或者東風東風東風)。將指的是兩個重牌。

四種情況:

                         1.假如將在【萬】裡面那麼【餅】【條】【風】(包含中發白)必然是整撲。

                         2.假如將在【餅】裡面那麼【萬】【條】【風】(包含中發白)必然是整撲。

                         3.假如將在【條】裡面那麼【萬】【餅】【風】(包含中發白)必然是整撲。                      

                         4.假如將在【風】裡面(包含中發白)那麼【萬】【餅】【條】必然是整撲。

假如當前癩子的數目是curHunNum。

現在先獲取【萬】【餅】【條】【風】各自成為整撲所需要癩子的個數,如果是情況一。

needHunNum= 【餅】成為整撲需要癩子的個數+【條】成為整撲需要癩子的個數+【風】成為整撲需要癩子的個數;

如果hadHunNum = needHunNum - curHunNum; 如果hadHunNum<0 需求的比擁有的多  就不做判斷。

否則就判斷【萬】中成為整撲一將需要的數目。

情況二三四依次類推。

更新

今天又寫了一下聽牌,既然寫了希望能幫助更多的人吧

聽的牌要麼是將要麼是撲,有的牌有可能同時當將和當撲都能贏

當結果有一個能聽的時候說明只要再來一個混也能贏。

程式碼:

majmap = {"101":"一萬","102":"二萬","103":"三萬","104":"四萬","105":"五萬","106":"六萬","107":"七萬","108":"八萬","109":"九萬",
          "201":"一餅","202":"二餅","203":"三餅","204":"四餅","205":"五餅","206":"六餅","207":"七餅","208":"八餅","209":"九餅",
          "301":"一條","302":"二條","303":"三條","304":"四條","305":"五條","306":"六條","307":"七條","308":"八條","309":"九條",
          "401":"東風","402":"西風","403":"南風","404":"北風","405":"紅中","406":"發財","407":"白板"}

if __name__ == "__main__":

    #####################################
    #測試胡牌
    # samArr = [111, 111, 111, 111, 214, 214, 214, 214, 315, 315, 315, 315, 118, 119, 119 ]
    # print testHu(112, samArr, 111)
    #####################################


    #####################################
    #測試具體能聽哪些牌
    # samArr = [405,202,203,203,204,205,302,302,303,303]#執行時間:0:00:00.001100 <callTime:83>  [ 一餅 , 四餅 , 二條 , 三條 , 紅中 , ]
    # samArr = [405,202,203,203,204,205,301,302,303,303]#執行時間:0:00:00.000721 <callTime:72>  [ 一餅 , 四餅 , 三條 , 紅中 , ]
    # samArr = [405,202,203,203,204,205,301,302,302,303]#執行時間:0:00:00.000659 <callTime:72>  [ 一餅 , 四餅 , 二條 , 紅中 , ]
    # samArr = [405,203,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000887 <callTime:72>  [ 三餅 , 六餅 , 一條 , 四條 , 紅中 , ]
    # samArr = [405,202,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000715 <callTime:59>  [ 二餅 , 五餅 , 一條 , 四條 , 紅中 , ]
    # samArr = [405,202,203,203,204,301,302,302,303,303]#執行時間:0:00:00.000797 <callTime:72>  [ 三餅 , 一條 , 四條 , 紅中 , ]
    # samArr = [405,104,104,107,107,107,203,204,205,305,307,308,309]#執行時間:0:00:00.000791 <callTime:83>  [ 四萬 , 五條 , 三條 , 四條 , 六條 , 七條 , 紅中 , ]
    # samArr = [405,104,104,107,107,107,203,204,205,207,307,308,309]#執行時間:0:00:00.000844 <callTime:85>  [ 四萬 , 七餅 , 二餅 , 五餅 , 六餅 , 八餅 , 九餅 , 紅中 , ]
    # samArr = [405,104,105,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000799 <callTime:88>  [ 二餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,103,104,105,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000859 <callTime:89>  [ 二餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,103,104,105,106,107,108,202,202,302,304,306,306]#執行時間:0:00:00.000836 <callTime:88>  [ 二餅 , 三條 , 六條 , 紅中 , ]
    # #測試兩個癩子的執行時間
    # samArr = [405,405,103,105,106,107,108,202,202,302,304,306,306]#執行時間:0:00:00.000955 <callTime:136>  [ 四萬 , 二餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,405,103,105,106,107,108,201,202,302,304,306,306]#執行時間:0:00:00.000696 <callTime:94>  [ 四萬 , 三餅 , 三條 , 紅中 , ]
    # samArr = [405,405,103,105,106,107,108,201,202,302,303,306,306]#執行時間:0:00:00.000790 <callTime:94>  [ 四萬 , 三餅 , 一條 , 四條 , 紅中 , ]
    # #測試三個癩子的執行時間
    # samArr = [405,405,405,105,106,107,108,202,202,302,304,306,306]#執行時間:0:00:00.001111 <callTime:135>  [ 五萬 , 八萬 , 三萬 , 四萬 , 六萬 , 七萬 , 九萬 , 二餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,405,405,105,106,107,108,201,202,302,304,306,306]#執行時間:0:00:00.000926 <callTime:118>  [ 五萬 , 八萬 , 三萬 , 四萬 , 六萬 , 七萬 , 九萬 , 三餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,405,405,105,106,107,108,201,202,302,303,306,306]#執行時間:0:00:00.000949 <callTime:116>  [ 五萬 , 八萬 , 三萬 , 四萬 , 六萬 , 七萬 , 九萬 , 三餅 , 一條 , 四條 , 六條 , 紅中 , ]
    # #測試四個癩子的執行時間
    # samArr = [405,405,405,405,106,107,108,202,202,302,304,306,306]#執行時間:0:00:00.000607 <callTime:18>  [ 一萬 , 二萬 , 三萬 , 四萬 , 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 一餅 , 二餅 , 三餅 , 四餅 , 五餅 , 六餅 , 七餅 , 八餅 , 九餅 , 一條 , 二條 , 三條 , 四條 , 五條 , 六條 , 七條 , 八條 , 九條 , 東風 , 西風 , 南風 , 北風 , 紅中 , 發財 , 白板 , ]
    # samArr = [405,405,405,405,106,107,108,201,202,302,304,306,306]#執行時間:0:00:00.000390 <callTime:18>  [ 一萬 , 二萬 , 三萬 , 四萬 , 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 一餅 , 二餅 , 三餅 , 四餅 , 五餅 , 六餅 , 七餅 , 八餅 , 九餅 , 一條 , 二條 , 三條 , 四條 , 五條 , 六條 , 七條 , 八條 , 九條 , 東風 , 西風 , 南風 , 北風 , 紅中 , 發財 , 白板 , ]
    # samArr = [405,405,405,405,106,107,108,201,202,302,303,306,306]#執行時間:0:00:00.000758 <callTime:18>  [ 一萬 , 二萬 , 三萬 , 四萬 , 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 一餅 , 二餅 , 三餅 , 四餅 , 五餅 , 六餅 , 七餅 , 八餅 , 九餅 , 一條 , 二條 , 三條 , 四條 , 五條 , 六條 , 七條 , 八條 , 九條 , 東風 , 西風 , 南風 , 北風 , 紅中 , 發財 , 白板 , ]
    #####################################

    #####################################
    #測試打出哪些牌能聽
    # tingNumArr = [405,202,203,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000704 <callTime:48>  [ 二餅 , 三餅 , 五餅 , 一條 , 二條 , 三條 , ]
    # tingNumArr = [405,104,104,107,107,107,203,204,205,207,305,307,308,309]#執行時間:0:00:00.000649 <callTime:61>  [ 七餅 , 五條 , ]
    # tingNumArr = [405,103,104,105,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000874 <callTime:82>  [ 三萬 , 六萬 , 九萬 , ]
    # #測試兩個癩子
    # tingNumArr = [405,405,203,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000424 <callTime:35>  [ 三餅 , 四餅 , 五餅 , 一條 , 二條 , 三條 , ]
    # tingNumArr = [405,405,104,107,107,107,203,204,205,207,305,307,308,309]#執行時間:0:00:00.000616 <callTime:63>  [ 四萬 , 七餅 , 五條 , ]
    # tingNumArr = [405,405,104,105,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000551 <callTime:48>  [ 四萬 , 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 二餅 , 四條 , 六條 , 二條 , ]
    # #測試三個癩子
    # tingNumArr = [405,405,405,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000493 <callTime:27>  [ 三餅 , 四餅 , 五餅 , 一條 , 二條 , 三條 , ]
    # tingNumArr = [405,405,405,107,107,107,203,204,205,207,305,307,308,309]#執行時間:0:00:00.000518 <callTime:36>  [ 七萬 , 三餅 , 四餅 , 五餅 , 七餅 , 五條 , 七條 , 八條 , 九條 , ]
    # tingNumArr = [405,405,405,105,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000522 <callTime:53>  [ 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 二餅 , 四條 , 六條 , 二條 , ]
    # #測試四個癩子
    # tingNumArr = [405,405,405,405,204,205,301,302,302,303,303]#執行時間:0:00:00.000422 <callTime:24>  [ 四餅 , 五餅 , 一條 , 二條 , 三條 , ]
    # tingNumArr = [405,405,405,405,107,107,203,204,205,207,305,307,308,309]#執行時間:0:00:00.000604 <callTime:35>  [ 七萬 , 三餅 , 四餅 , 五餅 , 七餅 , 五條 , 七條 , 八條 , 九條 , ]
    # tingNumArr = [405,405,405,405,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000678 <callTime:31>  [ 六萬 , 七萬 , 八萬 , 九萬 , 二餅 , 四條 , 六條 , 二條 , ]


    ##############################
    # 測試特殊情況  單一花色重複多次
    # tingNumArr = [405,201,201,201,201,202,202,202,202,205,205,205,205,209]#執行時間:0:00:00.006123 <callTime:356>  [ 五餅 , 九餅 , ]
    # tingNumArr = [405,405,202,204,205,205,206,206,207,208,208,208,209,306]#執行時間:0:00:00.002929 <callTime:358>  [ 二餅 , 九餅 , 六條 , ]
    # samArr = [405,201,201,201,201,201,202,202,202,202,204,204,205]#執行時間:0:00:00.016391 <callTime:1029>  [ 三餅 , 六餅 , 紅中 , ]
    samArr = [405,405,201,201,201,201,202,202,202,202,204,204,209]#執行時間:0:00:00.026671 <callTime:1527>  [ 三餅 , 四餅 , 七餅 , 八餅 , 九餅 , 紅中 , ]
    ##############################

    global callTime
    callTime = 0
    begin = datetime.datetime.now()
    # 測試摸哪些牌能胡牌
    tingArr = getTingArr(samArr,405)
    #測試打哪些牌能聽牌
    # tingArr = getTingNumArr(tingNumArr,405)
    #測試摸到這張牌是不是能胡牌
    # tingArr = {}
    # print testHu(209, samArr, 405)
    # tingArr = []
    # for i in tingNumArr:
    #     tmp = []
    #     tmp.extend(tingNumArr)
    #     tmp.remove(i)
    #     getTingNumArr(tmp,405)
    end = datetime.datetime.now()
    runTime = end-begin
    rstr = "執行時間:" + str(runTime) + " <callTime:" + str(callTime) + ">  [ "
    for i in range(0,len(tingArr)):
        key = str(tingArr[i])
        rstr +=  majmap.get(key) + " , "
    rstr += "]"
    print  rstr


可以發現一般情況下大概1ms時間左右,我用c++測試的時候大概能快五六倍的樣子。但是當花色比較單一,並且重複的數字比較多的時候,執行速度變得很慢。

原因如下:


不用再下載了直接貼上來吧也好直接更改:

#coding:utf8
#####################
#作者:skillart
#bolg:http://blog.csdn.net/skillart/article/details/40422885
#
#####################
# 資料格式:型別=value/100, 數值=value%10
# [111-119] 萬
# [121-129]
# [131-139]
# [141-149]

# [211-219] 餅
# [221-229]
# [231-239]
# [241-249]

# [311-319] 條
# [321-329]
# [331-339]
# [341-349]

# [411-417] 東西南北中發白
# [421-427]
# [431-437]
# [441-447]
import random
g_NeedHunCount = 4
g_mjsArr = [
    101, 102, 103, 104, 105, 106, 107, 108, 109, #萬
    101, 102, 103, 104, 105, 106, 107, 108, 109,
    101, 102, 103, 104, 105, 106, 107, 108, 109,
    101, 102, 103, 104, 105, 106, 107, 108, 109,
    201, 202, 203, 204, 205, 206, 207, 208, 209, #餅
    201, 202, 203, 204, 205, 206, 207, 208, 209,
    201, 202, 203, 204, 205, 206, 207, 208, 209,
    201, 202, 203, 204, 205, 206, 207, 208, 209,
    301, 302, 303, 304, 305, 306, 307, 308, 309, #條
    301, 302, 303, 304, 305, 306, 307, 308, 309,
    301, 302, 303, 304, 305, 306, 307, 308, 309,
    301, 302, 303, 304, 305, 306, 307, 308, 309,
    401, 402, 403, 404, 405, 406, 407, # 東 西 南 北 中 發 白
    401, 402, 403, 404, 405, 406, 407,
    401, 402, 403, 404, 405, 406, 407,
    401, 402, 403, 404, 405, 406, 407
] # end

# [ 測試使用
g_testMjsArr = [
    101, 101, 101, 101, 102, 102, 104, 201, 103,
    103, 103, 103, 104, 104, 102, 104, 105, 105,
    105, 105, 106, 106, 106, 106, 107, 107, 107,
    107, 108, 108, 108, 108, 109, 109, 109, 109,
    201, 202, 203, 204, 204, 202, 203, 205, 201,
    202, 203, 204, 201, 202, 203, 204, 205, 206,
    207, 208, 206, 102, 207, 208, 205, 206, 207,
    208, 205, 206, 207, 208, 209, 209, 209, 209,
    301, 302, 303, 304, 301, 302, 303, 304, 301,
    302, 303, 304, 301, 302, 303, 304, 305, 306,
    307, 308, 305, 306, 307, 308, 305, 306, 307,
    308, 305, 306, 307, 308, 309, 309, 309, 309,
    401, 402, 403, 404, 405, 406, 407, # 東 西 南 北 中 發 白
    401, 402, 403, 404, 405, 406, 407,
    401, 402, 403, 404, 405, 406, 407,
    401, 402, 403, 404, 405, 406, 407
]
def getTestMjs():
    mjArr = []
    mjArr.extend(g_testMjsArr)
    return mjArr
# 測試使用 ]

# 獲得一副牌並混亂牌
def randomMjs():
    mjArr = []
    mjArr.extend(g_mjsArr)
    i = random.randint(1, 5 )
    while i > 0:
        random.shuffle( mjArr )
        i = i - 1
    return mjArr

# 判斷是否有效
def isValidMj( mj ):
    itype = mj / 100
    clr = mj % 100 / 10 # 忽略該欄位,僅用於判別有效
    value = mj % 10
    if itype == 1 or itype == 2 or itype == 3:
        if value < 1 or value > 9 or clr != 0:
            return False
        else:
            return True
    elif itype == 4:
        if value < 1 or value > 7 or clr != 0:
            return False
        else:
            return True
    else:
        return False

# 獲得混
def getHunMj(fanMj):
    t = fanMj / 100
    v = fanMj % 10
    if t == 4:
        v = v+1
        if v > 7:
            v = 1
    elif t>0 and t<5:
        v = v+1
        if v > 9:
            v = 1
    return t*100 + v

def sortArr(arr):
    if len(arr) == 0:
        return
    arr.sort( None, key=lambda v:v%10 )

def seprateArr( mjArr, hunMj ):
    reArr = [[],[],[],[],[]]
    ht = hunMj / 100
    hv = hunMj % 10
    for mj in mjArr:
        t = mj / 100
        v = mj % 10
        if ht == t and hv == v:
            t = 0
        reArr[t].append( mj )
        sortArr( reArr[t] )
    return reArr

def test3Combine( mj1, mj2, mj3 ):
    t1, t2, t3 = mj1/100, mj2/100, mj3/100
    # 牌型不同不能組合
    if t1 != t2 or t1 != t3:
        return False
    v1, v2, v3 = mj1%10, mj2%10, mj3%10
    # 重牌
    if v1 == v2 and v1 == v3:
        return True
    if t3 == 4:
        return False
    if (v1+1) == v2 and (v1+2) == v3:
        return True
    return False


def getModNeedNum(arrLem,isJiang):
    if arrLem <=0:
        return 0
    modNum = arrLem % 3
    needNumArr = [0,2,1]
    if isJiang:
        needNumArr = [2,1,0]
    return needNumArr[modNum]

def getNeedHunInSub( subArr, hNum ):
    global callTime
    callTime += 1

    global g_NeedHunCount
    if g_NeedHunCount == 0:
        return

    lArr = len(subArr)

    if hNum + getModNeedNum(lArr,False) >= g_NeedHunCount:
        return

    if lArr == 0:
        g_NeedHunCount = min( hNum, g_NeedHunCount )
        return
    elif lArr == 1:
        g_NeedHunCount = min( hNum+2, g_NeedHunCount )
        return
    elif lArr == 2:
        t = subArr[0] / 100
        v0 = subArr[0] % 10
        v1 = subArr[1] % 10
        if t == 4: # 東南西北中發白(無順)
            if v0 == v1:
                g_NeedHunCount = min( hNum+1, g_NeedHunCount )
                return
        elif  (v1-v0) < 3:
            g_NeedHunCount = min( hNum+1, g_NeedHunCount )
        return
    elif lArr >= 3: # 大於三張牌
        t  = subArr[0] / 100
        v0 = subArr[0] % 10
        v2 = subArr[2] % 10

        #第一個和另外兩個一鋪
        arrLen = len(subArr)
        for i in range( 1, arrLen ):
            if hNum + getModNeedNum(lArr-3,False)  >= g_NeedHunCount:
                break
            v1 = subArr[i] % 10
            #13444   134不可能連一起
            if v1 - v0 > 1:
                break
            if ( i+2 )  < arrLen:
                if ( subArr[i+2]%10 ) == v1:
                    continue
            if i+1 < arrLen:
                tmp1, tmp2, tmp3 = subArr[0],subArr[i], subArr[i+1]
                if test3Combine( tmp1, tmp2, tmp3 ):
                    subArr.remove( tmp1 )
                    subArr.remove( tmp2 )
                    subArr.remove( tmp3 )
                    subLen = len(subArr)
                    getNeedHunInSub(subArr, hNum)
                    subArr.append( tmp1 )
                    subArr.append( tmp2 )
                    subArr.append( tmp3 )
                    sortArr( subArr )

        # 第一個和第二個一鋪
        v1 = subArr[1] % 10
        if hNum + getModNeedNum(lArr-2,False) +1 < g_NeedHunCount:
            if t == 4: # 東南西北中發白(無順)
                if v0 == v1:
                    tmp1 = subArr[0]
                    tmp2 = subArr[1]
                    subArr.remove( tmp1 )
                    subArr.remove( tmp2 )
                    getNeedHunInSub(subArr, hNum+1)
                    subArr.append( tmp1 )
                    subArr.append( tmp2 )
                    sortArr( subArr )

            else:
                arrLen= len(subArr)
                for i in range( 1, arrLen ):
                    if hNum + getModNeedNum(lArr-2,False) +1  >= g_NeedHunCount:
                        break;
                    v1 = subArr[i] % 10
                    #如果當前的value不等於下一個value則和下一個結合避免重複
                    if (i+1) != arrLen:
                        v2 = subArr[i+1] % 10
                        if v1 == v2:
                            continue
                    mius = v1 - v0
                    if  mius < 3:
                        tmp1 = subArr[0]
                        tmp2 = subArr[i]
                        subArr.remove( tmp1 )
                        subArr.remove( tmp2 )
                        getNeedHunInSub(subArr, hNum+1)
                        subArr.append( tmp1 )
                        subArr.append( tmp2 )
                        sortArr( subArr )
                        if mius >= 1:
                            break
                    else:
                        break

        # 第一個自己一鋪
        if  hNum + getModNeedNum(lArr-1,False)+2 < g_NeedHunCount:
            tmp = subArr[0]
            subArr.remove( tmp )
            getNeedHunInSub( subArr, hNum+2 )
            subArr.append( tmp )
            sortArr( subArr )
    else:
        return

def test2Combine( mj1, mj2 ):
    t1, t2 = mj1 / 100, mj2 / 100
    v1, v2 = mj1 % 10, mj2 % 10
    if t1 == t2 and v1 == v2:
        return True
    return False

def canHu( hunNum, arr ):
    global g_NeedHunCount
    tmpArr = []
    tmpArr.extend(arr)
    arrLen  = len( tmpArr )
    if arrLen <= 0:
        if hunNum >= 2:
            return True
        return False

    if hunNum < getModNeedNum(arrLen,True):
        return False

    for i in range( arrLen ):
        if i == (arrLen - 1 ):# 如果是最後一張牌
            if hunNum > 0:
                tmp = tmpArr[i]
                hunNum = hunNum - 1
                tmpArr.remove( tmpArr[i] )
                g_NeedHunCount = 4
                getNeedHunInSub(tmpArr, 0)
                if g_NeedHunCount <= hunNum:
                    # print 'type:',tmp/100, 'value', tmp%10, 1
                    return True
                hunNum = hunNum +1
                tmpArr.append(tmp)
                sortArr(tmpArr)
        else:
            if ( i+2 ) == arrLen or (tmpArr[i]%10) != (tmpArr[i+2]%10):
                if test2Combine( tmpArr[i], tmpArr[i+1] ):
                    tmp1 = tmpArr[i]
                    tmp2 = tmpArr[i+1]
                    tmpArr.remove( tmp1 )
                    tmpArr.remove( tmp2 )
                    g_NeedHunCount = 4
                    getNeedHunInSub(tmpArr, 0)
                    if g_NeedHunCount <= hunNum:
                        # print 'type:',tmp1/100, 'value', tmp1%10, 2
                        return True
                    tmpArr.append( tmp1 )
                    tmpArr.append( tmp2 )
                    sortArr(tmpArr)
            if hunNum>0 and (tmpArr[i]%10) != (tmpArr[i+1]%10):
                hunNum = hunNum -1
                tmp = tmpArr[i]
                tmpArr.remove( tmp )
                g_NeedHunCount = 4
                getNeedHunInSub(tmpArr, 0)
                if g_NeedHunCount <= hunNum:
                    # print 'type:',tmp/100, 'value', tmp%10, 3
                    return True
                hunNum = hunNum +1
                tmpArr.append( tmp )
                sortArr( tmpArr )
    return False

# 判斷胡牌
def testHu( mj, mjArr, hunMj ):
    global g_NeedHunCount
    tmpArr = []
    tmpArr.extend(mjArr) # 建立一個麻將陣列的copy
    if mj != 0:
        tmpArr.append( mj ) # 插入一個麻將
    sptArr = seprateArr( tmpArr, hunMj )
    curHunNum = len( sptArr[0] )
    if curHunNum > 3:
        return True

    ndHunArr = [] # 每個分類需要混的陣列
    for i in range( 1, 5 ):
        g_NeedHunCount = 4
        getNeedHunInSub( sptArr[i], 0 )
        ndHunArr.append(g_NeedHunCount)
    isHu = False
    # 將在萬中
    #如果需要的混小於等於當前的則計算將在將在萬中需要的混的個數
    ndHunAll = ndHunArr[1] + ndHunArr[2] + ndHunArr[3]
    if ndHunAll <= curHunNum:
        hasNum = curHunNum - ndHunAll
        isHu = canHu( hasNum, sptArr[1] )
        if isHu:
            return True
    # 將在餅中
    ndHunAll = ndHunArr[0] + ndHunArr[2] + ndHunArr[3]
    if ndHunAll <= curHunNum:
        hasNum = curHunNum - ndHunAll
        isHu = canHu( hasNum, sptArr[2] )
        if isHu:
            return True
    # 將在條中
    ndHunAll = ndHunArr[0] + ndHunArr[1] + ndHunArr[3]
    if ndHunAll <= curHunNum:
        hasNum = curHunNum - ndHunAll
        isHu = canHu( hasNum, sptArr[3] )
        if isHu:
            return True
    # 將在風中
    ndHunAll = ndHunArr[0] + ndHunArr[1] + ndHunArr[2]
    if ndHunAll <= curHunNum:
        hasNum = curHunNum - ndHunAll
        isHu = canHu( hasNum, sptArr[4] )
        if isHu:
            return True
    return False

def testGang( mj, mjArr, hunMj ):
    t = mj / 100
    v = mj % 10
    c = 0
    tmpArr = []
    tmpArr.extend(mjArr)
    sptArr = seprateArr( tmpArr, hunMj )
    if len( sptArr[t] ) < 2:
        return False
    else:
        for tmj in sptArr[t]:
            if ( tmj%10 ) == v:
                c = c+1
        if c == 3:
            return True

def testPeng( mj, mjArr, hunMj ):
    t = mj / 100
    v = mj % 10
    c = 0
    tmpArr = []
    tmpArr.extend(mjArr)
    sptArr = seprateArr( tmpArr, hunMj )
    if len( sptArr[t] ) < 2:
        return False
    else:
        for tmj in sptArr[t]:
            if ( tmj%10 ) == v:
                c = c+1
        if c == 2 or c == 3:
            return True

def analyzeAnGang( mjArr, hunMj ):
    result = []
    tmpArr = []
    tmpArr.extend(mjArr)
    sptArr = seprateArr( tmpArr, hunMj )
    for i in range(len(sptArr)):
        subLen = len( sptArr[i] )
        if subLen < 4:
            continue
        else:
            for j in range(subLen):
                if ( subLen - 1 - j )<3:
                    break
                if (sptArr[i][j]%10) == (sptArr[i][j+1]%10) and \
                                (sptArr[i][j+1]%10) == (sptArr[i][j+2]%10) and \
                                (sptArr[i][j+2]%10) == (sptArr[i][j+3]%10):
                    result.append( sptArr[i][j] )
    return result

def rmSample( mj, mjArr, cnt=0 ):
    i = cnt
    j = 0
    while i>0:
        if mjArr.count( mj ):
            mjArr.remove( mj )
            j += 1
        i -= 1
    return j


def getJiangNeedHum(arr):
    global g_NeedHunCount
    minNeedNum = 4
    tmpArr = []
    tmpArr.extend(arr)
    arrLen  = len( tmpArr )
    if arrLen <= 0:
        return 2
    for i in range( arrLen ):
        if i == (arrLen - 1 ):# 如果是最後一張牌
            tmp = tmpArr[i]

            tmpArr.remove( tmpArr[i] )
            g_NeedHunCount = 4
            getNeedHunInSub(tmpArr, 0)
            minNeedNum = min(minNeedNum,g_NeedHunCount+1)

            tmpArr.append(tmp)
            sortArr(tmpArr)
        else:
            if ( i+2 ) == arrLen or (tmpArr[i]%10) != (tmpArr[i+2]%10):
                if test2Combine( tmpArr[i], tmpArr[i+1] ):
                    tmp1 = tmpArr[i]
                    tmp2 = tmpArr[i+1]
                    tmpArr.remove( tmp1 )
                    tmpArr.remove( tmp2 )
                    g_NeedHunCount = 4
                    getNeedHunInSub(tmpArr, 0)

                    minNeedNum = min(minNeedNum,g_NeedHunCount)

                    tmpArr.append( tmp1 )
                    tmpArr.append( tmp2 )
                    sortArr(tmpArr)
            if (tmpArr[i]%10) != (tmpArr[i+1]%10):


                tmp = tmpArr[i]
                tmpArr.remove( tmp )
                g_NeedHunCount = 4
                getNeedHunInSub(tmpArr, 0)

                minNeedNum = min(minNeedNum,g_NeedHunCount+1)

                tmpArr.append( tmp )
                sortArr( tmpArr )
    return minNeedNum


def getTingArr(mjArr,hunMj):
    global g_NeedHunCount
    global callTime
    tmpArr = []
    tmpArr.extend(mjArr) # 建立一個麻將陣列的copy
    sptArr = seprateArr( tmpArr, hunMj )

    ndHunArr = [] # 每個分類需要混的陣列
    for i in range( 1, 5 ):
        g_NeedHunCount = 4
        getNeedHunInSub( sptArr[i], 0 )
        ndHunArr.append(g_NeedHunCount)


    jaNdHunArr = []#每個將分類需要混的陣列
    for i in range(1,5):
        jdNeedHunNum = getJiangNeedHum(sptArr[i])
        jaNdHunArr.append(jdNeedHunNum)


    curHunNum = len( sptArr[0])
    tingArr = []
    paiArr = [[101,110],[201,210],[301,310],[401,408]]

    #是否單調將
    isAllHu = False
    needNum = 0
    for i in range(0,4):
        needNum += ndHunArr[i]
    if curHunNum - needNum == 1:
        isAllHu = True
    if isAllHu:
        for lis in paiArr:
            for x in range(lis[0],lis[1]):
                tingArr.append(x)
        return  tingArr


    for i in range(0,4):
        # if len(sptArr[i+1]) == 0:
        #     continue;
        # 聽牌是將
        needNum = 0
        for j in range(0,4):
            if(i != j):
                needNum = needNum + ndHunArr[j]

        if needNum <= curHunNum:
            for k in range(paiArr[i][0],paiArr[i][1]):
                t = [k]
                t.extend(sptArr[i+1])
                sortArr(t)
                if canHu(curHunNum-needNum,t):
                    tingArr.append(k)
                    # print callTime

        # 聽牌是撲
        for j in range(0,4):
            if(i != j):
                needNum = 0
                for k in range(0,4):
                    if(k != i):
                        if(k == j):
                            needNum += jaNdHunArr[k]
                        else:
                            needNum += ndHunArr[k]
                if needNum <= curHunNum:
                    for k in range(paiArr[i][0],paiArr[i][1]):
                        if k not in tingArr:
                            t = [k]
                            t.extend(sptArr[i+1])
                            g_NeedHunCount = 4
                            sortArr(t)
                            getNeedHunInSub(t, 0 )
                            if g_NeedHunCount <= curHunNum - needNum:
                                tingArr.append(k)

    if(len(tingArr) > 0) and hunMj not in tingArr:
        tingArr.append(hunMj)
    return  tingArr;



def getTingNumArr(mjArr,hunMj):
    global g_NeedHunCount
    global callTime
    tmpArr = []
    tmpArr.extend(mjArr) # 建立一個麻將陣列的copy
    sptArr = seprateArr( tmpArr, hunMj )

    ndHunArr = [] # 每個分類需要混的陣列
    for i in range( 1, 5 ):
        g_NeedHunCount = 4
        getNeedHunInSub( sptArr[i], 0 )
        ndHunArr.append(g_NeedHunCount)


    jaNdHunArr = []#每個將分類需要混的陣列
    for i in range(1,5):
        jdNeedHunNum = getJiangNeedHum(sptArr[i])
        jaNdHunArr.append(jdNeedHunNum)

    #給一個混看能不能胡
    curHunNum = len( sptArr[0])+1
    tingArr = []


    #是否單調將
    isAllHu = False
    needNum = 0
    for i in range(0,4):
        needNum += ndHunArr[i]
    if curHunNum - needNum == 1:
        isAllHu = True
    if isAllHu:
        tingArr.extend(tmpArr)
        return  tingArr

    for i in range(0,4):
        setTmp = set(sptArr[i+1])
        for x in setTmp:
            t = []
            t.extend(sptArr[i+1])
            t.remove(x)
            # 將
            needNum = 0
            for j in range(0,4):
                if(i != j):
                    needNum = needNum + ndHunArr[j]
            if needNum <= curHunNum and x not in tingArr:
                if canHu(curHunNum-needNum,t):
                    tingArr.append(x)
            # print callTime

            # 撲
            for j in range(0,4):
                if len(sptArr[j+1]) == 0:
                    continue
                if(i != j):
                    needNum = 0
                    for k in range(0,4):
                        if(k != i):
                            if(k == j):
                                needNum += jaNdHunArr[k]
                            else:
                                needNum += ndHunArr[k]
                    if needNum <= curHunNum and x not in tingArr:
                        g_NeedHunCount = 4
                        getNeedHunInSub(t, 0 )
                        if g_NeedHunCount <= curHunNum - needNum:
                            tingArr.append(x)
                            # print str(callTime) + 10*'-'
    return tingArr


import datetime
# print 'reqOtherAction +2'
# samArr = [111, 111, 111, 111, 214, 214, 214, 214, 315, 315, 315, 315, 118, 119, 119 ]
# begin = datetime.datetime.now()
# print testHu(112, samArr, 111)
# end = datetime.datetime.now()
# print end-begin

# samArr = [405,104,104,107,107,203,204,205,207,105,107,108,109]
# getTingArr(samArr,405)
#testPengGang( 302,samArr, 403 ):
# hasGang( samArr, 115 )
#samArr = [101, 101,101,101,101,101,101,101,101,101,101,101,101,101,101,101,101 ]
#print testGang(101, samArr, 102)




majmap = {"101":"一萬","102":"二萬","103":"三萬","104":"四萬","105":"五萬","106":"六萬","107":"七萬","108":"八萬","109":"九萬",
          "201":"一餅","202":"二餅","203":"三餅","204":"四餅","205":"五餅","206":"六餅","207":"七餅","208":"八餅","209":"九餅",
          "301":"一條","302":"二條","303":"三條","304":"四條","305":"五條","306":"六條","307":"七條","308":"八條","309":"九條",
          "401":"東風","402":"西風","403":"南風","404":"北風","405":"紅中","406":"發財","407":"白板"}

if __name__ == "__main__":

    #####################################
    #測試胡牌
    # samArr = [111, 111, 111, 111, 214, 214, 214, 214, 315, 315, 315, 315, 118, 119, 119 ]
    # print testHu(112, samArr, 111)
    #####################################


    #####################################
    #測試具體能聽哪些牌
    samArr = [405,202,203,203,204,205,302,302,303,303]#執行時間:0:00:00.001100 <callTime:83>  [ 一餅 , 四餅 , 二條 , 三條 , 紅中 , ]
    # samArr = [405,202,203,203,204,205,301,302,303,303]#執行時間:0:00:00.000721 <callTime:72>  [ 一餅 , 四餅 , 三條 , 紅中 , ]
    # samArr = [405,202,203,203,204,205,301,302,302,303]#執行時間:0:00:00.000659 <callTime:72>  [ 一餅 , 四餅 , 二條 , 紅中 , ]
    # samArr = [405,203,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000887 <callTime:72>  [ 三餅 , 六餅 , 一條 , 四條 , 紅中 , ]
    # samArr = [405,202,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000715 <callTime:59>  [ 二餅 , 五餅 , 一條 , 四條 , 紅中 , ]
    # samArr = [405,202,203,203,204,301,302,302,303,303]#執行時間:0:00:00.000797 <callTime:72>  [ 三餅 , 一條 , 四條 , 紅中 , ]
    # samArr = [405,104,104,107,107,107,203,204,205,305,307,308,309]#執行時間:0:00:00.000791 <callTime:83>  [ 四萬 , 五條 , 三條 , 四條 , 六條 , 七條 , 紅中 , ]
    # samArr = [405,104,104,107,107,107,203,204,205,207,307,308,309]#執行時間:0:00:00.000844 <callTime:85>  [ 四萬 , 七餅 , 二餅 , 五餅 , 六餅 , 八餅 , 九餅 , 紅中 , ]
    # samArr = [405,104,105,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000799 <callTime:88>  [ 二餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,103,104,105,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000859 <callTime:89>  [ 二餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,103,104,105,106,107,108,202,202,302,304,306,306]#執行時間:0:00:00.000836 <callTime:88>  [ 二餅 , 三條 , 六條 , 紅中 , ]
    # #測試兩個癩子的執行時間
    # samArr = [405,405,103,105,106,107,108,202,202,302,304,306,306]#執行時間:0:00:00.000955 <callTime:136>  [ 四萬 , 二餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,405,103,105,106,107,108,201,202,302,304,306,306]#執行時間:0:00:00.000696 <callTime:94>  [ 四萬 , 三餅 , 三條 , 紅中 , ]
    # samArr = [405,405,103,105,106,107,108,201,202,302,303,306,306]#執行時間:0:00:00.000790 <callTime:94>  [ 四萬 , 三餅 , 一條 , 四條 , 紅中 , ]
    # #測試三個癩子的執行時間
    # samArr = [405,405,405,105,106,107,108,202,202,302,304,306,306]#執行時間:0:00:00.001111 <callTime:135>  [ 五萬 , 八萬 , 三萬 , 四萬 , 六萬 , 七萬 , 九萬 , 二餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,405,405,105,106,107,108,201,202,302,304,306,306]#執行時間:0:00:00.000926 <callTime:118>  [ 五萬 , 八萬 , 三萬 , 四萬 , 六萬 , 七萬 , 九萬 , 三餅 , 三條 , 六條 , 紅中 , ]
    # samArr = [405,405,405,105,106,107,108,201,202,302,303,306,306]#執行時間:0:00:00.000949 <callTime:116>  [ 五萬 , 八萬 , 三萬 , 四萬 , 六萬 , 七萬 , 九萬 , 三餅 , 一條 , 四條 , 六條 , 紅中 , ]
    # #測試四個癩子的執行時間
    # samArr = [405,405,405,405,106,107,108,202,202,302,304,306,306]#執行時間:0:00:00.000607 <callTime:18>  [ 一萬 , 二萬 , 三萬 , 四萬 , 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 一餅 , 二餅 , 三餅 , 四餅 , 五餅 , 六餅 , 七餅 , 八餅 , 九餅 , 一條 , 二條 , 三條 , 四條 , 五條 , 六條 , 七條 , 八條 , 九條 , 東風 , 西風 , 南風 , 北風 , 紅中 , 發財 , 白板 , ]
    # samArr = [405,405,405,405,106,107,108,201,202,302,304,306,306]#執行時間:0:00:00.000390 <callTime:18>  [ 一萬 , 二萬 , 三萬 , 四萬 , 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 一餅 , 二餅 , 三餅 , 四餅 , 五餅 , 六餅 , 七餅 , 八餅 , 九餅 , 一條 , 二條 , 三條 , 四條 , 五條 , 六條 , 七條 , 八條 , 九條 , 東風 , 西風 , 南風 , 北風 , 紅中 , 發財 , 白板 , ]
    # samArr = [405,405,405,405,106,107,108,201,202,302,303,306,306]#執行時間:0:00:00.000758 <callTime:18>  [ 一萬 , 二萬 , 三萬 , 四萬 , 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 一餅 , 二餅 , 三餅 , 四餅 , 五餅 , 六餅 , 七餅 , 八餅 , 九餅 , 一條 , 二條 , 三條 , 四條 , 五條 , 六條 , 七條 , 八條 , 九條 , 東風 , 西風 , 南風 , 北風 , 紅中 , 發財 , 白板 , ]
    #####################################

    #####################################
    #測試打出哪些牌能聽
    # tingNumArr = [405,202,203,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000704 <callTime:48>  [ 二餅 , 三餅 , 五餅 , 一條 , 二條 , 三條 , ]
    # tingNumArr = [405,104,104,107,107,107,203,204,205,207,305,307,308,309]#執行時間:0:00:00.000649 <callTime:61>  [ 七餅 , 五條 , ]
    # tingNumArr = [405,103,104,105,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000874 <callTime:82>  [ 三萬 , 六萬 , 九萬 , ]
    # #測試兩個癩子
    # tingNumArr = [405,405,203,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000424 <callTime:35>  [ 三餅 , 四餅 , 五餅 , 一條 , 二條 , 三條 , ]
    # tingNumArr = [405,405,104,107,107,107,203,204,205,207,305,307,308,309]#執行時間:0:00:00.000616 <callTime:63>  [ 四萬 , 七餅 , 五條 , ]
    # tingNumArr = [405,405,104,105,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000551 <callTime:48>  [ 四萬 , 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 二餅 , 四條 , 六條 , 二條 , ]
    # #測試三個癩子
    # tingNumArr = [405,405,405,203,204,205,301,302,302,303,303]#執行時間:0:00:00.000493 <callTime:27>  [ 三餅 , 四餅 , 五餅 , 一條 , 二條 , 三條 , ]
    # tingNumArr = [405,405,405,107,107,107,203,204,205,207,305,307,308,309]#執行時間:0:00:00.000518 <callTime:36>  [ 七萬 , 三餅 , 四餅 , 五餅 , 七餅 , 五條 , 七條 , 八條 , 九條 , ]
    # tingNumArr = [405,405,405,105,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000522 <callTime:53>  [ 五萬 , 六萬 , 七萬 , 八萬 , 九萬 , 二餅 , 四條 , 六條 , 二條 , ]
    # #測試四個癩子
    # tingNumArr = [405,405,405,405,204,205,301,302,302,303,303]#執行時間:0:00:00.000422 <callTime:24>  [ 四餅 , 五餅 , 一條 , 二條 , 三條 , ]
    # tingNumArr = [405,405,405,405,107,107,203,204,205,207,305,307,308,309]#執行時間:0:00:00.000604 <callTime:35>  [ 七萬 , 三餅 , 四餅 , 五餅 , 七餅 , 五條 , 七條 , 八條 , 九條 , ]
    # tingNumArr = [405,405,405,405,106,107,108,109,202,202,302,304,306,306]#執行時間:0:00:00.000678 <callTime:31>  [ 六萬 , 七萬 , 八萬 , 九萬 , 二餅 , 四條 , 六條 , 二條 , ]
    tingNumArr = [407,407,104,105,106,106,306,306,402,402,405,405,406,406]

    ##############################
    # 測試特殊情況  單一花色重複多次
    # tingNumArr = [405,201,201,201,201,202,202,202,202,205,205,205,205,209]#執行時間:0:00:00.006123 <callTime:356>  [ 五餅 , 九餅 , ]
    # tingNumArr = [405,405,202,204,205,205,206,206,207,208,208,208,209,306]#執行時間:0:00:00.002929 <callTime:358>  [ 二餅 , 九餅 , 六條 , ]
    # samArr = [405,201,201,201,201,201,202,202,202,202,204,204,205]#執行時間:0:00:00.016391 <callTime:1029>  [ 三餅 , 六餅 , 紅中 , ]
    # samArr = [405,405,201,201,201,201,202,202,202,202,204,204,209]#執行時間:0:00:00.026671 <callTime:1527>  [ 三餅 , 四餅 , 七餅 , 八餅 , 九餅 , 紅中 , ]
    ##############################

    global callTime
    callTime = 0
    begin = datetime.datetime.now()
    # 測試摸哪些牌能胡牌
    # tingArr = getTingArr(samArr,405)
    #測試打哪些牌能聽牌
    tingArr = getTingNumArr(tingNumArr,405)
    #測試摸到這張牌是不是能胡牌
    # tingArr = {}
    # print testHu(209, samArr, 405)
    # tingArr = []
    # for i in tingNumArr:
    #     tmp = []
    #     tmp.extend(tingNumArr)
    #     tmp.remove(i)
    #     getTingNumArr(tmp,405)
    end = datetime.datetime.now()
    runTime = end-begin
    rstr = "執行時間:" + str(runTime) + " <callTime:" + str(callTime) + ">  [ "
    for i in range(0,len(tingArr)):
        key = str(tingArr[i])
        rstr +=  majmap.get(key) + " , "
    rstr += "]"
    print  rstr