大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
1.0版预览地址:http://hudong.miaos.me/majiang/index.html
2.0博客直通车:https://www.cnblogs.com/qinyulin/p/13566107.html
2.0版预览地址:http://hudong.miaos.me/majiang/new.html
一直想做这个程序有几年时间了,主要是每次打血战到底的时候,战局比较复杂的话,最后算钱肯定是一个比较费脑的事情,所以就一直想自己手写这么一段代码,可以把这个计算解放出来。这次在疫情期间,也是和家里人一起打麻将,终于又出现了这个比较迷人的烦恼,所以干脆一不做二不休,花了2天时间完成了这个程序的初版,然后在实战中缝缝补补了几个补丁,最终完成了成麻1.0的版本,主体界面完成情况是下面这样的:
最开始在写布局代码的时候考虑的是把界面分为上中下三个部分,大概的样子如下图:
这样分别对应布局上中下三个部分,上面为一个用户,中间两边各为一个用户,下面为一个用户,中间的中间部分是公共的牌面。大体的布局代码如下:
根据四个用户申明对应的变量,如下图代码:
data: { userArr: ['', '庄家', '左家', '上家', '右家'], user1: "", user2: "", user3: "", user4: "", totalArr: [], zongpanVisiable: false, shuomingVisiable: false, zongpanArr: [], baseMoney: 2, orderArr: [], userCount: 4, countArr: [], messageArr: [], djShow1: false, djShow2: false, djShow3: false, djShow4: false, over1: false, over2: false, over3: false, over4: false, result1: "", result2: "", result3: "", result4: "", dgStatus: false, dgIndex: 0, hpStatus: false, hpIndex: 0, fpStatus: false, fpIndex: 0, fanArr: [], total1: 0, total2: 0, total3: 0, total4: 0, resultVisiable: false, mingziVisiable: false, fan1: 0, fan2: 0, fan3: 0, fan4: 0 },
每个用户涉及到的操作是有巴杠、暗杠、点杠、胡牌和自摸5个事件,从字面上理解,我们可以会把三个杠分为一类,其实这里的分类应该是巴杠、暗杠和自摸,因为这三种都是属于第一人称和其他用户数组为主,点杠和胡牌都是需要自己和对方一起完成,所以我们先贴上布局代码:
1 <div class="user"> 2 <div class="zujian"> 3 <div class="gang flex-x-y-center"> 4 <div @click="bagang(2)">巴杠</div> 5 <div @click="angang(2)">暗杠</div> 6 <div @click="diangang(2)">杠</div> 7 </div> 8 <div class="hu flex-x-y-center"> 9 <div @click="zimo(2)">自摸</div> 10 <div @click="hupai(2)">胡牌</div> 11 </div> 12 </div> 13 <span>{{userArr[2]}}</span><!--用户名--> 14 <div class="dianji" v-if="djShow2" @click="dianji(2)">请选择</div><!--被杠和被胡牌的选择弹层--> 15 <div class="over" v-if="over2"><span>{{result2}}</span></div><!--本局胡牌结束标识层--> 16 <div v-if="resultVisiable" class="zongshu">{{userArr[2]}}<br>{{total2}}</div><!--最终输赢钱的文字层--> 17 </div>
布局的话我们还是先把杠和赢钱的事件分开放,下面还有用户名,被杠和被胡牌的选择弹层,本局胡牌结束标识层和最终输赢钱的文字层。如上面的代码注释,选择弹层这里要说明一下,比如说我胡牌或者杠牌了,这个时候我需要选择另外打给我牌的用户,那么我就需要在界面上对应用户区域去选择,就需要一个选择弹层,展示效果如下图:
当然巴杠、暗杠和自摸不需要出这个一个弹层,因为这几个事件肯定是针对于场上还没胡牌的其他所有玩家,是一对多的关系,可能大家会说一炮多响也是一对多,但是拆开来看,一炮多响其实也是一对一事件,只是连续发生了两次。那么接下来我们就来说这几个事件的具体结果,如下图:
主要代码分为两类,巴杠、暗杠和自摸为第一类,点杠和胡牌为第二类,第一类里面有个obj对象的user属性值,主要是这个值标识是赢钱的人,over数组里面是收集其他人的胡牌情况,到后面清算的时候就找到这个数组里面没结束的;第二类主要是根据over情况弹出选择框,然后去点击对应的用户再进行具体的逻辑代码,如下面的代码:
1 dianji: function (index) { 2 if (this.dgStatus) { 3 //此时是点杠,选择收钱的那一方 4 let obj = { 5 name: "diangang", 6 ying: this.dgIndex, 7 shu: index, 8 } 9 this[`fan${this.dgIndex}`] += 1; 10 this.countArr.push(obj); 11 this.fanArr.push(-1); 12 } 13 if (this.hpStatus) { 14 //此时是胡牌,选择放炮的那一方 15 let obj = { 16 name: "hupai", 17 ying: this.hpIndex, 18 shu: index, 19 fanshu: 1 20 } 21 this.countArr.push(obj); 22 this.fanArr.push(0); 23 this[`over${this.hpIndex}`] = true; 24 this[`result${this.hpIndex}`] = "胡牌"; 25 } 26 this.statusInit(); 27 },
因为第二类事件是一对一的操作,所以我就没有用数组,主要还是一个单字符串的对象来标识。
然后我们来说一下文字列表,这里主要是记录我们每一次操作的描述,比如说谁杠牌,谁自摸,谁胡牌了,界面主要如下:
这里需要注意的其实就是后面有一个番数数字的加减,为什么这里要进行手动的加减呢?可能大家会问,每次杠不都是有记录么?其实如果是手动记录的话,在本局结束之前,程序是没办法知道当前用户具体是胡的什么牌,如果是涉及到对子胡或者清一色可能就会加番,所以这里就需要在最终完成结算之前进行一个手动的操作番数,加减的代码如下:
1 jian: function (index) { 2 if (this.fanArr[index] <= 0) { 3 return; 4 } else { 5 this.fanArr[index]--; 6 this.fanArr.splice(0, 0); 7 } 8 }, 9 jia: function (index) { 10 if (this.fanArr[index] >= 3) { 11 return; 12 } else { 13 this.fanArr[index]++; 14 this.fanArr.splice(0, 0); 15 } 16 },
当完成这一步的时候,终于最终的结算就要来了,我们主要是通过countArr和fanArr这两个的数组信息进行结算,因为涉及到相似代码,所以下面是代码截图:
主要还是通过对countArr的循环,根据之前记录的字段和对应的操作类型,对单个的用户总计进行加减。比如说暗杠就是巴杠的两倍,自摸就相当于巴杠再加一个底等。完成这个结算的界面如下图:
这样就计算出本局每个用户的输赢情况,这里或者之前就会出现一种情况,如果说某一个操作我手动点错了,或者这个时候我觉得计算错误,我要返回重新操作,那么就可以点击公共牌面上的悔棋按钮,悔棋按钮的主要逻辑就是删除countArr里面最新的一条数据,代码如下:
1 huiqi: function () { 2 if (this.resultVisiable) { 3 this.resultVisiable = false; 4 return; 5 } 6 if (!this.countArr.length) { 7 return; 8 } 9 let obj = this.countArr.pop(); 10 if (obj.name == "zimo") { 11 this[`over${obj.user}`] = false; 12 } else if (obj.name == "hupai") { 13 this[`over${obj.ying}`] = false; 14 } 15 this.fanArr.pop(); 16 alert("已经悔了一步了") 17 },
如果正常完成本局计算,那么就是进入下一局,这里我把结算和下一局按钮做成的相斥状态,不能同时出现,下一局事件主要还是还原变量的值,具体的代码如下:
1 nextTime() { 2 if (!this.resultVisiable) { 3 alert("需要先结算当前局") 4 return; 5 } 6 this.resultVisiable = false; 7 let arr = localStorage.getItem("totalArr"); 8 let total = localStorage.getItem("total"); 9 arr = arr == "" ? [] : JSON.parse(arr); 10 total = total == "" ? [] : JSON.parse(total); 11 12 arr.push(this.countArr); 13 let name = []; 14 for (let i = 1; i <= 4; i++) { 15 let obj = { 16 user: i, 17 name: this.userArr[i], 18 money: this[`total${i}`] 19 } 20 name.push(obj); 21 } 22 total.push(name); 23 localStorage.setItem("totalArr", JSON.stringify(arr)); 24 localStorage.setItem("total", JSON.stringify(total)); 25 26 this.fanArr = []; 27 this.countArr = []; 28 this.over1 = this.over2 = this.over3 = this.over4 = false; 29 },
然后点击下一局的时候,这里做了一个localStorage缓存功能,避免用户不小心刷新页面丢失数据的问题,同样的,在每次页面进来的时候也去读取一下localStorage,把之前的局的结果数据读取出来,代码如下:
1 mounted() { 2 let username = localStorage.getItem("username"); 3 let arr = localStorage.getItem("totalArr"); 4 let total = localStorage.getItem("total"); 5 if (arr == null) { 6 localStorage.setItem("totalArr", []); 7 } 8 if (total == null) { 9 localStorage.setItem("total", []); 10 } 11 if (username == null) { 12 let a = ['', '庄家', '右家', '对家', '左家']; 13 localStorage.setItem("username", JSON.stringify(a)); 14 } 15 this.userArr = JSON.parse(localStorage.getItem("username")) 16 },
然后我们再来看一下每局之后保存完,需要看所有局的一个结果,就点击总盘,显示界面如下,我贴一个以前的战况图:
我只能说这个东西谁用谁知道,很爽!请各位忽略战况。
最后再说一下改名的功能,主要就是改变对应用户的名称字段,界面展示和代码如下:
1 gaimingzi() { 2 this.user1 = this.userArr[1]; 3 this.user2 = this.userArr[2]; 4 this.user3 = this.userArr[3]; 5 this.user4 = this.userArr[4]; 6 this.mingziVisiable = true; 7 },
到这里,这个程序的功能基本上都说完了,因为时间比较赶,想着能用就行,所以在代码布局和质量上面写得比较差,但是在后面的实战中,计算都是全部正确的,虽然辛苦了我个人手动操作,但是却大大减少了计算牌局的时间,最开始大家还要多多少少计算一下,后面基本上就完全依赖这个程序,打几个小时的牌,也不用每次把RMB拿出来给,只需要在最后的时候看一下总的完成情况,微信转一个账就行了。
终于这个第一版就告一段落,可能大家有个疑问,这是第一版1.0,那肯定就有2.0了,对滴!因为在后面的实战当中发现了两个这版不太好解决的问题:1、最后查叫,如果两个人需要赔一家,就没办法操作,因为当第一个用户赔叫的时候,赢钱的这家就已经over了,不能进行下一家的差叫;2、换位的问题,因为最开始没考虑到面向对象问题,所以我把几个用户的很多变量值都写得很固定,以至于后面想去修改的时候就是一团乱麻,这个时候再想去理清楚就很麻烦;3、买马的问题,如果需要增加其他用户想参与进来买马或者接下的话,当前的这种代码结构不好扩展;4、打好多钱的问题,目前我是写得固定2元底,没有把这个弄成变量,导致我们目前只能打2元,如果想打5块,10块就不好操作。针对于上面的4点问题,我也是纠结了很久,需不需要进行优化,终于后面在考虑培训课题问题上面,想要不然趁着这个机会,正好做一次优化,把项目进行重构,所以就在一个周末有了2.0的诞生,特别是写2.0的时候,有了一个更好玩的想法,这个想法还是等以后实现了再说,那么下面就是2.0版本的博客,我会在里面写一些更多不一样的编程思想。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/167558.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...