斗地主算法

时间:2022-05-05
本文章向大家介绍斗地主算法,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

       不得不承认,算法搁置了一些时间,代码的风格下降了好多!  贴上一个曹点多多且丑的代码!  Orz... 

题目要求:     

编码:3表示3点 ,4表示4点点  。。。。 10表示10点  11表示J   12表示Q   13表示K  14表示A   15表示2   16表示小王   17表示大王

要求:

1)出牌牌型包括(单子,对子, 三张,三带一,三带二,顺子,连对(3连对,4连对,5连对....),四张炸弹,四带一 ,四带二(四带2个单张,四带2个对子),对王炸弹)

2)初始化1副牌(54张),随机发一手牌(17张)显示出来

3)手动输入一个几张牌。判断输入牌是否是1)中的牌型。 程序根据牌型判断自己的手牌是否可以压该牌,如果可以压,则显示可以压的牌型,并输出,从手牌减去已压的牌。显示剩余的牌

4)循环过程要求3 直到牌出完。

代码:  

  1 #define Gxjun
  2 #define LOCAL
  3 #define Test
  4 #include<stdio.h>
  5 #include<string.h>
  6 #include<stdlib.h>
  7 #include<time.h>
  8 #define Arr_Len(arr)  sizeof(arr)/sizeof(arr[0])
  9 #define maxn 18
 10 
 11 
 12 int cod[maxn]={0};
 13 int mycod[maxn];
 14 int chu[maxn];
 15 int rtmp[maxn]={0} ,mtmp[maxn]={0};
 16 int mx  , mn ;
 17 
 18 typedef struct LianD
 19 {
 20     int en ; //连对结尾的牌号
 21     int du ; //最接近的度数
 22     int cnt ; //连对的个数
 23     int tem ; //临时存储的结尾牌号
 24     //复制值函数
 25     void copfun(struct LianD  *Ld){
 26 
 27         Ld->cnt=cnt;
 28         Ld->du=du;
 29         Ld->en=en;
 30         Ld->tem=tem;
 31     }
 32     //初始化
 33     void init(){
 34       en=du=cnt=tem=0;
 35     }
 36 };
 37 
 38 enum AlgTyNn{  ShunZi=1 ,
 39                LianDui
 40              };
 41 
 42 int cmp(const void * arg  , const void * brg){
 43 
 44    int * ar =(int *) arg ;
 45    int * br =(int *) brg ;
 46    return *ar - *br ;
 47 
 48 }
 49 
 50 #ifdef Test
 51 void init()
 52 {
 53   int i , cnt=0 ;
 54 
 55 
 56 #ifdef Test
 57   #ifndef LOCAL
 58    #define LOCAL
 59    #endif // LOCAL
 60 #endif // Test
 61 
 62 #ifdef LOCAL
 63    freopen("data.in","r",stdin);
 64 #endif // Test
 65   memset(mycod , 0 , sizeof(mycod));
 66   mycod[cnt]=17;
 67   cod[17]=cod[16]=1;   //一对王
 68   for(i=3 ; i<16 ;i++)
 69       cod[i]=4;
 70   while(++cnt<maxn){
 71      scanf("%d" ,&mycod[cnt]);
 72      cod[mycod[cnt]]--;
 73  }
 74  #ifdef Gxjun
 75   fclose(stdin);
 76   freopen("CON","r",stdin);
 77   #endif // Gxjun
 78   qsort(mycod+1 , cnt-1 , sizeof(mycod-1) , cmp);
 79 }
 80 
 81 #endif // Test
 82 
 83 #ifndef Test
 84 void init(){
 85 
 86   int i , hopg ,cnt=0 ;
 87   memset( mycod , 0 , sizeof(mycod) ) ;
 88   mycod[cnt]=17;
 89   for(i=3 ; i<16 ;i++)
 90       cod[i]=4;
 91   cod[16]=1;   //一对王
 92   cod[17]=1;
 93   srand(time(NULL));
 94 
 95   while(cnt<maxn){
 96 
 97     hopg = rand()%maxn ;
 98 
 99     if( hopg>0&&cod[hopg] > 0 ){
100       cod[hopg]--;
101       mycod[++cnt]=hopg;
102     }
103   }
104   qsort(mycod+1 , cnt-1 , sizeof(mycod-1) , cmp);
105 
106 }
107 #endif // Test
108 
109 //对方输出
110 void inputs(){
111 
112   int cnt=0;
113   memset(chu , 0 , sizeof(chu));
114    while(1){
115      scanf("%d",&chu[++cnt]);
116      if(chu[cnt]<0) break;
117   }
118    chu[0]=cnt-1 ;  //最开头放置纸牌的数目
119 
120 }
121 
122 //打印剩余
123 void output(){
124 
125     printf("  |   剩牌系: ");
126     //剩余牌系
127     int cont=0;
128     for(int j=3 ; j<maxn ;j++){
129         cont+=mtmp[j];
130         for(int k=0; k<mtmp[j] ; k++){
131            printf("%d ",j);
132         }
133     }
134      mtmp[0]=cont;
135      printf("t剩牌数: [ %d ]",mtmp[0]);
136      puts("");
137 }
138 
139 //计数排序
140 void connt(int *tmp , const int ss []){
141 
142    //memset(tmp ,0 ,sizeof(tmp));
143    tmp[0]=ss[0];
144   for(int i= 1 ; i<=ss[0] ; i++)
145         tmp[ss[i]]++;     //计数排序
146 }
147 
148 //判断是否有炸弹或者王炸
149 void ZhaDan(){
150 
151 for(int i=3 ; i<maxn ;i++)
152   if(mtmp[i]==4){
153     printf("%d %d %d %d ",i,i,i,i);
154     mtmp[i]-=4;
155     output();
156     return ;
157   }
158 
159  if(mtmp[16]==1&&mtmp[17]==1){
160      printf("%d %d ",16,17);
161      mtmp[16]=mtmp[17]=0;
162      output();
163      return ;
164  }
165    puts("没有牌能压过!");
166    return ;
167 }
168 
169 
170 //如果是连对处理方案
171 void AlgLDOrShZi(int var){
172 
173   int i=mn+1 ,len = mx-mn+1 ,kk=0;
174   struct LianD   Ld , Tmpld;
175 
176        Ld.init();
177     //时间复杂度为0(n^2)
178  for( i = mn+1 ; i<15 ; i++){
179         Tmpld.init();
180         Tmpld.tem=i;
181     for(int j=i ; j<i+len ; j++){
182      if(mtmp[j]>=var){
183 
184         if(mtmp[j]==var){
185             Tmpld.du++;
186         }
187          Tmpld.cnt++;
188      }else{
189           Tmpld.init();
190           Tmpld.tem=j+1;
191       }
192      if(Tmpld.cnt==len){
193          Tmpld.en =Tmpld.tem;
194          if(Ld.en==0||Ld.du<Tmpld.du)
195             Tmpld.copfun(&Ld);
196              break;
197      }
198     }
199  }
200   if(Ld.cnt == len){
201       puts("提示:");
202     for(i=Ld.en; i<Ld.en+len ;i++){
203           mtmp[i]-=var;
204           kk=0;
205     while(kk++<var)
206         printf("%d " ,i);
207     }
208      output();
209   }else{
210     //寻求炸弹或者王炸
211     ZhaDan();
212   }
213 
214 }
215 
216 //三带的处理情况
217 
218 void  AlgShanDai(){
219 
220   int pos=0 , cnt=0 , res=0,fcnt=0;
221   int mcnt=0 , mpos=0 ;
222   //分析牌型
223   for(int i=mn;i<=mx ;i++){
224     if(rtmp[i]==3){
225         pos=i;
226         cnt++;
227      }else if(rtmp[i]>0){
228           res+=rtmp[i];  //统计剩余牌的数量
229           fcnt++;
230      }
231     }
232     if((fcnt==cnt&&res%cnt==0)||(fcnt<cnt&&(fcnt*res==cnt||0==res))) ;
233     else{
234       puts("输出的牌不符合规则!请重新输出:");
235       return ;
236     }
237      res/=cnt;
238      for(int i=pos-cnt+2 ; i<=pos; i++){
239             if(rtmp[i]!=3){
240             puts("输出的牌不符合规则!请重新输出:");
241              return ;
242             }
243      }
244 
245   //如果为三带情况 即 cnt =1
246     for(int i=pos-cnt+2 ; i<17 ;i++){
247        if(mtmp[i]==3){
248             mpos=i;
249             mcnt++;
250        }else
251             mcnt=0;
252        if(mcnt==cnt) break;
253     }
254     //查询副牌是否能够满足
255     if(mcnt==cnt){
256       //说明有解决方案
257       int stpos = mpos - cnt +1 ;
258       int src[maxn]={0} ,tt=0;
259       bool tag = false;
260       for(int i=3 ; i<17 ;i++){
261         //满足不再连续范围之内的即可333444不能为3,4
262         if(i<stpos||i>mpos){
263           if(mtmp[i]>=res){
264              for(int kk=0 ; kk<mtmp[i] ;kk+=res){
265                   src[tt++]=i;
266                   if(tt==cnt){
267                     tag = true;
268                     break;
269                   }
270              }
271           }
272         }
273         if(tag)  break ;
274       }
275       if(tt==cnt){
276         //则解决方案为
277         int mstpos = mpos - cnt +1;
278         for(int i=mstpos ; i<=mpos ; i++){
279              printf("%d %d %d " ,i,i,i);
280              mtmp[i]-=3;
281         }
282         //打印副牌
283         for(int i=0; i<tt ;i++){
284           for(int k=0 ;k<res ;k++){
285               printf("%d ",src[i]);
286           }
287             mtmp[src[i]]-=res;
288          }
289            output();   //打印剩余牌
290       }
291     }else{
292 
293       //查询是否有炸弹
294       ZhaDan();
295     }
296 }
297 
298 //四带情况
299 void AlgSiDai(){
300     if(chu[0]>4)  ZhaDan();
301     else{
302        for(int i=chu[1]+1 ; i<15 ; i++){
303           if(mtmp[i]==4){
304                 printf("%d %d %d %d ",i,i,i,i);
305                 mtmp[i]-=4;
306                 output();
307               return ;
308         }
309     }
310    if(mtmp[16]==1&&mtmp[17]==1){
311      printf("%d %d ",16,17);
312       mtmp[16]=mtmp[17]=0;
313       output();
314       return ;
315 
316 
317 
318 
319 
320  }
321    puts("没有牌能压过!");
322    return ;
323   }
324 }
325 
326 
327 //对子的情况
328 void AlgDuiZi(){
329     for(int i=chu[1]+1 ; i<16 ;i++)
330     {
331         if(mtmp[i]>1&&mtmp[i]<4){
332             printf("%d %d n",i,i);
333             mtmp[i]-=2;
334             output();
335             return ;
336         }
337     }
338     ZhaDan();
339 }
340 
341 //对于个子的情况
342 void AlgGreZi(){
343 
344 for(int i=chu[1]+1 ; i<18 ;i++)
345     {
346         if(mtmp[i]>0&&mtmp[i]<4){
347             printf("%d n",i);
348             mtmp[i]-=1;
349             output();
350             return ;
351         }
352     }
353     ZhaDan();
354 }
355 
356 //查询对应的方案
357 
358 //对子
359 
360 bool IsDuiZi(){
361 
362   if(chu[0]==2)  //则必定是对子
363      return true;
364  return false ;
365 }
366 
367 //个子
368 bool IsGreZi(){
369 
370  if(chu[0]==1)  //则必定是对子
371      return true;
372 
373  return false ;
374 }
375 
376 //判断是否是顺子
377 bool IsShunZi(){
378 
379 //顺子的条件
380 if(chu[0]>4){
381 if((mx-mn+1==chu[0])&& mx<15)
382     return true ;
383 }
384 return false;
385 }
386 
387 //判断是否是连对
388 bool IsLianDui(){
389  if(chu[0]>5&&mx<15){
390     for(int i=mn; i<=mx ;i++)
391          if(rtmp[i]!=LianDui)
392             return false;
393    return true ;
394  }
395   return false;
396 }
397 
398 //判断是否是三带
399 bool IsShanDai(){
400 
401       for(int i=mn; i<=mx ;i++)
402          if(rtmp[i]==3)
403             return true;
404 
405   return false ;
406 }
407 
408 
409 
410 //判断是否是四带或者炸弹
411 bool IsSiDai(){
412 
413    for(int i=mn; i<=mx ;i++)
414     if(rtmp[i]==4)
415         return true;
416 
417     return false ;
418 }
419 
420 
421 //统计判断
422 void AlgMxn(){
423 
424       //求最大值,最小值
425   for(int i =3 ; i<= 17 ; i++)
426       if(rtmp[i]>0){
427           mn = i;
428           break;
429     }
430    for(int i =17 ; i>= 3 ; i--)
431       if(rtmp[i]>0){
432           mx = i;
433           break;
434       }
435 
436 }
437 
438 void print(){
439 
440  int i;
441  for(i=1; i<17; i++)
442     printf("%d ", mycod[i]);
443  printf("%dn", mycod[17]);
444 
445 }
446 
447 //检测出牌方
448 bool checked(){
449 
450   for(int i=1 ; i<maxn ;i++){
451      if(cod[i]<rtmp[i])
452         return false;
453   }
454 
455    if(IsGreZi()
456       ||IsDuiZi()
457       ||IsShunZi()
458       ||IsSiDai()
459       ||IsShanDai()){
460         for(int i=1; i<=chu[0] ;i++){
461             cod[chu[i]]--;
462         }
463       }
464       return true ;
465     //如果为一对王
466     if(chu[0]==2&&mtmp[16]==1&&mtmp[17]==1)
467         return true;
468   return false;
469 }
470 
471 int main(int argc , char * argv)
472 {
473 
474  init();
475 
476  memset(mtmp , 0 , sizeof(mtmp));
477 
478  connt(mtmp , mycod);
479  print();
480 
481  while(true){
482    printf("请出牌:n");
483   while(1){
484   inputs();
485   memset(rtmp , 0 ,sizeof(rtmp));
486   connt(rtmp , chu);
487   AlgMxn();
488   if(checked())   break;
489   else
490      puts("输出的牌不符合规则!请重新输出:");
491   }
492         //如果满足顺子
493     if(IsGreZi())
494           AlgGreZi();
495     else
496         if(IsDuiZi())
497              AlgDuiZi();
498     else
499         if(IsShunZi())
500              AlgLDOrShZi(ShunZi);
501     else
502         if(IsLianDui())
503              AlgLDOrShZi(LianDui);   //对于连对的情况
504     else
505         if(IsShanDai())
506              AlgShanDai();
507     else
508         if(IsSiDai())
509              AlgSiDai();
510 
511    if(mtmp[0]<1){
512       puts("恭喜你,win!");
513     break;
514    };
515  }
516  return 0;
517 
518 }