带万能牌的麻将和牌算法

该算法通过计算需要的万能牌个数,和已经拥有的万能牌个数对比来判断是否胡牌,算法通用,且效率高,计算胡牌算法每秒钟可以运行三百万次以上。以下是算法的具体内容。

1. 所有合法牌:

0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09  万
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            东西南北中发白

2. 核心算法

将万、条、筒、风各种花色分开,然后分别计算每种花色构成整扑(整扑即三张相同牌或者顺子)还需要多少张万能牌。如:0x01、0x01、0x02、0x03、0x04 构成整扑需要一张万能牌, 0x01、0x03、0x05、0x07构成整扑需要两张万能牌。
计算整扑按照从小到大的顺序一次计算。

  1. 首先检查是否是刻子(三张相同的牌)。
  2. 然后检测检查是否是对子。
    如果是对子,检测它是否符合A2BxCy,xyz为张数,
    如果x>0,y>1或者x>1,y>0,则分别检测A成对子和A成顺子需要的万能牌张数。否则,则A组合成对子来计算。
  3. 如果是单张牌,则检测其是否可以与后面的牌组合成顺子,如果不能则检测是否能组合成吃牌,例如一万二万,一万三万这种组合,如果还是不能,就是一张单独的牌。
  4. 风牌判断比较简单,这里就不赘述了。

计算完成后,依次遍历将牌在万,条,筒,风的情况下,它们构成整扑加将需要的万能牌个数,然后对比拥有的万能牌个数,便可以得出是否可以胡牌。

3. 逻辑代码

判断字牌(万,条,筒)组合成整扑需要的万能牌个数。

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
}

判断(万,条,筒)组合成整扑需要的万能牌个数。

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
}

检测是否可以胡牌

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)

    //分析万,条,筒
    //ziPaiType := []string{"万", "条", "筒"}
    ziPaiNeedMagicNums := make([]int, 3)
    for i:=0; i<3; i++ {
        copy(tmpIndexList, tmpCardIndex[i*9:(i+1)*9])
        tmpResult := CheckZiPaiZhengPu(tmpIndexList)
        ziPaiNeedMagicNums[i] = tmpResult
    }

    //分析风
    copy(tmpIndexList, tmpCardIndex[27:34])
    fengPaiNeedMagicNum := CheckFengPaiZhengPu(tmpIndexList)

    //jiangType := []string{"万", "条", "筒"}
    totalNeedMagick := ziPaiNeedMagicNums[0] + ziPaiNeedMagicNums[1] + ziPaiNeedMagicNums[2] + fengPaiNeedMagicNum

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

    //将在万,条,筒中
    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
                    }
                }
            }
        }
    }

    //将在风中
    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. 测试代码

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)
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354

推荐阅读更多精彩内容