麻将胡牌算法

最近抽空学习了下麻将胡牌相关算法。其实今年自己开发了差不多4款麻将,但是止于兴趣,对公司麻将框架也只是浅尝则止,在实际开发中保持着够用的水平。不得不说,考虑到通用性,商业级别的框架拆得相当细致。加上C++大量的define,typeof减少了阅读障碍,当然前提是要理解开发人员的的命名习惯。

除了打表,麻将的算法基本就是拆牌递归,当然还有人提出的正则表达,在实际项目中还要考虑到番型的计算,可扩展性太差(其实是我不擅长这东西)。这里还是整理下通用的胡牌算法吧,对与x(ABC)+y(DDD)+EE && x+y=3的判断。

  1. 拆出EE,先找到将牌并排除
  2. 找出剩余手牌第一张中所有可能的牌组ABC,或者AAA。
  3. 分别对拆出ABC,AAA的剩余牌组重复2,3步骤
  4. 剩余手牌数为0就胡了

这段时间学习unity,试着用c#实现了下(还没拿大数据验证过:):

public struct HuComb
{
    public List<List<int>> groups;
    public List<FanType> typeids;
    public int fancount;

    public void countFan()
    {

    }
}

public class MahRule
{
    //获取所有牌(默认136)
    public bool GetAllMahs(ref List<int> mahs, int count)
    {
        //万,条,筒,东南西北风,中发白
        int[] tmpmahs = { 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 37, 38, 39, 41, 43, 45, 47, 51, 53, 55 };
        List<int> tmpAllMahs = new List<int>();
        foreach (int mah in tmpmahs)
        {
            for (int i = 0; i < 4 * count; i++)
            {
                tmpAllMahs.Add(mah);
            }
        }

        mahs = tmpAllMahs;

        return true;
    }
    //判断是否和
    public bool IsHu(List<int> mahs, int joker)
    {

        if (mahs.Count != 14) return false;

        mahs.Sort();

        List<HuComb> huCombs = new List<HuComb>();
        List<List<int>> groupJiang = new List<List<int>>();

        if (SplitJiang(mahs, joker, ref groupJiang))
        {
            foreach (List<int> item in groupJiang)
            {
                List<int> handsLeft = new List<int>(mahs);
                MahFunc.delmahs(handsLeft, item);
                List<int> normals = new List<int>();
                List<int> jokers = new List<int>();
                SplitJoker(handsLeft, joker, ref normals, ref jokers);

                List<List<int>> groups = new List<List<int>>();
                groups.Add(item);
                if (Split3N(normals, jokers, groups, huCombs))
                {
                    Console.WriteLine("Get it!");
                }
            }
        }
        else
        {
            return false;
        }


        return (huCombs.Count > 0);
    }

    //手牌中财神拆出
    public bool SplitJoker(List<int> hands, int joker, ref List<int> normals, ref List<int> jokers)
    {
        List<int> handsNormal = new List<int>(hands);
        handsNormal.RemoveAll(i => (i == joker));
        normals = handsNormal;
        List<int> handsJoker = new List<int>();
        int jokerCnt = hands.Count - handsNormal.Count;
        while (jokerCnt > 0)
        {
            handsJoker.Add(joker);
            joker--;
        }
        jokers = handsJoker;

        return true;
    }

    //拆出所有可能的将
    public bool SplitJiang(List<int> hands, int joker, ref List<List<int>> groups)
    {
        groups.Clear();
        List<int> normals = new List<int>();
        List<int> jokers = new List<int>();
        SplitJoker(hands, joker, ref normals, ref jokers);

        Dictionary<int, int> map = new Dictionary<int, int>();
        MahFunc.list_to_map(normals, ref map);

        List<int> tmp = new List<int>();
        foreach (int i in map.Keys)
        {
            if (map[i] >= 2)
            {
                tmp.Clear();
                tmp.Add(i);
                tmp.Add(i);
                groups.Add(tmp);
            }
        }

        if (jokers.Count == 1)
        {
            foreach (int i in map.Keys)
            {
                tmp.Clear();
                tmp.Add(i);
                tmp.Add(joker);
                groups.Add(tmp);
            }

        }

        if (jokers.Count == 2)
        {
            tmp.Clear();
            tmp.Add(joker);
            tmp.Add(joker);
            groups.Add(tmp);

        }

        return groups.Count > 0;
    }

    //拆拆出第一张牌所有的顺子和刻子
    public bool SplitGroup(List<int> handsnormal, List<int> handsjoker, ref List<List<int>> spgroups)
    {
        spgroups.Clear();
        List<int> cards = new List<int>(handsnormal);
        List<int> jokers = new List<int>(handsjoker);
        List<List<int>> groups = new List<List<int>>(spgroups);
        // 顺子
        for (int last = cards[0] + 2; last >= cards[0]; last++)
        {
            if (last % 10 < 3 || last / 10 >= 4)
            {
                continue;
            }

            int count = 0;
            for (int i = 0; i < 3; i++)
            {
                if (cards.Contains(last - i))
                {
                    ++count;
                }
            }
            if (count == 3 || count + jokers.Count >= 3)
            {
                List<int> groupChow = new List<int>();
                for (int i = 2; i >= 0; i--)
                {
                    if (cards.Contains(last - i))
                    {
                        groupChow.Add(last - i);
                        MahFunc.delmah(cards, (last - i));
                    }
                    else
                    {
                        groupChow.Add(jokers[0]);
                        jokers.RemoveAt(0);
                    }
                }
                groups.Add(groupChow);
            }

        }
        //刻子
        List<int> groupKe = new List<int>();
        groupKe.Add(cards[0]);
        if (cards[1] == cards[0])
        {
            groupKe.Add(cards[1]);
        }
        if (cards[2] == cards[0])
        {
            groupKe.Add(cards[2]);
        }
        if (groupKe.Count == 3 || groupKe.Count + jokers.Count >= 3)
        {
            MahFunc.delmahs(cards, groupKe);
            for (int i = 0; i < (3 - groupKe.Count); i++)
            {
                groupKe.Add(jokers[0]);
                jokers.RemoveAt(0);
            }
            groups.Add(groupKe);
        }
        spgroups = groups;

        return (spgroups.Count > 0);
    }

    //尝试拆清
    public bool Split3N(List<int> normals, List<int> jokers, List<List<int>> groups, List<HuComb> hucombs)
    {
        if (normals.Count % 3 + jokers.Count != 0)
        {
            return false;
        }
        if (normals.Count == 0 && jokers.Count == 0)
        {
            return true;
        }


        List<List<int>> allgroups = new List<List<int>>();
        SplitGroup(normals, jokers, ref allgroups);
        foreach (List<int> i in allgroups)
        {

            List<int> tmpnormals = new List<int>(normals);
            List<int> tmpjokers = new List<int>(jokers);
            foreach (int mah in i)
            {
                if (MahFunc.delmah(tmpnormals, mah))
                { }
                else
                {
                    MahFunc.delmah(tmpjokers, mah);
                }
            }
            List<List<int>> hugroups = new List<List<int>>(groups);
            hugroups.Add(i);
            if (Split3N(tmpnormals, tmpjokers, hugroups, hucombs))
            {
                HuComb tmpcomb = new HuComb();
                tmpcomb.groups = new List<List<int>>(hugroups);
                hucombs.Add(tmpcomb);
            }
        }

        return (hucombs.Count > 0);
    }

}