新手友好大师:tcp麻将游戏上篇

基于tcp通讯的麻将游戏

为什么可以实现,基础还是在于一个服务器可以连接多个客户端,游戏主程序作为服务器,连接4个客户端,利用读写字符串进行游戏。
项目采用tcp工具,和控制台:
TCPUDPDbg.exe 参数设置如下

游戏逻辑划分:大体的分为3个模块,一个就是发牌的模块,一个是摸牌的模块,一个是赢棋的判定。
首先,讲第一个模块的实现,发牌之前的准备,我这里直接将简化之后的,需要2个数组,一个String数组储存136张牌的牌面信息,即中文牌名,举个例子:以条字牌开头,下标为0的元素就是“一条”,一副牌“一条”有4个,依此类推。另外一个int数组,储存0到135。下面给出来了,直接复制粘贴使用。
static String[] standard = { "一条", "一条", "一条", "一条", "二条", "二条", "二条", "二条", "三条", "三条", "三条", "三条", "四条", "四条", "四条", "四条", "五条", "五条", "五条", "五条", "六条", "六条", "六条", "六条", "七条", "七条", "七条", "七条", "八条", "八条", "八条", "八条", "九条", "九条", "九条", "九条", "一饼", "一饼", "一饼", "一饼", "二饼", "二饼", "二饼", "二饼", "三饼", "三饼", "三饼", "三饼", "四饼", "四饼", "四饼", "四饼", "五饼", "五饼", "五饼", "五饼", "六饼", "六饼", "六饼", "六饼", "七饼", "七饼", "七饼", "七饼", "八饼", "八饼", "八饼", "八饼", "九饼", "九饼", "九饼", "九饼", "一万", "一万", "一万", "一万", "二万", "二万", "二万", "二万", "三万", "三万", "三万", "三万", "四万", "四万", "四万", "四万", "五万", "五万", "五万", "五万", "六万", "六万", "六万", "六万", "七万", "七万", "七万", "七万", "八万", "八万", "八万", "八万", "九万", "九万", "九万", "九万", "东风", "东风", "东风", "东风", "南风", "南风", "南风", "南风", "西风", "西风", "西风", "西风", "北风", "北风", "北风", "北风", "红中", "红中", "红中", "红中", "发财", "发财", "发财", "发财", "白板", "白板", "白板", "白板" };
static int[] nums = { 0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135};
发牌的准备工作做完了,现在开始实现发牌。 首先打乱int数组的顺序,但是int数组不自带排序。
咋们先把它转化为List,这里推荐两种方法,使用流,
List<Integer> numlist = Arrays.stream(nums).boxed().collect(Collectors.toList());
第二种,使用aslist方法,
List<Integer> numlist = Arrays.asList(nums);
得到numlist之后,使用Collections 接口提供的shuffle方法,在这里不用管方法内部是怎样实现随机的,先用着就行了,其实实现集合随机的方法无外乎三种。
Collections.shuffle(numlist);
测试随机结果
第一组:72,106,95,0,133,55,93,19,41,28,110,86,37,122,54,69,132,115,48,123,109,57,125,76,88,32,78,52,127,113,39,42,22,10,17,34,77,99,1,61,20,58,135,43,67,18,59,6,53,83,94,91,128,81,75,38,130,101,119,49,129,114,5,44,112,63,51,15,87,40,68,124,84,13,111,134,108,60,100,8,66,29,62,79,121,47,85,7,45,30,12,70,104,118,71,64,98,14,11,74,117,116,105,89,56,102,4,107,73,25,36,103,126,35,16,31,92,21,131,3,120,46,96,33,2,27,23,80,90,9,82,26,97,50,65,
第二组:3,15,1,19,80,87,62,48,43,31,88,134,86,5,67,109,60,54,103,77,58,132,111,53,6,50,79,113,41,18,131,25,127,46,75,61,49,120,93,0,89,23,37,84,9,107,63,57,64,35,78,16,135,76,7,39,105,94,51,117,44,72,34,42,17,40,122,27,4,11,92,98,90,126,124,70,21,123,30,73,28,106,65,85,74,71,22,59,12,128,20,38,91,99,8,101,52,36,83,108,115,2,104,102,14,68,112,118,55,56,26,81,96,114,110,13,133,130,100,69,45,119,32,10,24,129,95,121,125,33,116,82,29,66,47,
第三组:129,124,37,10,46,24,117,98,62,104,105,49,3,58,4,100,133,69,14,56,59,106,118,64,61,109,66,113,31,8,40,41,0,81,84,53,121,88,12,1,13,65,48,135,76,28,74,29,130,52,35,128,91,132,26,9,25,83,111,89,122,39,123,79,116,127,44,68,33,34,38,110,42,54,134,102,77,7,5,126,36,119,96,120,6,50,27,90,93,17,97,19,80,18,99,2,23,101,75,22,131,112,20,92,71,86,72,85,115,32,60,45,67,95,11,94,57,78,16,82,43,107,55,125,87,70,108,51,21,30,63,114,103,15,73,
结果很满意。
现在再说一个项目结构方面的东西。我的数据直接放在主程序中,用Static共享,这样的好处是,既避免了数据放在xml里要经过繁琐的提取出来,提取出来还要拆包,还避免了将数据单独作为类,调用洗牌方法后无法初始化的问题。然后,我写了一个准备类prepare,这个类有rush(洗牌),putcard(发牌)。主程序运行后,创建完容器就实例化这个对象,调用洗牌方法得到上面随机数集合,调用发牌方法得到4个字符串数组,4个整型数组,一个剪切后的numlist,里面存放没发完的牌。
这里我们使用了rush方法,得到随机数组,返回存放随机数集合,
public List rush(int [] nums) { List<Integer> numlist = Arrays.stream(nums).boxed().collect(Collectors.toList()); Collections.shuffle(numlist); return numlist; }
先给大家看第一位玩家的发牌代码:

 String[] p1cards=new String[30];
       int [] p1nums=new int[18];
	   public  void  putcard( List numlist,String[] standard) {
      for(int i=0;i<17;i++) {
	        	        p1nums[i]= (int) numlist.get(i);
	        	       System.out.print(p1nums[i]+"\t");
		            }
	           Arrays.sort(p1nums);
	           //17个随机数
	           System.out.println(" ");
	           for(int i=1;i<18;i++) {
           	System.out.print(p1nums[i]+"\t");
	            }


代码很容易理解,但它内部逻辑很多学生可能看不懂,这里我解释一下:
先new一个p1cards数组,装牌面。再new一个int数组装序列,(ps:关于命名,我有个小习惯,数组加s,集合加list)这里一个人17张牌,为什么要给int数组初始化为18呢
数组的长度不能随便增删,到后面不是很麻烦吗,其实这个大家结合一下实际逻辑就能理解了,发牌是一人17张,但麻将的摸牌,是先与出牌的,也就是第一轮,甚至前几轮,大家的牌数都会到达18张,在之后序列数组只会少不会多,所以初始化为18,然后,在赋值的过程中,只给17张牌赋值了,nums【18】值还是为0的,然后使用int数组自带的顺序排列方法,
Arrays.sort(p1nums);
这样会把0排到第一位,那如果我分到的是0怎么办呢,没事咋们统一从第二位开始读,读到的就是自然升序序列,有了顺序序列,根据标准数组standard开始打印牌面。这样打出来的牌面数组还是有序的,方便玩家看牌。
打印结果如下:
玩家一的牌组是:
6 60 127 70 100 108 103 121 32 104 134 92 93 59 82 126 132
6 32 59 60 70 82 92 93 100 103 104 108 121 126 127 132 134
二条 九条 六饼 七饼 九饼 三万 六万 六万 八万 八万 九万 东风 北风 红中 红中 白板 白板
这货的牌能从头吃到尾,这把牌型有点特殊,咋们再来一次验证随机性;
玩家1的牌组是:
130 128 18 70 103 97 13 7 28 56 113 54 31 121 135 117 35
7 13 18 28 31 35 54 56 70 97 103 113 117 121 128 130 135
二条 四条 五条 八条 八条 九条 五饼 六饼 九饼 七万 八万 南风 西风 北风 发财 发财 白板
再来一次
玩家1的牌组是:
91 44 59 55 120 130 118 101 0 9 12 22 89 82 62 43 66
0 9 12 22 43 44 55 59 62 66 82 89 91 101 118 120 130
一条 三条 四条 六条 二饼 三饼 五饼 六饼 七饼 八饼 三万 五万 五万 八万 西风 北风 发财
这一次的一条就被抽到了,还是能正常显示,所有说,不用担心,都有办法;
四个玩家顺序获得numlist里的元素,0到16、17到33、34到50,51到67;
在这里全部读完了,我们做一个remove();
将numlist下标0到67的元素,全部都给移除。
for(int i=0;i<68;i++) { numlist.remove(i); }
剩下的是未摸到的牌,且顺序不乱。
发牌方法写完了,现在写个客户端,连进来。java语言实现服务器与客户端非常方便。现在主程序里写服务器,

ServerSocket server=new ServerSocket(8888);
	   Socket socket=server.accept();	   	   
       System.out.print("玩家一已经准备好");
	   OutputStream os=socket.getOutputStream();
	   InputStream is=socket.getInputStream(); 

开启8888端口,接受客户端的连接;
这时候,先不管关闭流,关闭客户端,关闭服务器,咋们先打开tcp测试工具来测试,这里,应该是本机现在上网的ip地址和127.0.0.1都可以接入,咋们使用127.0.0.1即本地地址;
图中的ipv4地址也可以作为你的服务器地址
打开tcp连接工具后,参数设置如下:
第一个客户端连接
在连接之前,咋们还要设计一下交互的格式和顺序。
我们默认客户端一连接上,服务器创建的字节输出流对象就给它发送打印好的牌面,让玩家看到自己的牌,(摸牌在这之前就已经完成了)然后玩家返回一个字符串给服务器,代表他要出的牌,先完成这一部分,后面还有完成牌组数据的同步,和吃碰杠胡四种方法。

 p1num[0]=numlist.get(0);
  p1card[18]=standard[numlist.get(0)];
  Arrays.sort(p1num);
 for(int j=0;j<18;j++) {
   os.write((p1card[j]).getBytes());	
 }
System.out.println("到你的出牌了了!!!!!!!!!!");   
byte[] bytep1=new byte[1024];
int len1=is.read(bytep1);
String outcard1=new String(bytep1,0,len1);

测试代码:
其中两点细节要讲一下,一个是println跟print方法的区别,第二点是在打印的时候,为了更好的视觉感
加上“\t”,大家在下面的代码里好好感受:

	   System.out.println("");
         p1card[18]=standard[numlist.get(0)];
p1num[0]=numlist.get(0);
Arrays.sort(p1num);
for(int j=0;j<17;j++) {
	System.out.print(p1num[j]+"\t");	
}	   

测试结果为:
拿到牌面,出白板
控制台显示
上篇就到这里结束了,大家在下面好好运行测试。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值