麻将胡牌算法(查表法 和 拆解法)

https://blog.csdn.net/weixin_40019413/article/details/78312501

/*
    1.查表法计算麻将胡牌(原理:http://hp.vector.co.jp/authors/VA046927/mjscore/mjalgorism.html)
    2.跟拆解法对比进行效率比较
*/
package main

import (
    "fmt"
    "time"
    "sort"
    "os"
    "encoding/json"
    "log"
    "runtime/debug"
    "io/ioutil"
    "io"
    "math/rand"
)

type jsonData struct {
    K int
    V int
}

var g_cards = []int {
    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, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91,  /* 东南西北中发白 */
}

func value2index(value int) int {
    if value < 0x31 {
        return ((value&0xF0)>>4)*9 + (value&0x0F) - 1
    } else {
        return 27 + ((value&0xF0)>>4) - 3
    }
}

func count(cards []int) []int {
    nums := make([]int, 34)
    for _, v := range cards {
        nums[value2index(v)]++
    }
    return nums
}

func calcKey(nums []int) int {
    p := -1
    x := 0
    b := false

    for i := 0; i < 3; i++ {
        for j := 0; j < 9; j++ {
            if (nums[i * 9 + j] == 0) {
                if (b) {
                    b = false
                    x |= 0x1 << uint32(p)
                    p++
                }
            } else {
                p++
                b = true
                switch (nums[i * 9 + j]) {
                    case 2:
                        x |= 0x3 << uint32(p)
                        p += 2
                    case 3:
                        x |= 0xF << uint32(p)
                        p += 4
                    case 4:
                        x |= 0x3F << uint32(p)
                        p += 6
                }
            }
        }
        if (b) {
            b = false
            x |= 0x1 << uint32(p)
            p++
        }
    }

    dong := value2index(0x31)
    bai := value2index(0x91)
    // 字牌
    for i := dong; i <= bai; i++ {
        if (nums[i] > 0) {
            p++
            switch (nums[i]) {
                case 2:
                    x |= 0x3 << uint32(p)
                    p += 2
                case 3:
                    x |= 0xF << uint32(p)
                    p += 4
                case 4:
                    x |= 0x3F << uint32(p)
                    p += 6
            }
            x |= 0x1 << uint32(p)
            p++
        }
    }
    return x
}

/* 可以是这种牌型11 111,虽然1不可能有5张,但是可以胡1的.且最多有一种牌是5张牌的组合 */
func checkIsValid(nums []int) bool {
    if (len(nums)%3 != 2) {
        return false
    }

    n := 0
    for _, v := range nums {
        if v > 5 {
            return false
        }
        if v == 5 {
            n++
        }
    }
    if n > 1 {
        return false
    }
    return true
}

func getCardsNum(nums []int) int {
    cardsNum := 0
    for _, v := range nums {
        if v > 0 {
            cardsNum += v
        }
    }
    return cardsNum
}

var printNum = 1
func encode(encodeData map[int]int, cards []int) {
    //sort.Sort(sort.IntSlice(cards))
    nums := count(cards)
    if checkIsValid(nums) {
        encodeData[calcKey(nums)] = 1
        if len(encodeData) / 100 == printNum && len(encodeData) % 100 == 0 {
            printNum++
            fmt.Println("len(map)=", len(encodeData))
        }
    }
}

func getPairs() [][]int {
    pairs := make([][]int, 0, len(g_cards))

    for _, v := range g_cards {
        pair := []int{v, v}
        pairs = append(pairs, pair)
    }

    return pairs
}

func getGroups() [][]int {
    groups := make([][]int, 0, len(g_cards)+(9-2)*3)

    // find three identical tiles
    for _, v := range g_cards {
        group := []int{v, v, v}
        groups = append(groups, group)
    }

    // find three sequence tiles
    for i := 2; i < len(g_cards); i++ {
        if g_cards[i-2]+1 == g_cards[i-1] && g_cards[i-1] == g_cards[i]-1 {
            group := []int{g_cards[i-2], g_cards[i-1], g_cards[i]}
            groups = append(groups, group)
        }
    }

    return groups
}

/* 将所有胡牌牌型编码成json数据 */
func encodeCards(pairs [][]int, groups [][]int) map[int]int {
    encodeData := make(map[int]int, 800)
    for _, p := range pairs {
        encode(encodeData, p)

        for _, a := range groups {
            var a_temp []int
            a_temp = append(a_temp, p...)
            a_temp = append(a_temp, a...)
            encode(encodeData, a_temp)

            for _, b := range groups {
                var b_temp []int
                b_temp = append(b_temp, a_temp...)
                b_temp = append(b_temp, b...)
                encode(encodeData, b_temp)

                for _, c := range groups {
                    var c_temp []int
                    c_temp = append(c_temp, b_temp...)
                    c_temp = append(c_temp, c...)
                    encode(encodeData, c_temp)

                    for _, d := range groups {
                        var d_temp []int
                        d_temp = append(d_temp, c_temp...)
                        d_temp = append(d_temp, d...)
                        encode(encodeData, d_temp)
                    }
                }
            }
        }
    }

    fmt.Println("-----------------七对----------------")
    //七对
    l := len(pairs)
    temp := make([]int, 14)
    for i := 0; i < l; i++ {
        temp = append(temp[:0], pairs[i]...)

        for j := i; j < l; j++ {
            temp = append(temp[:2], pairs[j]...)

            for m := i+1; m < l; m++ {
                temp = append(temp[:4], pairs[m]...)

                for n := m; n < l; n++ {
                    temp = append(temp[:6], pairs[n]...)

                    for x := m+1; x < l; x++ {
                        temp = append(temp[:8], pairs[x]...)

                        for y := x; y < l; y++ {
                            temp = append(temp[:10], pairs[y]...)

                            for u := x+1; u < l; u++ {
                                temp = append(temp[:12], pairs[u]...)
                                encode(encodeData, temp)
                            }
                        }
                    }
                }
            }
        }
    }
    return encodeData
}

func huCards2JSON() {
    begin := time.Now().UTC().UnixNano()

    pairs := getPairs()
    groups := getGroups()
    m := encodeCards(pairs, groups)

    f, err := os.Create("huCards.json")
    defer f.Close()

    if err != nil {
        log.Fatal("Create", err)
    }

    var jd jsonData
    enc := json.NewEncoder(f)
    fmt.Println("map len = ", len(m))

    for k, v := range m {
        jd.K = k
        jd.V = v

        if err := enc.Encode(jd); err != nil {
            log.Fatal("Encode", err)
        }
    }

    fmt.Printf(", 花费时间:%vs", float32(float32(time.Now().UTC().UnixNano() - begin)/1000000000))
}

/* 配置文件只有一个json数据,如:{"K":30319,"V":1}, 有多个是会出错 */
func readOneJson() {
    configPath := "huCards.json"
    data, err := ioutil.ReadFile(configPath)
    if err != nil {
        log.Printf("%v", err)
    }

    var jd jsonData
    err = json.Unmarshal(data, &jd)
    if err != nil {
        log.Printf("%v", err)
    }
    fmt.Println(jd)
}

func readAllJson() map[int]int {
    f, err := os.Open("huCards.json")
    defer f.Close()

    if err != nil {
        log.Printf("Open", err)
    }

    var jd jsonData
    dec := json.NewDecoder(f)
    m := make(map[int]int)
    for {
        if err := dec.Decode(&jd); err == io.EOF {
            break
        } else if err != nil {
            log.Printf("Decode", err)
        }
        m[int(jd.K)] = jd.V
    }
    return m
}

var totalCardNums = []int{2, 5, 8, 11, 14}
func randCards(allCards []int, n int) []int {
    if n <= 0 {
        n = totalCardNums[rand.Int31n(int32(len(totalCardNums)))]
    }
    allLen := len(allCards)
    var cards = make([]int, n)
    for i := 0; i < n; i++ {
        index := rand.Int31n(int32(allLen))
        cards[i] = allCards[index]
        allCards[allLen-1], allCards[index] = allCards[index], allCards[allLen-1]
        allLen--
    }
    return cards
}

/* 判断胡牌-拆解法(3N+2) */
func huSplitMethod(nums []int, cardsNum int) bool {
    length := len(nums)
    if cardsNum == 0 {
        return true
    }

    f := func(n int) bool {
        for i := 0; i < length; i++ {
            if nums[i] >= n {
                nums[i] -= n
                cardsNum -= n
                if huSplitMethod(nums, cardsNum) {
                    return true
                } else {
                    nums[i] += n
                    cardsNum += n
                }
            }
        }
        return false
    }

    //对子
    if cardsNum % 3 == 2 {
        return f(2)
    }

    //三张
    if f(3) {
        return true
    }

    //顺子
    loop := [][]int{{0, 7}, {9, 16}, {18, 25}}
    for j := 0; j < len(loop); j++ {
        for i := loop[j][0]; i < loop[j][1]; i++ {
            if nums[i] > 0 && nums[i+1] > 0 && nums[i+2] > 0 {
                nums[i] -= 1
                nums[i+1] -= 1
                nums[i+2] -= 1
                cardsNum -= 3
                if huSplitMethod(nums, cardsNum) {
                    return true
                } else {
                    nums[i] += 1
                    nums[i+1] += 1
                    nums[i+2] += 1
                    cardsNum += 3
                }
            }
        }
    }
    return false
}

/* 胡牌-七对判断 */
func huQiDui(nums []int) bool {
    for _, v := range nums {
        if v != 0 && v != 2 && v != 4 {
            return false
        }
    }
    return true
}

func IsHu(nums []int) bool {
    if !huQiDui(nums) {
        cardsNum := getCardsNum(nums)
        if cardsNum <= 0 || cardsNum > 14 || !checkIsValid(nums) || !huSplitMethod(nums, cardsNum) {
            return false
        }
    }
    return true
}

func test1_split(times int, allCards []int) {
    for i := 0; i < times; i++ {
        cards := randCards(allCards, 0)
        nums := count(cards)
        if IsHu(nums) {

        }
    }
}

func test1(times int, m map[int]int, allCards []int) {
    for i := 0; i < times; i++ {
        cards := randCards(allCards, 0)
        nums := count(cards)
        key := calcKey(nums)
        _, ok := m[key]
        if ok {

        }
    }
}

func test2(times int, m map[int]int, allCards []int) {
    cards := randCards(allCards, 14)
    //sort.Sort(sort.IntSlice(cards))
    nums := count(cards)
    for i := 0; i < times; i++ {
        key := calcKey(nums)
        _, ok := m[key]
        if ok {

        }
    }
}

func test3(times int, m map[int]int, allCards []int) {
    for i := 0; i < 9 ; i++ { //all yes
        cards := []int{0x01, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x09, 0x09}
        cards = append(cards, []int{i+1}...)
        sort.Sort(sort.IntSlice(cards))
        nums := count(cards)
        key := calcKey(nums)
        _, ok := m[key]
        if ok {
            fmt.Println("A can hu:", i+1)
        }
        if IsHu(nums) {
            fmt.Println("A can hu:", i+1)
        }
    }

    cardsTmp := [][]int{
        {0x21, 0x21}, //yes
        {0x21, 0x28}, //no
        {0x21, 0x21, 0x21, 0x21, 0x21}, //yes
        {0x21, 0x21, 0x21, 0x11, 0x12}, //no
        {0x91, 0x91, 0x22, 0x22, 0x22, 0x24, 0x25, 0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28}, //yes
        {0x11, 0x11, 0x11, 0x11, 0x22, 0x23, 0x24}, //no
        {0x21, 0x21, 0x21, 0x21, 0x22, 0X23, 0X37, 0X37}, //yes
        {0x21, 0x22, 0x23, 0x24, 0x25, 0X26, 0X33, 0X33, 0X33, 0X33}, //no
        {0x01, 0x01, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x02, 0x03, 0x04}, //yes
        {0x02, 0x02, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x01, 0x02, 0x03, 0x02, 0x03, 0x04}, //no
        {0x11, 0x11, 0x21, 0x21, 0x22, 0X22, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //yes
        {0x11, 0x11, 0x11, 0x21, 0x21, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //no
        {0x11, 0x11, 0x21, 0x21, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //yes
        {0x11, 0x11, 0x12, 0x12, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0x61, 0X07, 0X07}, //no
        {0x11, 0x11, 0x12, 0x12, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //yes
        {0x11, 0x11, 0x11, 0x11, 0X31, 0X31, 0X31, 0X31, 0X61, 0X61, 0X61, 0X61, 0X07, 0X07}, //yes
        {0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0X13, 0X13, 0X13, 0X13, 0X14, 0X14}, //yes
    }
    for i := 0; i < len(cardsTmp) ; i++ {
        cards := cardsTmp[i]
        sort.Sort(sort.IntSlice(cards))
        nums := count(cards)
        key := calcKey(nums)
        _, ok := m[key]
        if ok {
            fmt.Println("BB can hu:", i+1, "yes")
        } else {
            fmt.Println("BB cannot hu:", i+1, "no")
        }
        if IsHu(nums) {
            fmt.Println("BB can hu:", i+1, "yes")
        } else {
            fmt.Println("BB cannot hu:", i+1, "no")
        }
    }
}

func benchmark() {
    m := readAllJson()
    fmt.Printf("len(map)=%v\n", len(m))
    var allCards []int
    for i := 0; i < 4; i++ {
        allCards = append(allCards, g_cards...)
    }

    begin := time.Now().UTC().UnixNano()
    times := 10000000
    //test1(times, m, allCards)
    test1_split(times, allCards)
    //test2(times, m, allCards)
    //test3(times, m, allCards)

    fmt.Printf("计算胡牌%v次, 花费时间:%vs\n", times, float32(float32(time.Now().UTC().UnixNano() - begin)/1000000000))
}

func main(){
    defer func(){
        if err := recover(); err != nil {
            fmt.Println(err)
            fmt.Println(string(debug.Stack()))
            time.Sleep(time.Duration(3000)*time.Second)
        }
    }()
    fmt.Println("start...")

    //huCards2JSON()
    benchmark()

    fmt.Println("end...")
    time.Sleep(time.Duration(3000)*time.Second)
}

结论: 查表法效率稍高。拆解法简单方便,不需要预先生成表,但不稳定:不同的牌型,计算胡牌时消耗时间相差可能很大。查表法需要预先生成表,计算胡牌比较稳定

---------------------

本文来自 woshiyuanlei 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/woshiyuanlei/article/details/80944155?utm_source=copy 

猜你喜欢

转载自blog.csdn.net/qq_31967569/article/details/82850091