在此声明一下这不是
前两天和几个做在线棋牌游戏的朋友聚会,聊到了洗牌算法,正好以前写过一些扑克牌的游戏,中间做了一个洗牌算法,就写了个例子给他们做试验。
基本思路很简单,就是交换法,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
肖舸
如果您对我的文章感兴趣,请点这里添加我为好友:http://student.csdn.net/invite.php?u=39028&c=42fcd4a519102d74
博客主站:http://blog.csdn.net/tonyxiaohome
CSDN学生大本营个人主页:http://student.csdn.net/?39028
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tonyxiaohome/archive/2010/04/21/5512086.aspx