前兩天和幾個做在線棋牌游戲的朋友聚會,聊到了洗牌算法,正好以前寫過一些撲克牌的游戲,中間做了一個洗牌算法,就寫了個例子給他們做試驗。
基本思路很簡單,就是交換法,54張牌排好,隨機選擇兩張牌交換,一般說來,只要交換次數足夠多,比如54張牌就交換54次,牌就已經洗得很爛了,呵呵,可以打牌了。如果還不放心,那double,洗108次好了。
程序還是比較簡單的,大家應該一目了然,我花了差不多半個小時左右寫出來,又測試了一下。
Code:
- #define POKER_MAX 54 //54張撲克
- #define POKER_COLOR_MAX 4 //四種主花色
- #define POKER_POINT_MAX 13 //每種花色13張牌,J=11,Q=12,K=13
-
- #define POKER_COLOR_KING 4 //王的花色
- #define POKER_COLOR_0 0 //黑桃花色
- #define POKER_COLOR_1 1 //紅桃花色
- #define POKER_COLOR_2 2 //櫻花花色
- #define POKER_COLOR_3 3 //方塊花色
-
- #define POKER_KING_POINT_BIG 1 //大王的點數
- #define POKER_KING_POINT_LITTLE 0 //小王的點數
-
-
- typedef struct _POKER_CARD_
- {
- short m_sID; //全序列排列時的ID(0~53)
- char m_cColor; //撲克花色
- char m_cPoint; //撲克點數
- }SCard;
- const unsigned long SCardSize=sizeof(SCard);
- class CPoker
- {
- public:
- CPoker()
- {
- //先整齊排列54張牌,按黑紅櫻方順序,每種花色按0~12順序
- int i=0;
- int j=0;
- int nIndex=0;
- for(i=0;i<POKER_COLOR_MAX;i++)
- {
- for(j=0;j<POKER_POINT_MAX;j++)
- {
- SetPokerInfo(i,j,nIndex);
- nIndex++;
- }
- }
- //王放在最後兩張
- SetPokerInfo(POKER_COLOR_KING,POKER_KING_POINT_LITTLE,nIndex); //小王
- nIndex++;
- SetPokerInfo(POKER_COLOR_KING,POKER_KING_POINT_BIG,nIndex); //大王
- }
- ~CPoker(){}
- //一般說來,按牌總數決定洗牌次數,已經洗得很爛了
- void Wash(int nTime=POKER_MAX)
- {
- int i=0;
- for(i=0;i<nTime;i++)
- Random();
- }
- //實際的游戲,從此處取洗好的牌張數據
- bool Get(unsigned int nIndex,SCard* pCard)
- {
- if(POKER_MAX<=nIndex) return false;
- memcpy((char*)pCard,(char*)&m_Poker[nIndex],SCardSize);
- return true;
- }
- void PrintInfo(void)
- {
- int i=0;
- SCard Card;
- for(i=0;i<POKER_MAX;i++)
- {
- if(Get(i,&Card))
- {
- printf("%02d - ID=%02d, Color=%d, Point=%02d\n",
- i,Card.m_sID,Card.m_cColor,Card.m_cPoint);
- }
- }
- printf("===============\n");
- }
- private:
- //給sID指定的牌張賦值
- void SetPokerInfo(char cColor,char cPoint,short sID)
- {
- m_Poker[sID].m_cColor=cColor;
- m_Poker[sID].m_cPoint=cPoint;
- m_Poker[sID].m_sID=sID;
- }
- //交換兩張牌
- void Exchange(int a,int b)
- {
- char szTemp[SCardSize];
- memcpy(szTemp,(char*)&m_Poker[a],SCardSize);
- memcpy((char*)&m_Poker[a],(char*)&m_Poker[b],SCardSize);
- memcpy((char*)&m_Poker[b],szTemp,SCardSize);
- }
- //隨機選取兩張牌張交換洗牌
- //如果隨機數相等,發生碰撞,則b=a+1,總之不一樣就可以了。
- void Random(void)
- {
- int a=GetRandomBetween(0,POKER_MAX);
- int b=GetRandomBetween(0,POKER_MAX);
- if(a==b)
- {
- b=a+1;
- if(POKER_MAX<=b) b=0;
- }
- Exchange(a,b);
- }
- private:
- SCard m_Poker[POKER_MAX];
- };
其實主要就是Wash這個函數,默認是洗54遍,當然,調用者如果高興,多洗幾遍也沒問題,呵呵。
用Get函數拿指定的撲克張子來用。
我試了一下,沒什麼問題,呵呵,刺激一下大家哈,從我測試代碼看,又是0bug。
不過,這段代碼有個問題,就是不是多線程安全的,起碼,一個線程洗牌,另外一個線程拿牌,會出錯。所以,我根據《0bug-C/C++商用工程之道》裡面的“資源鎖”概念,又封裝了一個加鎖的線程安全版。
Code:
- class CPokerWithLock
- {
- public:
- CPokerWithLock(){}
- ~CPokerWithLock(){}
- void Wash(int nTime=POKER_MAX)
- {
- m_Lock.Lock();
- m_Poker.Wash(nTime);
- m_Lock.Unlock();
- }
- bool Get(unsigned int nIndex,SCard* pCard)
- {
- bool bRet=false;
- m_Lock.Lock();
- bRet=m_Poker.Get(nIndex,pCard);
- m_Lock.Unlock();
- return bRet;
- }
- void PrintInfo(void)
- {
- m_Lock.Lock();
- m_Poker.PrintInfo();
- m_Lock.Unlock();
- }
- private:
- CPoker m_Poker; //"資源鎖"概念,私有聚合,所有公有方法加鎖,確保安全性
- CMutexLock m_Lock; //這是《0bug-C/C++商用工程之道》裡面的跨平台安全鎖,見6.2小節,P226頁
- };
嗯,這個加鎖版本不是必須的,只有當多線程環境時才需要。CMutexLock這個類我這裡就懶得提供了,有書的朋友,自己去查書吧。
嗯,最後,給段測試代碼,大家可以看看效果。
Code:
- inline void TestCPoker(void)
- {
- CPokerWithLock Poker;
- srand((unsigned int)time(NULL));
- Poker.PrintInfo();
- Poker.Wash();
- Poker.PrintInfo();
- Poker.Wash();
- Poker.PrintInfo();
- }
上述代碼在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
肖舸
本文出自 “肖舸的blog” 博客,請務必保留此出處http://tonyxiaohome.blog.51cto.com/925273/302220