zoukankan      html  css  js  c++  java
  • 成麻结账程序

        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版本的博客,我会在里面写一些更多不一样的编程思想。

  • 相关阅读:
    常见寻找OEP脱壳的方法
    Windows内核原理系列01
    HDU 1025 Constructing Roads In JGShining's Kingdom
    HDU 1024 Max Sum Plus Plus
    HDU 1003 Max Sum
    HDU 1019 Least Common Multiple
    HDU 1018 Big Number
    HDU 1014 Uniform Generator
    HDU 1012 u Calculate e
    HDU 1005 Number Sequence
  • 原文地址:https://www.cnblogs.com/qinyulin/p/13564689.html
Copyright © 2011-2022 走看看