我以前用过的一个洗牌算法

前两天和几个做在线棋牌游戏的朋友聚会,聊到了洗牌算法,正好以前写过一些扑克牌的游戏,中间做了一个洗牌算法,就写了个例子给他们做试验。
基本思路很简单,就是交换法,54张牌排好,随机选择两张牌交换,一般说来,只要交换次数足够多,比如54张牌就交换54次,牌就已经洗得很烂了,呵呵,可以打牌了。如果还不放心,那double,洗108次好了。
程序还是比较简单的,大家应该一目了然,我花了差不多半个小时左右写出来,又测试了一下。
Code:
  1. #define POKER_MAX 54            //54张扑克   
  2. #define POKER_COLOR_MAX 4       //四种主花色   
  3. #define POKER_POINT_MAX 13      //每种花色13张牌,J=11,Q=12,K=13   
  4.   
  5. #define POKER_COLOR_KING    4   //王的花色   
  6. #define POKER_COLOR_0       0   //黑桃花色   
  7. #define POKER_COLOR_1       1   //红桃花色   
  8. #define POKER_COLOR_2       2   //樱花花色   
  9. #define POKER_COLOR_3       3   //方块花色   
  10.   
  11. #define POKER_KING_POINT_BIG    1   //大王的点数   
  12. #define POKER_KING_POINT_LITTLE 0   //小王的点数   
  13.   
  14.   
  15. typedef struct _POKER_CARD_   
  16. {   
  17.     short m_sID;        //全序列排列时的ID(0~53)   
  18.     char m_cColor;      //扑克花色   
  19.     char m_cPoint;      //扑克点数   
  20. }SCard;   
  21. const unsigned long SCardSize=sizeof(SCard);   
  22. class CPoker   
  23. {   
  24. public:   
  25.     CPoker()   
  26.     {   
  27.         //先整齐排列54张牌,按黑红樱方顺序,每种花色按0~12顺序   
  28.         int i=0;   
  29.         int j=0;   
  30.         int nIndex=0;   
  31.         for(i=0;i<POKER_COLOR_MAX;i++)   
  32.         {   
  33.             for(j=0;j<POKER_POINT_MAX;j++)   
  34.             {   
  35.                 SetPokerInfo(i,j,nIndex);   
  36.                 nIndex++;   
  37.             }   
  38.         }   
  39.         //王放在最后两张   
  40.         SetPokerInfo(POKER_COLOR_KING,POKER_KING_POINT_LITTLE,nIndex);  //小王   
  41.         nIndex++;   
  42.         SetPokerInfo(POKER_COLOR_KING,POKER_KING_POINT_BIG,nIndex); //大王   
  43.     }   
  44.     ~CPoker(){}   
  45.     //一般说来,按牌总数决定洗牌次数,已经洗得很烂了   
  46.     void Wash(int nTime=POKER_MAX)   
  47.     {   
  48.         int i=0;   
  49.         for(i=0;i<nTime;i++)   
  50.             Random();   
  51.     }   
  52.     //实际的游戏,从此处取洗好的牌张数据   
  53.     bool Get(unsigned int nIndex,SCard* pCard)   
  54.     {   
  55.         if(POKER_MAX<=nIndex) return false;   
  56.         memcpy((char*)pCard,(char*)&m_Poker[nIndex],SCardSize);   
  57.         return true;   
  58.     }   
  59.     void PrintInfo(void)   
  60.     {   
  61.         int i=0;   
  62.         SCard Card;   
  63.         for(i=0;i<POKER_MAX;i++)   
  64.         {   
  65.             if(Get(i,&Card))   
  66.             {   
  67.                 printf("%02d - ID=%02d, Color=%d, Point=%02d\n",   
  68.                     i,Card.m_sID,Card.m_cColor,Card.m_cPoint);   
  69.             }   
  70.         }   
  71.         printf("===============\n");   
  72.     }   
  73. private:   
  74.     //给sID指定的牌张赋值   
  75.     void SetPokerInfo(char cColor,char cPoint,short sID)   
  76.     {   
  77.         m_Poker[sID].m_cColor=cColor;   
  78.         m_Poker[sID].m_cPoint=cPoint;   
  79.         m_Poker[sID].m_sID=sID;   
  80.     }   
  81.     //交换两张牌   
  82.     void Exchange(int a,int b)   
  83.     {   
  84.         char szTemp[SCardSize];   
  85.         memcpy(szTemp,(char*)&m_Poker[a],SCardSize);   
  86.         memcpy((char*)&m_Poker[a],(char*)&m_Poker[b],SCardSize);   
  87.         memcpy((char*)&m_Poker[b],szTemp,SCardSize);   
  88.     }   
  89.     //随机选取两张牌张交换洗牌   
  90.     //如果随机数相等,发生碰撞,则b=a+1,总之不一样就可以了。   
  91.     void Random(void)   
  92.     {   
  93.         int a=GetRandomBetween(0,POKER_MAX);   
  94.         int b=GetRandomBetween(0,POKER_MAX);   
  95.         if(a==b)   
  96.         {   
  97.             b=a+1;   
  98.             if(POKER_MAX<=b) b=0;   
  99.         }   
  100.         Exchange(a,b);   
  101.     }   
  102. private:   
  103.     SCard m_Poker[POKER_MAX];   
  104. };  
其实主要就是Wash这个函数,默认是洗54遍,当然,调用者如果高兴,多洗几遍也没问题,呵呵。
用Get函数拿指定的扑克张子来用。
我试了一下,没什么问题,呵呵,刺激一下大家哈,从我测试代码看,又是0bug。
不过,这段代码有个问题,就是不是多线程安全的,起码,一个线程洗牌,另外一个线程拿牌,会出错。所以,我根据《0bug-C/C++商用工程之道》里面的“资源锁”概念,又封装了一个加锁的线程安全版。
Code:
  1. class CPokerWithLock   
  2. {   
  3. public:   
  4.     CPokerWithLock(){}   
  5.     ~CPokerWithLock(){}   
  6.     void Wash(int nTime=POKER_MAX)   
  7.     {   
  8.         m_Lock.Lock();   
  9.             m_Poker.Wash(nTime);   
  10.         m_Lock.Unlock();   
  11.     }   
  12.     bool Get(unsigned int nIndex,SCard* pCard)   
  13.     {   
  14.         bool bRet=false;   
  15.         m_Lock.Lock();   
  16.             bRet=m_Poker.Get(nIndex,pCard);   
  17.         m_Lock.Unlock();   
  18.         return bRet;   
  19.     }   
  20.     void PrintInfo(void)   
  21.     {   
  22.         m_Lock.Lock();   
  23.             m_Poker.PrintInfo();   
  24.         m_Lock.Unlock();   
  25.     }   
  26. private:   
  27.     CPoker m_Poker;     //"资源锁"概念,私有聚合,所有公有方法加锁,确保安全性   
  28.     CMutexLock m_Lock;  //这是《0bug-C/C++商用工程之道》里面的跨平台安全锁,见6.2小节,P226页   
  29. };  
嗯,这个加锁版本不是必须的,只有当多线程环境时才需要。CMutexLock这个类我这里就懒得提供了,有书的朋友,自己去查书吧。
嗯,最后,给段测试代码,大家可以看看效果。
Code:
  1. inline void TestCPoker(void)   
  2. {   
  3.     CPokerWithLock Poker;   
  4.     srand((unsigned int)time(NULL));   
  5.     Poker.PrintInfo();   
  6.     Poker.Wash();   
  7.     Poker.PrintInfo();   
  8.     Poker.Wash();   
  9.     Poker.PrintInfo();   
  10. }  
上述代码在VS2008下测试成功。有兴趣的朋友,可以自己试试。嗯,如果没有书的朋友,CPokerWithLock Poker;这句话可以改为CPoker Poker;,直接用非线程安全版本就好了。
大家看看,有什么问题欢迎讨论哈。
=======================================================
在线底价购买《0bug-C/C++商用工程之道》
(直接点击下面链接或拷贝到浏览器地址栏)
http://s.click.taobao.com/t_3?&p=mm_13866629_0_0&n=23&l=http%3A%2F%2Fsearch8.taobao.com%2Fbrowse%2F0%2Fn-g%2Corvv64tborsvwmjvgawdkmbqgboq---g%2Cgaqge5lhebbs6qzlfmqmttgtyo42jm6m22xllqa-------------1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2C14%2C15%2C16%2C17%2C18%2C19%2C20---40--coefp-0-all-0.htm%3Fpid%3Dmm_13866629_0_0
肖舸
 

你可能感兴趣的:(C++,c,算法,扑克,洗牌)