包包版网络游戏大厅+桥牌系统 1.发牌

返回目录

 

本来,我只是想写一个发牌的算法,后来才逐步发开出整个游戏大厅。关于这个算法,两年前我曾经发过一个帖子到CSDN,引起了广泛讨论。地址如下:http://topic.csdn.net/t/20060212/14/4551729.html

一起来回顾一下我最初的算法,说实话,我也忘得差不多了:

        private void GetCard()
        
{
            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[413];

            
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(西门吹雪)给出了一种很不错的算法,我称之为“换位算法”,经过我的改造如下:

        public void Shuffle()
        
{
            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里面牌的顺序是最不规则的。

            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;
            }

 

然后,第1个玩家取走arrCard[0]至arrCard[12]的牌,第2个玩家取走arrCard[13]至arrCard[25]的牌,以此类推。

至此,发牌结束。这个方法位于PlayCardClient这个项目的ServerJudgment类,为什么发牌逻辑在Client端程序而不是Server端,这个问题我会在以后的章节说明。 

补充:当初开发的时候还是基于.NET1.1,我在.NET2.0中将其升级为泛型版本,用IList代替了ArrayList,从而省去了拆箱装箱的操作,代码如下。

        public void Shuffle()
        
{
            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这样的变量类型。

 

 

posted @ 2008-07-19 17:04 包建强 阅读(2049) 评论(14)  编辑 收藏 所属分类: 包包版网络棋牌大厅

  回复  引用    
#1楼 2008-07-19 17:10 | 我只看看,不说话 [未注册用户]
传说中的 。。第一楼
  回复  引用  查看    
#2楼 2008-07-19 17:15 | lious-lee      
支持包包
  回复  引用  查看    
#3楼 2008-07-19 17:21 | 横刀天笑      
强,
两年前就开始为MVP而努力了?
  回复  引用    
#4楼 2008-07-19 17:45 | SnowInJune [未注册用户]
Ding
  回复  引用  查看    
#5楼 2008-07-19 18:37 | 金色海洋(jyk)      
原来包包这么帅呀。
  回复  引用  查看    
#6楼 2008-07-19 19:28 | reaper      
支持下。。。希望继续下去。。。
  回复  引用  查看    
#7楼 2008-07-19 20:53 | WilsonWu      
继续支持老包!!!!!!!!!1
  回复  引用  查看    
#8楼 2008-07-19 22:51 | 真见      
牛逼。
  回复  引用  查看    
#9楼 2008-07-19 23:31 | Colin Han      
一点非常小改进。因为牌确定有52张,使用数组的效率会更好一点。
另外, List<int> arrCard = new List<int>(52);也不错,可以减少内存分配。

呵呵,吹毛求疵~~
  回复  引用  查看    
#10楼 [楼主]2008-07-20 16:43 | 包建强      
@Colin Han
多谢提醒,我已经改正。
  回复  引用  查看    
#11楼 2008-07-20 18:53 | 丁学      
等着看你的Client发牌理由
四个人对战,四个人都一起发,这不可能啊,这样岂不是会有重牌。一个人发了通知其他人也不合适,还不如Server发,再说,让谁负责发牌?
  回复  引用  查看    
#12楼 [楼主]2008-07-20 19:41 | 包建强      
@DX
话说,我有一个代理Serverder的逻辑来指定其中一个Client端负责发牌
  回复  引用  查看    
#13楼 2008-07-21 16:12 | 丁学      
--引用--------------------------------------------------
包建强: @DX
话说,我有一个代理Serverder的逻辑来指定其中一个Client端负责发牌
--------------------------------------------------------
话说,Client发牌相对Server发牌有什么优势?难道就为了省那么一点点的CPU?
如果被指定的客户端作弊,岂不是很好玩了?
  回复  引用  查看    
#14楼 2008-07-21 16:15 | 丁学      
再者说,你还可以省点网络带宽,省得把发完牌的数据传给客户端
可是,你得通知四个人哪个发牌吧?发牌的人要把结果告诉其他三个人吧?如果用服务器中转,你这个作法一点意义也米有了咧,如果不中转,那就是四个客户端间直接通信喽?
直接通信,容易被篡改数据的说,服务器无法监视出牌,其中一个作弊的有可能,四个人联合作弊也可能
再者说,因为不经过服务器,所以这个心跳包~~~~~~~你自己想着办,解释清楚给我

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-07-31 18:34 编辑过


相关链接:

历史上的今天:
2007-07-19 SharpDevelop使用心得