洗牌、发牌算法 (打乱扑克牌顺序)

 
#include 
#include 
#include 
int  d[6];
int  i,n,a,b,t;
int  c,j;
void  main() {
     srand ( time (NULL));
     printf ( "shuffle 0..n-1 demo\n" );
     for  (n=1;n<=5;n++) { /* 测试1~5个元素 */
         printf ( "_____n=%d_____\n" ,n);
         j=1;
         for  (c=1;c<=n;c++) j=j*c; /* j为n! */
         j*=n*2;
         for  (c=1;c<=j;c++) { /* 测试n*2*n!次 */
             for  (i=0;i /* 填写0~n-1 */
             for  (i=n;i>0;i--) { /* 打乱0~n-1 */
                 a=i-1;b= rand ()%i;
                 if  (a!=b) {t=d[a];d[a]=d[b];d[b]=t;}
             }
             printf ( "%04d:" ,c);
             for  (i=0;i printf ( "%d" ,d[i]);
             printf ( "\n" );
         }
     }
     printf ( "shuffle 1..n demo\n" );
     for  (n=1;n<=5;n++) { /* 测试1~5个元素 */
         printf ( "_____n=%d_____\n" ,n);
         j=1;
         for  (c=1;c<=n;c++) j=j*c; /* j为n! */
         j*=n*2;
         for  (c=1;c<=j;c++) { /* 测试n*2*n!次 */
             for  (i=1;i<=n;i++) d[i]=i; /* 填写1~n */
             for  (i=n;i>1;i--) { /* 打乱1~n */
                 a=i;b= rand ()%i+1;
                 if  (a!=b) {t=d[a];d[a]=d[b];d[b]=t;}
             }
             printf ( "%04d:" ,c);
             for  (i=1;i<=n;i++)  printf ( "%d" ,d[i]);
             printf ( "\n" );
         }
     }
}
 
 
 洗牌的算法有很多,这里主要介绍下几种主要的算法。

  方法一:每次找一个随机的位置,然后将这54个数放到找的位置中。

   步骤:1.用一个整型数组记录各个位置是否已经放置了数,如果放置了则不为0,否则为0。所以在算法开始的时候,初始化此数组每个元素的值都为0.

           2.每次产生一个0-53之间的数,看这个位置是否放置了数,如果已经放置了,则继续采用同样的方法找一个随机的位置进行判断,如果这个位置还未放置,则设置此位置。

           3.反复执行步骤2,直到所有的位置都放好了数。

   代码实现如下:

    

 1 void shuffle(int *dest,int N)
 2 {
 3     int pos,card;
 4     for (card=1;card<=N;card++)
 5     {
 6         do 
 7         {
 8             pos=rand()%(N-1);
 9         } while (dest[pos]!=0);
10 
11         dest[pos]=card;
12     }
13 }
14 
15 void main()
16 {
17     int dest[54]={0};
18     shuffle(dest,54);
19     for (int i=0;i<54;i++)
20     {
21         cout<" ";
22     }
23 }

    此方法有个缺点就是很耗时间,因为随着空位置越来越少,寻找会越来越困难。

   方法二:就是先对数组进行初始化,然后随机交换数组中任意两个元素。交换的次数越多就越随机。此方法也很简单,而且时间复杂度也比较低,计算量也不大。

      代码实现如下:

                 

 1 void shuffle ( int a[], int n ) 
 2 {
 3     int tmp = 0, p1, p2;
 4     int cnt = rand() % 1023;
 5     while (cnt--)
 6     {
 7         p1 = rand() % n;
 8         p2 = rand() % n;           
 9         tmp = a[p1];
10         a[p1] = a[p2];
11         a[p2] = tmp;
12     }
13 }

   

     方法三:它的基本思想是初始化一个vector,顺序加入所有牌,即1-54个数,然后从这个vector中随机抽取一个到另一个vector中,将这个过程执行54次即可完成。

       代码的实现如下:

                

 1 void vectorShuffle(vector<int> &unshuffle,vector<int> &shuffled)
 2 {
 3     unsigned int temp,len=unshuffle.size();
 4     while(len)
 5     {
 6         temp=rand()%(len--);
 7         shuffled.push_back(unshuffle[temp]);
 8         unshuffle.erase(unshuffle.begin()+temp);
 9     }
10 }
11 
12 
13 void main()
14 {
15     vector<int> uncard,carded;
16     for (int i=0;i<54;i++)
17     {
18         uncard.push_back(i+1);
19     }
20     vectorShuffle(uncard,carded);
21     for (int j=0;j<54;j++)
22     {
23         cout<" ";
24     }
25 }

    PS:STL中也有一个封装好了的洗牌算法-random_shuffle。使用方法:如果定义有 vector datas,那么直接调用该函数。例如:random_shuffle(datas.begin(),datas.end()); 还可以定义起始迭代器和末尾迭代器来对序列中的某一部分进行洗牌,并且所耗费的时间也较少。

 

 

[code=html]

 

本程序实现了一般扑克牌游戏的洗牌、发牌和理牌过程,程序模拟了对四个玩家的发牌,且能让各自玩家按大小顺序整理自己的手牌。牌的大小顺序按斗地主的规则(但无大小王)。
   本程序定义了一个牌的结构体,其成员变量是两个字符指针。一个指向点数的字符数组,另一个指向花色的字符数组。考虑到牌的整理需要比较两张牌的大小,而字符不太好比较,从而构造了一个辅助函数Value来实现将字符转换为整数。程序得最后还对桌牌和手牌进行了检测,看是否有重复的牌。

 

算法说明:

发牌算法

Void Deal(CARD *Deck,CARD (*player)[13]){

     for (i = 0;i < 4; i++){

        // 遍历四个玩家

for (j = 0;j < 13; j++){

// 洗牌第i个玩家的第j张牌是手牌中的第4*j+i张(轮流发牌)

   player[i][j] = Deck[4*j+i];

    }

}

}

   排序算法

void  InsertSort ( CARD *R) { // 对某一玩家R作直接插入排序,有13张牌
     for  ( i=1; i   temp = R[i]; // 复制为监视哨
  for  ( j=i-1; j>=0; --j )

if(Value(R[j].face)

? Value(R[j].suit) < Value(temp.suit) ? 1 : 0 : 0) break;

for  (k = j-1; k > j;k--)
       R[j+1] = R[j]; // 记录后移
        R[k+1] = temp; // 插入到正确位置
  }
  } // InsertSort

         测试算法

测试桌牌和手牌是否有重复的牌。其思想是从第一张开始与后面的牌一次比较,直到找到一张与待比较的牌相同的或者倒数第二张与最后一张牌比较后终止。

算法描述:

       For(I = 0; I < 13; I++ ){

           Temp = R[I]; // 将待检测的牌放入temp中

           If( Locat(Temp,R)){

            Print :ERROR! 

break; // 如果Temp在R[I+1…13]中则返回1 

}

// 若没有重复的牌不提示任何信息
         变量说明:

一.  张牌(Card)都用一个 strcut card 结构体类型来表示具体定义如下 struct card{

char *face;  // 指针 face 指向不同纸牌的点数

    char *suit;  // 指针 suit 指向不同纸牌的花色

} CARD;


再定义两个字符数组分别存储牌的点数和花色:

char *face[] = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K","A", "2"};
char *suit[] = {"/004", "/005", "/003", "/006"};
定义两个CARD型数组 deck[52],player[4][13],deck用与存储桌牌,player用于存储玩家手牌。

[/code]

 

 

[cpp] view plain copy print ?
  1. // 扑克牌发牌游戏 -- 第一次上机作业   
  2. /* 整个程序由三部分组成 
  3.    1.扑克牌初始化 
  4.    2.洗牌 
  5.    3.发牌 
  6.    4.玩家各自将手牌排序(按黑红紫坊从小到大的排序,2点最大) 
  7.    */  
  8. #include    
  9. #include    
  10. #include    
  11. #include    
  12. typedef struct card{  
  13.     // 每张牌(Card)都用一个 strcut card 结构体类型来表示  
  14.     char *face;  // 指针 face 指向不同纸牌的点数  
  15.     char *suit;  // 指针 suit 指向不同纸牌的花色  
  16. } CARD;   
  17. CARD deck[52], player[4][13];    
  18.      char *face[] = {"3""4""5""6""8""8""9""10""J""Q""K","A""2"};  
  19.      char *suit[] = {"/004""/005""/003""/006"};  
  20.  // 比较时用的牌值   
  21. void InitDeck (CARD * , char *[] , char *[]);  
  22. void Shuffle (CARD *);  
  23. void Deal (CARD * , CARD (*)[13]);  
  24. void Sort (CARD (*)[13]);  
  25. int  Value (char *);  
  26. void Disply(void );  
  27. int Locat(CARD *, int , CARD , int (*)(CARD, CARD));  
  28. int Equl(CARD, CARD);  
  29. void Testcard(CARD *, CARD (*)[13]);  
  30. int main(){  
  31.     srand(time(NULL));    
  32.     InitDeck (deck, face, suit);  // 填新牌   
  33.     Shuffle (deck);  // 洗牌   
  34.     Deal (deck, player); // 发牌   
  35.     Sort (player);  // 对玩家的手牌进行排序  
  36.     Disply();  
  37.     Testcard(deck, player);  
  38.     return 0;  
  39. }  
  40. void InitDeck (CARD *pDeck, char *pface[], char *psuit[]){  
  41.     /* wDeck 结构体指针,指向待初始化桌牌 deck[52] 
  42.        wface 字符数组指针,指向点数 
  43.        wsuit 字符数组指针,指向花色 
  44.        该函数用于初始化桌牌 deck[52] 
  45.        */  
  46.     int i;  
  47.     for (i = 0; i <= 51; i++){  
  48.         pDeck[i].face = pface[i%13];  // 点数初始化  
  49.         pDeck[i].suit = psuit[i/13];  // 花色初始化  
  50.     }  
  51. }  
  52. void Shuffle (CARD *pDeck){  
  53.     // 用产生的随机数 j 依次与前面的牌交换   
  54.     int i, j;  
  55.     CARD temp;  
  56.     for (i = 0; i <= 51; i++){  
  57.         // 洗牌   
  58.         j = rand()%52;  // 产生0到51之间的随机数   
  59.         temp = pDeck[i];  // 交换纸牌  
  60.         pDeck[i] = pDeck[j];  
  61.         pDeck[j] = temp;  
  62.     }  
  63. }  
  64. void Deal (CARD *pDeck , CARD (*pplayer)[13]) {  
  65.     int i, j;  // i为玩家编号,j为手牌编号   
  66.     for (i = 0;i < 4; i++){  
  67.         // 遍历四个玩家   
  68.         for (j = 0;j < 13; j++){  
  69.         // 洗牌第i个玩家的第j张牌是手牌中的第4*j+i张(轮流发牌)  
  70.         pplayer[i][j] = pDeck[4*j+i];  
  71.         }  
  72.     }  
  73. }  
  74. void Sort (CARD (*pplayer)[13]){  
  75.     // 运用插入排序法分别对四个玩家的手牌排序   
  76.     int i, j, p, q;  // i为玩家编号,j为手牌编号,p, q 为工作指针  
  77.     CARD temp;  
  78.     for (i = 0;i < 4; i++){  
  79.         // 外层循环,对四个玩家遍历   
  80.         for (j = 1;j < 13; j++){  
  81.             // 内层循环,开始排序   
  82.             // 先比较face 再比较suit   
  83.             temp = pplayer[i][j];  // 待插入的牌  
  84.             for (p = j-1; p >= 0; p--){  
  85.                 // 从后往前找出第一个小于temp的牌的序号  
  86.             if (Value(pplayer[i][p].face) < Value(temp.face))  // 找到比temp小的牌  
  87.                    break;  
  88.             else if (Value(pplayer[i][p].face) == Value(temp.face)  // 若face 相同 则比较suit  
  89.                 ? Value(pplayer[i][p].suit) < Value(temp.suit) ? 1 : 0 : 0) break;  
  90.             }  
  91.             for (q = j-1; q > p; q--){  // 插入操作  
  92.                 pplayer[i][q+1] = pplayer[i][q];  
  93.             }  
  94.             pplayer[i][q+1] = temp;  
  95.         }  
  96.     }  
  97. }  
  98. int Value(char *str){  
  99.     // 字符映射函数   
  100.     int i = 0;  
  101.       
  102.     while(i < 4 && *str - *suit[i++]) ;  
  103.     if (i == 4) {  
  104.     i = 0;  
  105.     while(i < 13 && *str - *face[i++]) ;  
  106.     }  
  107.     return i;  
  108. }  
  109. void Disply(){  
  110.     using namespace std;  
  111.     int i, j;  
  112.       
  113.     cout << "player1" << "player2" << "player3" << "player4" << endl;  
  114.     for (j = 0; j < 13; j++){  
  115.         // j为第 j 行   
  116.         for (i = 0; i < 4; i++){  
  117.             // i为第 i 位玩家   
  118.             cout << player[i][j].suit << player[i][j].face << "/t";  
  119.             if (i == 3) cout << endl;  
  120.         }  
  121.     }  
  122. }  
  123. void Testcard(CARD *pdeck, CARD (*pplayer)[13]){  
  124.     using namespace std;  
  125.     int i, j;  
  126.     CARD temp;  
  127.    
  128.     for (i = 0; i < 51; i++){  
  129.         temp = pdeck[i];  
  130.         if (Locat(&deck[i+1],52,temp,Equl)){  
  131.             cout << "error! the deckcard are same"<< endl;  
  132.             break;  
  133.         }  
  134.     }  // 测试桌牌是否重复   
  135.     for (i = 0; i < 4; i++){  
  136.         for (j = 0; j < 13; j++){  
  137.             temp = pplayer[i][j];  
  138.             if(Locat(&pplayer[i][j+1],13,temp,Equl)){  
  139.                 cout << "error! the player's card are same" << endl;  
  140.                 break;  
  141.             }  
  142.         }  // 测试手牌是否重复   
  143.     }  
  144. }  
  145. int Locat(CARD *pdecks, int len, CARD deck, int (*compare)(CARD , CARD )){  
  146.     // 若牌堆中有满足 compare() 的牌则返回1,没有则返回0   
  147.     // len牌堆的长度 有13和52   
  148.     CARD *p;  
  149.     int i = 0;  // 第一张牌  
  150.     p = pdecks;  // 第一张牌的地址   
  151.     while (i < len && compare(*p++ , deck))   
  152.         ++i;  
  153.     if(i == len)  
  154.         return 0;  
  155.     else  
  156.         return 1;  
  157. }  
  158. int Equl(CARD card1, CARD card2){  
  159.     // 比较card1 和 card2 是否相同,相同则返回0  
  160.     // 不同返回1   
  161.     int fval, sval;  
  162.     // fval 为点数字符差,sval 为花色字符差   
  163.     fval = *card1.face - *card2.face;  
  164.     sval = *card1.suit - *card2.suit;  
  165.     if(fval || sval)  
  166.         return 1;  // 有一个不同就不相同  
  167.     else   
  168.         return 0;  
  169. }  

 

 

你可能感兴趣的:(C)