帶萬(wàn)能牌的麻將和牌算法

該算法通過(guò)計(jì)算需要的萬(wàn)能牌個(gè)數(shù)延欠,和已經(jīng)擁有的萬(wàn)能牌個(gè)數(shù)對(duì)比來(lái)判斷是否胡牌陌兑,算法通用,且效率高由捎,計(jì)算胡牌算法每秒鐘可以運(yùn)行三百萬(wàn)次以上话肖。以下是算法的具體內(nèi)容。

1. 所有合法牌:

0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09  萬(wàn)
0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19  條
0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29  筒
0x31 0x32 0x33 0x34 0x35 0x36 0x37            東西南北中發(fā)白

2. 核心算法

將萬(wàn)遇汞、條卵洗、筒、風(fēng)各種花色分開(kāi)心肪,然后分別計(jì)算每種花色構(gòu)成整撲(整撲即三張相同牌或者順子)還需要多少?gòu)埲f(wàn)能牌锭亏。如:0x01、0x01硬鞍、0x02慧瘤、0x03戴已、0x04 構(gòu)成整撲需要一張萬(wàn)能牌, 0x01锅减、0x03糖儡、0x05、0x07構(gòu)成整撲需要兩張萬(wàn)能牌上煤。
計(jì)算整撲按照從小到大的順序一次計(jì)算休玩。

  1. 首先檢查是否是刻子(三張相同的牌)。
  2. 然后檢測(cè)檢查是否是對(duì)子劫狠。
    如果是對(duì)子拴疤,檢測(cè)它是否符合A2BxCy,xyz為張數(shù)独泞,
    如果x>0,y>1或者x>1,y>0呐矾,則分別檢測(cè)A成對(duì)子和A成順子需要的萬(wàn)能牌張數(shù)。否則懦砂,則A組合成對(duì)子來(lái)計(jì)算蜒犯。
  3. 如果是單張牌,則檢測(cè)其是否可以與后面的牌組合成順子荞膘,如果不能則檢測(cè)是否能組合成吃牌罚随,例如一萬(wàn)二萬(wàn),一萬(wàn)三萬(wàn)這種組合羽资,如果還是不能淘菩,就是一張單獨(dú)的牌。
  4. 風(fēng)牌判斷比較簡(jiǎn)單屠升,這里就不贅述了潮改。

計(jì)算完成后,依次遍歷將牌在萬(wàn)腹暖,條汇在,筒,風(fēng)的情況下脏答,它們構(gòu)成整撲加將需要的萬(wàn)能牌個(gè)數(shù)糕殉,然后對(duì)比擁有的萬(wàn)能牌個(gè)數(shù),便可以得出是否可以胡牌以蕴。

3. 邏輯代碼

判斷字牌(萬(wàn)糙麦,條,筒)組合成整撲需要的萬(wàn)能牌個(gè)數(shù)丛肮。

func SwitchCardToIndex(card uint8) uint8 {
    return 9 * (card / 16) + card % 16 - 1
}

func SwitchCardsToIndexList(cards []uint8) []uint8 {
    indexList := make([]uint8, 34)
    for _, card := range cards {
        index := SwitchCardToIndex(card)
        indexList[index] ++
    }
    return indexList
}

func CheckZiPaiZhengPu(cardsIndex []uint8) int {
    tmpCardsIndex := cardsIndex
    needMagicNum := 0
    curPos := 0
    for curPos < len(tmpCardsIndex){
        if tmpCardsIndex[curPos] >= 3 {
            tmpCardsIndex[curPos] -= 3
        } else if tmpCardsIndex[curPos] == 2 {
            if tmpCardsIndex[curPos+1] > 0 && tmpCardsIndex[curPos+2] > 0 && (tmpCardsIndex[curPos+1]>1 || tmpCardsIndex[curPos+2]>1){
                shunCardsIndex := make([]uint8, len(tmpCardsIndex))
                copy(shunCardsIndex, tmpCardsIndex)
                shunCardsIndex[curPos] --
                shunCardsIndex[curPos+1] --
                shunCardsIndex[curPos+2] --
                shunNeedNum := CheckZiPaiZhengPu(shunCardsIndex)
                duiCardsIndex := tmpCardsIndex
                duiCardsIndex[curPos] -= 2
                duiNeedNum := CheckZiPaiZhengPu(duiCardsIndex) + 1
                if duiNeedNum > shunNeedNum {
                    return shunNeedNum + needMagicNum
                }
                return duiNeedNum + needMagicNum
            } else {
                needMagicNum ++
                tmpCardsIndex[curPos] -= 2
            }
        } else if tmpCardsIndex[curPos] == 1{
            if tmpCardsIndex[curPos+1] >= 1 && tmpCardsIndex[curPos+2] >= 1 {
                tmpCardsIndex[curPos] --
                tmpCardsIndex[curPos+1] --
                tmpCardsIndex[curPos+2] --
            } else if tmpCardsIndex[curPos+1] >= 1 {
                needMagicNum ++
                tmpCardsIndex[curPos] --
                tmpCardsIndex[curPos+1] --
            } else if tmpCardsIndex[curPos+2] >= 1 {
                needMagicNum ++
                tmpCardsIndex[curPos] --
                tmpCardsIndex[curPos+2] --
            } else {
                needMagicNum += 2
                tmpCardsIndex[curPos] --
            }
        } else {
            curPos ++
        }
    }
    return needMagicNum
}

判斷(萬(wàn),條魄缚,筒)組合成整撲需要的萬(wàn)能牌個(gè)數(shù)宝与。

func CheckFengPaiZhengPu(cardsIndex []uint8) int {
    tmpCardsIndex := cardsIndex
    needMagicNum := 0
    curPos := 0
    for curPos < 7{
        if tmpCardsIndex[curPos] >= 3 {
            tmpCardsIndex[curPos] -= 3
        } else if tmpCardsIndex[curPos] == 2 {
            needMagicNum ++
            tmpCardsIndex[curPos] -= 2
        } else if tmpCardsIndex[curPos] == 1{
            needMagicNum += 2
            tmpCardsIndex[curPos] --
        } else {
            curPos ++
        }
    }
    return needMagicNum
}

檢測(cè)是否可以胡牌

func CanHuPai(handCardIndex []uint8, magicCard uint8) bool {
    magicIndex := SwitchCardToIndex(magicCard)
    tmpCardIndex := make([]uint8, len(handCardIndex))
    copy(tmpCardIndex, handCardIndex)
    magickNum := tmpCardIndex[magicIndex]

    if magickNum >= 3 {
        return true
    }

    tmpCardIndex[magicIndex] = 0
    tmpIndexList := make([]uint8, 11)

    //分析萬(wàn),條焚廊,筒
    //ziPaiType := []string{"萬(wàn)", "條", "筒"}
    ziPaiNeedMagicNums := make([]int, 3)
    for i:=0; i<3; i++ {
        copy(tmpIndexList, tmpCardIndex[i*9:(i+1)*9])
        tmpResult := CheckZiPaiZhengPu(tmpIndexList)
        ziPaiNeedMagicNums[i] = tmpResult
    }

    //分析風(fēng)
    copy(tmpIndexList, tmpCardIndex[27:34])
    fengPaiNeedMagicNum := CheckFengPaiZhengPu(tmpIndexList)

    //jiangType := []string{"萬(wàn)", "條", "筒"}
    totalNeedMagick := ziPaiNeedMagicNums[0] + ziPaiNeedMagicNums[1] + ziPaiNeedMagicNums[2] + fengPaiNeedMagicNum

    //所有牌都可以成整樸,將必定在財(cái)神牌上
    if int(magickNum) - totalNeedMagick >= 0 {
        //fmt.Println("將在------",jiangType[ziPaiType])
        return true
    }

    //將在萬(wàn),條习劫,筒中
    for ziPaiType:=0; ziPaiType<3; ziPaiType++{
        leftMagicNum := int(magickNum) + ziPaiNeedMagicNums[ziPaiType] - totalNeedMagick
        if leftMagicNum >= 0 {
            for eyeIndex:=0; eyeIndex<9; eyeIndex ++ {
                copy(tmpIndexList, tmpCardIndex[ziPaiType * 9:(ziPaiType+1) * 9])
                if tmpIndexList[eyeIndex] >= 2 {
                    tmpIndexList[eyeIndex] -= 2
                    tmpNeedMagic := CheckZiPaiZhengPu(tmpIndexList)
                    if leftMagicNum >= tmpNeedMagic {
                        return true
                    }
                } else if tmpIndexList[eyeIndex] == 1 && leftMagicNum > 0{
                    tmpNeedMagic := 1
                    tmpIndexList[eyeIndex] --
                    tmpNeedMagic += CheckFengPaiZhengPu(tmpIndexList)
                    if leftMagicNum >= tmpNeedMagic {
                        return true
                    }
                }
            }
        }
    }

    //將在風(fēng)中
    leftMagicNum := int(magickNum) + fengPaiNeedMagicNum - totalNeedMagick
    if leftMagicNum >= 0 {
        for eyeIndex:=0; eyeIndex<7; eyeIndex ++ {
            copy(tmpIndexList, tmpCardIndex[27:34])
            if tmpIndexList[eyeIndex] >= 2 {
                tmpIndexList[eyeIndex] -= 2
                tmpNeedMagic := CheckFengPaiZhengPu(tmpIndexList)
                if leftMagicNum >= tmpNeedMagic {
                    return true
                }
            } else if tmpIndexList[eyeIndex] == 1 && leftMagicNum > 0{
                tmpNeedMagic := 1
                tmpIndexList[eyeIndex] --
                tmpNeedMagic += CheckFengPaiZhengPu(tmpIndexList)
                if leftMagicNum >= tmpNeedMagic {
                    return true
                }
            }
        }
    }
    return false
}

4. 測(cè)試代碼

func main() {
    if err:=recover(); err != nil {
        fmt.Println(err)
    }
    //cardsIndex := SwitchCardsToIndexList([]uint8{0x11,0x17,0x12,0x11,0x17,0x12,0x13,0x13,0x21,0x21,0x22,0x22,0x22,0x23,0x23,0x26,0x26})
    //cardsIndex := SwitchCardsToIndexList([]uint8{0x12,0x12,0x13,0x13,0x13,0x14,0x15,0x16,0x02,0x02,0x03,0x03,0x03,0x04,0x05,0x06,0x26,0x26})
    cardsIndex := SwitchCardsToIndexList([]uint8{0x11,0x11,0x12,0x13,0x13,0x22,0x22,0x23,0x24,0x26,0x26})
    isHu := CanHuPai(cardsIndex, 0x26)
    fmt.Println(isHu)
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咆瘟,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子诽里,更是在濱河造成了極大的恐慌袒餐,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谤狡,死亡現(xiàn)場(chǎng)離奇詭異灸眼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)墓懂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)焰宣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人捕仔,你說(shuō)我怎么就攤上這事匕积。” “怎么了榜跌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵闪唆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我钓葫,道長(zhǎng)悄蕾,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任瓤逼,我火速辦了婚禮笼吟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霸旗。我一直安慰自己贷帮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布诱告。 她就那樣靜靜地躺著撵枢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪精居。 梳的紋絲不亂的頭發(fā)上锄禽,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音靴姿,去河邊找鬼沃但。 笑死,一個(gè)胖子當(dāng)著我的面吹牛佛吓,可吹牛的內(nèi)容都是我干的宵晚。 我是一名探鬼主播垂攘,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼淤刃!你這毒婦竟也來(lái)了晒他?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤逸贾,失蹤者是張志新(化名)和其女友劉穎陨仅,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體铝侵,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灼伤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哟沫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饺蔑。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嗜诀,靈堂內(nèi)的尸體忽然破棺而出猾警,到底是詐尸還是另有隱情,我是刑警寧澤隆敢,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布发皿,位于F島的核電站,受9級(jí)特大地震影響拂蝎,放射性物質(zhì)發(fā)生泄漏穴墅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一温自、第九天 我趴在偏房一處隱蔽的房頂上張望玄货。 院中可真熱鬧,春花似錦悼泌、人聲如沸松捉。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)隘世。三九已至,卻和暖如春鸠踪,著一層夾襖步出監(jiān)牢的瞬間丙者,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工营密, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留械媒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓评汰,卻偏偏與公主長(zhǎng)得像滥沫,于是被迫代替她去往敵國(guó)和親侣集。 傳聞我的和親對(duì)象是個(gè)殘疾皇子键俱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容