麻将查听算法

麻将通常有13张牌,在打的时候随着吃,碰越来越少。总数应该维持在3*n + 1,n=0~4,比如1张,4张,7张或10张。胡牌时,加一张(可能是自摸或吃碰来的一张)组成n个顺子或暗刻,外加一个麻将对。

这里要研究的要点是:

1. 给出3n + 2张牌如何判断是否已经胡牌,所有的胡牌可能组合;

2. 如果给出3n+1张牌如何判断是否已经挺牌,挺哪些牌。

这两个问题其实主要是第一个问题,也就是如何判断3n +2 张牌是否胡牌的问题。后者可以简单地通过实验加34种麻将牌之一看是否胡牌来判断是否挺牌,以及挺哪些牌。

 

如何判断3n +2张牌是否胡牌

麻将牌包括:

1条~9条

1万~9万

1饼~9饼

东西南北中发白

共34种牌,34×4=136张牌。

给每张牌设一个编号

1条~9条     -------à 0 ~8

1万~9万     ------à 9~17

1饼~9饼     ------à 18~26

东西南北中发白    -----à 27~33

 

1 牌的存储

设一个宏,就是牌的种类

#define MAX_TILE_INDEX 34

所摸的牌就可以存在一个长度为34的数组中

int tiles[MAX_TILE_INDEX];

数组的每个成员最大值为4,因为每张牌的总数为4,就算摸到暗杠也不过是4. 所有数组成员加起来应该是3n + 2

 

2 结果的存储

每个胡牌必定是若干顺子/暗刻,外加一个麻将对,用一个简单的结构或类大概就是:

{

         int nSequence[4][3];

         int nPair[2];  //或直接nPair,不用数组就可以表示麻将对了

}

 

给定的3n + 2张牌普通只胡一种情况,但特殊情况也可能有多种胡法,比如4个一万,4个两万,4个3万,2个四万

胡牌至少可以:

i. 4个1万,2万和3万的顺子,外加4万的麻将对;

ii. 1个1万,2万和3万的顺子,1万暗刻,2万暗刻,3万暗刻,以及4万麻将对。

这些结果都应该被存起来,胡牌应该以最大番数计算。

C++可以用一个vector模板来实现结果列表,java可以考虑用哈希表。

 

3. 胡牌判断算法

判断胡牌与否

判断胡牌与否的过程:

3.1. 首先判断总牌数是否为3n + 2,如果不是肯定不胡牌

3.2. 遍历所有牌,找到所有一种牌数量大于2的情况,也就是ntile[index] >= 2,然后把这两张牌(对子)去掉,记录到结果中的nPair。然后就剩下3n张牌了,再判断剩下的3n张牌是否能组成顺子或是暗刻,如果可以全部组成,那么这把牌就胡了,否则需要遍历一下把其他牌当对子的可能性。

关于第二步中如何判断3n 张牌可否组成顺子或暗刻

从第一张(种)牌开始往后检查,每张牌有5种可能, 0, 1, 2,3,4。如果是0,直接检查下一张(种);

i. 如果是1张或两张,要胡牌的话他(们)必须和后面两张组成顺子,如果不能组成顺子,肯定不胡。如果可以组成顺子,把顺子牌取出,存入临时结果,接着处理剩下的牌;

ii. 如果是3张,要胡牌有两种可能,一是3张当作一个暗刻,还有一种可能是这3张都与后面的牌组成顺子。这里有检查这两种情况。比如3个一万,3个两万和3个三万。既可以以三暗刻算,也可以按三个顺子算。这两种在最后算番的时候不一样。

iii. 如果是4张,胡牌的话必须要跟后面两张牌组成一个顺子,然后本张(种)就剩3种了,然后继续2.2的步骤就可以了。

整个检查过程可以用一个函数递归调用就可以了,每次处理一张,如果不能凑成顺子或暗刻的话就推出返回错误,如果函数处理时总牌数为0,则所有牌都已处理完了,返回成功,结果也已经存在结果里了,把结果加入结果列表。

4. 挺牌检查

挺牌时应该是3*n + 1张牌。遍历34种牌,加入3n +1 ,这时就是3n+2了,根据刚才的算法算是否胡牌,如果胡牌,刚加入的那张牌就是挺牌。

 

5. 测试过程与结果

1. 挺牌检查 –选用了非常复杂的九莲宝灯,也就是3个一万(或条,饼),3个九万(或条饼),其他2万到8万(或条饼)。这种牌是挺从1万到9万,共9张挺牌。算法可以正确算出。

2. 胡牌检查 –选用了4个一万,4个二万,4个三万,以及2个4万,算法正确列出了3种胡牌结果。

 

原帖:http://blog.sina.com.cn/s/blog_7213e0310101dq3w.html

后面补了一遍源码实现 http://blog.csdn.net/u014261855/article/details/54987832

 

根据前面算法介绍,这里来实现核心部分

中间用到的一些功能函数,大家自己搞定吧,我把核心算法po出来大家参考

一、数据结构设计c#

 

算法流程:轮流判断每一张牌是否符合要求

 

找出所有可以当将的牌

while(将牌没遍历完)

{

    去除将牌,提取数组byte[] cardgroup

    while(数组不为空)

    {

          取出第一张牌byte  card  =  cardgroup[0];

          确认牌张数 int count = GetCardCount(card);//功能函数

          If(判断数组是不是空的)

          {

                 如果是空的break

         }

         else

         {

              If(判断数组中是否含有顺子或者刻子(三张))

               {

                    //根据张数处理

                    If(count  ==  1)

                     {
                         If(判断card能否组成顺子)
                         {

                              能就把这个顺子从牌组中删掉

                          }

                         else

                        {

                               break

                        }

                    }            

                               
                   If(count  ==  2)

                    {

                               If(判断card能否组成2个顺子)

                               {

                                        能就把这2个顺子从牌组中删掉

                                }

                              else

                               {

                                      break

                               }

                    }

 

                    If(count  ==  3)

                    {

                             If(判断card能否组成3个顺子)

                             {

                                      能就把这3个顺子从牌组中删掉

                               }

                              else

                             {

                                      不能就把这个刻子删掉

                              }

                      }

 

                      If(count  ==  4)

                      {

                             If(判断card能否组成4个顺子)

                             {

                                    能就凑够12张了,一定可以胡

                                    return true

                             }

                             else

                             {

                                       If(能否拿出一张和后面的牌组成顺子)

                                       {

                                              能就把这个顺子和刻子都删掉

                                       }

                                       else

                                       {

                                               否则break

                                        }

                           }

                     }

                }

               else

                {

                       //数组不为空,没有顺,没有刻,一定没胡

                       如果没有break

                }

           }

        }

 

         数组为空或者while被break到这里

         If(判断cardgroup是否为空(全部完整移除))

         {

                 是就表示胡return  true

          }

}

 

二、实现

 

    bool TingCard(byte[] JiangArray, byte[] Cards, int type)
    {
        byte[] cardgroup = Cards;
        while (JiangCount < JiangNumber)
        {
            byte[] jiang = new byte[2];//两张一样的
            jiang[0] = JiangArray[JiangCount];
            jiang[1] = JiangArray[JiangCount];
            JiangCount++;

            cardgroup = GameLogic.DeleteArr(jiang, Cards);//DeleteItem(jiang, Cards);
            if (cardgroup == null) return true;
            cardgroup = GameLogic.SortCard1(cardgroup);//Sort(cardgroup);

            while (!IsNilGrop(cardgroup))
            {
                if (IsHaveLine(cardgroup) || IsHaveThree(cardgroup))
                {
                    byte card = cardgroup[0];
                    int cardcount = GetCardCount(card, cardgroup);
                    if (cardcount == 1)
                    {
                        byte [] line = GetLine(cardgroup,card,1);
                        if (line!= null)
                        {
                            cardgroup = GameLogic.DeleteArr(line, cardgroup);
                        }
                        else
                        {
                            break;
                        }
                    }
                    else if (cardcount == 2)
                    {
                        byte[] line = GetLine(cardgroup, card,2);
                        if (line != null)
                        {
                            cardgroup = GameLogic.DeleteArr(line, cardgroup);
                            cardgroup = GameLogic.DeleteArr(line, cardgroup);
                        }
                        else
                        {
                            break;
                        }
                    }
                    else if (cardcount == 3)
                    {
                        byte[] line = GetLine(cardgroup, card, 3);
                        if (line != null)
                        {
                            cardgroup = GameLogic.DeleteArr(line, cardgroup);
                            cardgroup = GameLogic.DeleteArr(line, cardgroup);
                            cardgroup = GameLogic.DeleteArr(line, cardgroup);
                        }
                        else
                        {
                            cardgroup = GameLogic.DeleteArr(new byte[] {card, card, card}, cardgroup);
                        }
                    }
                    else if (cardcount == 4)
                    {
                        byte[] line = GetLine(cardgroup, card, 4);
                        if (line != null) return true;
                        else
                        {
                            byte[] line1 = GetLine(cardgroup, card, 1);
                            if (line1 != null)
                            {
                                cardgroup = GameLogic.DeleteArr(line1, cardgroup);
                                cardgroup = GameLogic.DeleteArr(new byte[] { card, card, card }, cardgroup);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                }
                else
                {
                    break;
                }
            }

            if (IsNilGrop(cardgroup))
            {
                return true;
            }
        }
        return false;

    }

 

只上了核心部分代码,这时候有的小伙伴会发现,当cardcount == 3的时候,如果可以组成3个顺子就直接把三个顺子删掉,如果出现其中一个要和其他牌组成顺子的话,会不会报错,放心吧不会的!这个特殊情况是不存在的,自己摆一摆就知道啦

 

后来自己发现了一个漏洞,就是没有考虑到添加完了有5张的情况,要加个break,不然会死循环

你可能感兴趣的:(麻将查听算法)