吴昊品游戏核心算法 Round 8(特别篇) —— 吴昊教你打麻将(和牌判定+递归)

 

  麻雀麻将马将蔴雀是一种源自中国的策略游戏。游戏参与者以四人居多,但也有二人、三人等变种(在日本、韩国较为常见)。麻将在各地的规则(尤其是计分方法)有很大不同,但基本目标都是通过一系列置换和取舍规则拼出某个特定组合的牌型,并阻止对手达成相同目的。游戏侧重技巧、策略运用和计算,但也涉及相当多的运气成份。比起扑克,麻将的组合方式更为变化多端,需要通过复杂的概率分析才能预测结果。但麻将需要记忆的规则和牌型也比一般扑克牌戏要多得多。在东亚与东南亚地区,特别是华人社区中,麻将常被当做娱乐或赌博手段。

  麻将的牌张,各地大同小异,但多与广东麻将相同,或为其子集。广东麻将是迄今仍流传的华人玩法之中,历史最悠久者,其牌张分三类。第 一类为序数牌(见附图),分“筒子/饼”、“索子/条”、“万子 /万”三门,每门有序数从一至九的牌各四张(三门共108只)。第二类是字牌,包括“东、南、西、北”四款“风牌”及“中、發、白”三款“三元牌”,每款 四张(七款共28张)。第三类是花牌,有“梅、兰、菊、竹、春、夏、秋、冬”八只。故全副麻将共计有144张。古代麻将有骨制、竹制或象牙制,现代麻将则多以塑胶制成。

  一副麻将除了牌张,还有骰子及其他道具,例如日本麻将有“点棒”,广东麻将就有一件称为“庄”的塑胶道具,用来识别庄家与显示“圈风”。

  好啦,不说废话了,在CSDN上有一位叫做shyworm(怕怕虫)的人开发了一种麻将和牌的判定程序,采用了递归的方法,根据不同的情形分别往下搜索。 他并没有实现七小对和十三幺的功能,这两个功能和判定函数的关系不大,可以独立出来,不过,我还是将其放置在里面了,并实现了这两个功能。这里基本上没有 用到过多的技巧吧,主要是递归的思想。缺陷是没有实现智能输入,这个也好办,暂时就不加了。

  Solve:

 

  1 /***************************************************************
  2   * 文件名:hu.cpp                                              *
  3   *                                                             *
  4   * 功  能:演示一个简洁明了的递归函数——判断[麻将]的和牌。    *
  5   *                                                             *
  6   * 说  明:1.  此函数不判断七对和十三幺(吴昊目前已经给出了),*
  7   *              读者不难自行判断;                             *
  8   *     同时由于麻将的规则各不相同,也请读者自己添加和修改。*
  9   *                                                             *
 10   *     2.  其他与麻将类似的游戏,如[字牌](又称跑胡子、    *
 11   *     二七十)等牌类游戏,也可采用类似的判断函数。        *
 12   *                                                             *
 13   * 环  境: VC 6.0,  但符合ANSI C标准,随便移植 ^_^             *
 14   *                                                             *
 15   * 作  者:shyworm(怕怕虫)&&haowu111(吴昊)                                     *
 16   * E_Mail: shyworm@sina.com                                    *
 17   ***************************************************************/
 18  
 19  #include <stdio.h>
 20  
 21  int Hu(int PAI[38]); //判定是否和牌的函数
 22  
 23  int Remain(int PAI[38]); //判定还剩下多少张牌
 24  
 25  int QIDUI(int PAI[38]);//七小对的判定
 26  
 27  int SHISANYAO(int PAI[38]);//十三幺的判定
 28  
 29  int main()
 30  {
 31    // 把一副牌放在下面的数组里,可以任意填入数字来测试函数正确与否。
 32    // 为了方便,PAI[0],PAI[10],PAI[20],PAI[30]都弃之不用,并且必须
 33    // 置为0,千万注意!这里是随意的一些数据,因为没有设置输入函数,可以在这里改进的
 34    int PAI[38] = { 0,
 35                    1,1,1,0,1,1,1,0,0,    // PAI[ 1- 9]  壹万~玖万的个数
 36                    0,
 37                    0,0,0,0,0,3,0,0,0,    // PAI[11-19]  壹铜~玖铜的个数
 38                    0,
 39                    0,0,0,0,0,0,0,0,0,    // PAI[21-29]  壹条~玖条的个数
 40                    0,
 41                    0,1,1,1,0,0,0         // PAI[31-37]  东南西北中发白的个数
 42                  };
 43    // 请务必先排除“七对” 和“十三幺”,由于简单,所以不提供了
 44    // if( QIDUI(PAI) )...
 45    // if( SHISANYAO(PAI) )...
 46    //这里吴昊已经给出了具体的办法,并设置在Hu(int PAI[38])函数中
 47    if( Hu(PAI) )  
 48      printf("win\n");
 49    else
 50      printf("not win\n");
 51    return 1;
 52  }
 53  
 54  // 判断和牌的递归函数,不考虑“七对” 和“十三幺”。因为如果
 55  // 把“七对” 和“十三幺”的判断放在递归函数里,将得不偿失。
 56  
 57  int Hu(int PAI[38])
 58  {
 59    QIDUI(PAI);
 60    SHISANYAO(PAI);
 61    int i;
 62    static int JIANG = 0;            // 将牌标志
 63    if( !Remain(PAI) ) return 1;     // 递归退出条件:如果没有剩牌,则和牌返回。
 64    for(i=1;!PAI[i]&&i<38;i++);  // 找到有牌的地方,i就是当前牌, PAI[i]是个数
 65    
 66    // 4张组合(杠子)
 67    if ( PAI[i] == 4 )               // 如果当前牌数等于4张
 68    {
 69      PAI[i] = 0;                  // 除开全部4张牌
 70      if( Hu(PAI) ) return 1;      // 如果剩余的牌组合成功,和牌
 71      PAI[i] = 4;                  // 否则,取消4张组合
 72    }
 73    // 3张组合(大对)
 74    if ( PAI[i] >= 3 )               // 如果当前牌不少于3张
 75    {
 76      PAI[i] -= 3;                 // 减去3张牌
 77      if( Hu(PAI) ) return 1;      // 如果剩余的牌组合成功,和牌
 78      PAI[i] += 3;                 // 取消3张组合
 79    }
 80    // 2张组合(将牌)
 81    if ( !JIANG && PAI[i] >= 2 )     // 如果之前没有将牌,且当前牌不少于2张,这里假定所有的牌都是可以作为将的
 82    {
 83      JIANG = 1;                   // 设置将牌标志
 84      PAI[i] -= 2;                 // 减去2张牌
 85      if( Hu(PAI) ) return 1;      // 如果剩余的牌组合成功,和牌
 86      PAI[i] += 2;                 // 取消2张组合
 87      JIANG = 0;                   // 清除将牌标志
 88    }
 89    if ( i > 30 )    return 0;       // “东南西北中发白”没有顺牌组合,不和
 90    
 91    // 顺牌组合,注意是从前往后组合!
 92    if( i%10 != 8 && i%10 != 9  &&   // 排除数值为8和9的牌
 93        PAI[i+1] && PAI[i+2] )      // 如果后面有连续两张牌
 94    {
 95      PAI[i]--;
 96      PAI[i+1]--;
 97      PAI[i+2]--;                  // 各牌数减1
 98      if( Hu(PAI) ) return 1;      // 如果剩余的牌组合成功,和牌
 99      PAI[i]++;
100      PAI[i+1]++;
101      PAI[i+2]++;                  // 恢复各牌数
102    }
103    // 无法全部组合,不和!
104    return 0;
105  }
106  
107  // 检查剩余牌数
108  int Remain(int PAI[38])
109  {
110    int sum = 0;
111    for(int i=1;i<38;i++)
112      sum += PAI[i];
113    return sum;
114  }
115 
116  int QIDUI(int PAI[38])
117  {
118    int count=0;
119    for(int i=0;i<38;i++)
120    {
121      if(PAI[i]>=2) count++;                 
122    }      
123    if(count==7return 1;
124    else return 0;
125  }
126  
127  int SHISANYAO(int PAI[38])
128  {
129    if(PAI[1]==1&&PAI[9]==1&&PAI[11]==1&&PAI[19]==1&&PAI[21]==1&&PAI[29]==1)
130    {
131      int count=0;  
132      for(int i=31;i<38;i++)
133      {
134        if(PAI[i]==1)
135        {
136          count++;             
137        }                     
138      }  
139      if(count==7)
140      {
141        return 1;            
142      }                                             
143    }    
144    return 0;
145  }


 
 

posted on 2013-02-27 21:30  吴昊系列  阅读(799)  评论(1)    收藏  举报

导航