包包版网络游戏大厅+桥牌系统 1.发牌
本来,我只是想写一个发牌的算法,后来才逐步发开出整个游戏大厅。关于这个算法,两年前我曾经发过一个帖子到CSDN,引起了广泛讨论。地址如下:http://topic.csdn.net/t/20060212/14/4551729.html。
一起来回顾一下我最初的算法,说实话,我也忘得差不多了:
{
Hashtable cards = new Hashtable();
int tmp = 0;
for (int i = 0; i < 4; i++)
{
for (int m = 0; m < 13; m++)
{
tmp = ((Byte)(i) << 4) | ((byte)m);
cards.Add((int)(i * 13 + m), (byte)tmp);
}
}
Hashtable tempcards = new Hashtable();
tempcards = (Hashtable)cards.Clone();
int[,] playerCard = new int[4, 13];
int mod = 52;
Random random = new Random();
for (int k = 0; k < 3; k++)
{
for (int j = 0; j < 13; j++)
{
int cardId = (int)(Math.Floor((random.NextDouble() * 103) % mod--));
playerCard[k, j] = (byte)tempcards[cardId];
if (cardId != mod)
{
tempcards.Remove(cardId);
tempcards.Add(cardId, tempcards[mod]);
}
tempcards.Remove(mod);
}
}
for (int j = 0; j < 13; j++)
{
playerCard[3, j] = (byte)tempcards[j];
}
}
依次来分析上面的这段代码:
第一个for循环,是为了造出一副牌来(大小怪不计在内),i从0到3,表示花色,m从0到12表示点数,关键是这一句:
tmp = ((Byte)(i) << 4) | ((byte)m);
我使用了移位运算,把花色i左移4位作为高位,然后将i*13+m,这是一个从0到51的变量,以此匹配前面移位运算产生的值,存入HashTable。当时的考量点是,位运算在进行花色和牌点的比较时,速度会比较快。
第二个for循环,把牌随机发成4份。为此我建立了playerCard这个4维数组,用来存储每个玩家手中的牌。这时k玩家,j表示牌,for循环的逻辑是第一个玩家先随机拿走13张牌,同时从hashtable中remove掉这些牌;然后第二个玩家再从剩下的牌中随机取走13张,第三个玩家如法炮制,最后一个玩家直接取走剩下的13张牌。注意这行代码:int cardId = (int)(Math.Floor((random.NextDouble() * 103) % mod--));
保证了每次取出的牌是随机而且与前面的不一样。
两年前的思维方式比较单纯,以为这么写是很不错的办法。CSDN的朋友们给了我很多建议,大致归结为2点:
1.逻辑太复杂
移位算法速度是很快,但是放在这里有大炮打蚊子之嫌,而且要和int转来转去,代码上更麻烦。
2.效率不高
对HastTable的操作太频繁
在CSDN的众多回复中,huxin2007(西门吹雪)给出了一种很不错的算法,我称之为“换位算法”,经过我的改造如下:
{
ArrayList arrCard = new ArrayList();
for (int i = 1; i <= 52; i++)
{
arrCard.Add(i);
}
int index, t;
Random rad = new Random();
for (int i = 0; i < 52; i++)
{
index = rad.Next(52);
t = (int)arrCard[i];
arrCard[i] = arrCard[index];
arrCard[index] = t;
}
for (int i = 0; i < 13; i++)
{
card1[i] = (int)arrCard[i];
}
for (int i = 0; i < 13; i++)
{
card2[i] = (int)arrCard[i + 13];
}
for (int i = 0; i < 13; i++)
{
card3[i] = (int)arrCard[i + 26];
}
for (int i = 0; i < 13; i++)
{
card4[i] = (int)arrCard[i + 39];
}
arrCard = null;
}
在上面的代码中,创建一个ArrayList类型的arrCard,存入1-52这些代表牌的整数。
“换位算法”体现在第二个for循环中:循环52次,i从0到51,每次都产生一个随机数index,将arrCard[i]与arrCard[index]互换,这就确保了每个arrCard[i]元素都有了一次与任意位置arrCard[index]元素交换位置的机会,从而最终的arrCard里面牌的顺序是最不规则的。
Random rad = new Random();
for (int i = 0; i < 52; i++)
{
index = rad.Next(52);
t = (int)arrCard[i];
arrCard[i] = arrCard[index];
arrCard[index] = t;
}
然后,第1个玩家取走arrCard[0]至arrCard[12]的牌,第2个玩家取走arrCard[13]至arrCard[25]的牌,以此类推。
至此,发牌结束。这个方法位于PlayCardClient这个项目的ServerJudgment类,为什么发牌逻辑在Client端程序而不是Server端,这个问题我会在以后的章节说明。
补充:当初开发的时候还是基于.NET1.1,我在.NET2.0中将其升级为泛型版本,用IList代替了ArrayList,从而省去了拆箱装箱的操作,代码如下。
{
List<int> arrCard = new List<int>(52);
for (int i = 1; i <= 52; i++)
{
arrCard.Add(i);
}
int index, t;
Random rad = new Random();
for (int i = 0; i < 52; i++)
{
index = rad.Next(52);
t = arrCard[i];
arrCard[i] = arrCard[index];
arrCard[index] = t;
}
for (int i = 0; i < 13; i++)
{
card1[i] = arrCard[i];
}
for (int i = 0; i < 13; i++)
{
card2[i] = arrCard[i + 13];
}
for (int i = 0; i < 13; i++)
{
card3[i] = arrCard[i + 26];
}
for (int i = 0; i < 13; i++)
{
card4[i] = arrCard[i + 39];
}
arrCard = null;
}
相关的代码也要改动一些,如Card1这样的变量类型。