• 第二次结对编程作业


    博客链接和Github链接

    结对同学博客链接

    本作业博客链接

    Github项目地址

    具体分工

    高星负责前端界面和美工

    我负责13水的算法部分和前端的接口处理

    psp表格

    PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划
    • Estimate • 估计这个任务需要多少时间 2500 7070
    Development 开发
    • Analysis • 需求分析 (包括学习新技术) 120 700
    • Design Spec • 生成设计文档 15 20
    • Design Review • 设计复审 10 30
    • Coding Standard • 代码规范 (为目前的开发制定合适的规范) 5 20
    • Design • 具体设计 20 120
    • Coding • 具体编码 2100 5160
    • Code Review • 代码复审 30 40
    • Test • 测试(自我测试,修改代码,提交修改) 200 980
    Reporting 报告
    • Test Report • 测试报告 20 30
    • Size Measurement • 计算工作量 10 5
    • Postmortem & Process Improvement Plan • 事后总结, 并提出过程改进计划 10 10
    合计 2540 7115

    解题思路与设计实现说明

    网络接口的使用

    用的vue的axios

    一开始计划用java写13水 因为接口部分的处理所以最后选择用了前端的框架vue,本来打算用vue的resource(因为之前学的这个)但是想到这个现在几乎没啥人用,最后用了axios

    axios的get和post方法都是去官方文档看的,慢慢学习

    这次代码有很多不完善的地方其中有一点就体现在vue接口部分的使用,我们两没有把axios封装好,导致我们两接口部分的代码不太好(简直差到没眼看)这部分应该再学习,每次写代码都发现好多不足和不会(前端没有我们两想象中的那么容易,菜是真菜)

    代码组织与内部实现

    1. 工程结构

    1. src中结构(内部实现设计)

    1. 对代码详细说明

    首先代码有五个主要界面 (登入界面,开始界面,游戏牌型显示界面,排行榜显示界面,历史记录界面)其中注册界面当作一个弹窗写在登入界面中,历史记录详情也是作为一个弹窗写在历史记录中(没有在另外写两个主要界面)

    界面名称 界面功能
    UserLogin.vue 这是登入界面用于用户登入的,其中注册界面也在其中
    BeginView.vue 这是登录成功后进入的界面,用户可以选择加入战局,或者查看历史排行榜
    CardShowVIew.vue 这是用户加入战局,此页面用于显示接收后端接口提供卡牌信息,经过ai处理后把处理好的牌的出牌的样式显示再界面上,并且把处理的卡牌信息通过发给后端
    HistoryList.vue 这是用来查看历史记录的页面,可以通过查看详情获取更多当局的信息
    RankingList.vue 这个用于查看排名总榜的页面

    界面的跳转用的vue的路由处理

    Token认证和用户id用的vuex存储在localStorage中,在把判断哪个页面需要用到Token,让它自动加进去,核心代码如下

    axios.interceptors.request.use(
    config => {
    if (
    config.url === 'https://api.shisanshui.rtxux.xyz/auth/login'
    ) {
    //如果是登录和注册操作,则不需要携带header里面的token
    } else {
    if (localStorage.getItem('Authorization')) {
      config.headers['X-Auth-Token'] = localStorage.getItem('Authorization')
    }
    }
    return config
    },
    error => {
    return Promise.reject(error)
    }
    )
    

    其中我们两的算法写在了CardShowVIew中,前端如何在连接一个后端处理代码我不会(能力不足能力不足,这也是代码的一大缺点)

    因为没有用到类,所以我类图就没有写了,给出前面给出了内部设计

    ​ 4.代码界面展示

    登入界面(UserLogin)

    登入成功界面(BeginView)

    开始游戏界面(CardShowVIew)

    点击开启战局之后,界面显示ai出牌的样式

    排行总榜(RankingList)

    历史总榜(HistoryList)

    历史总榜详情

    算法的关键与关键实现部分流程图

    算法关键

    算法关键1 有很多个数组同时记录牌的情况(不用对象数组的原因后面有讲)

    算法关键2 给把牌分等级,这样才方便比较J Q K A这样的牌(2就是等级2,...,J就是等级11,Q就是等级12,K就是等级13,A就是等级14)并且通过等级给牌排序(对应数组也要变化)

    算法关键3 有个数组放重复牌的数量 有个数组放四个花色(方块,梅花,黑桃,红桃)的数量,这样方便找到炸弹 葫芦 三条 同花的情况

    算法关键4 最大的放后墩 在挑出牌放中墩 剩下最后三张放前墩

    (为什么算法和代码这么的面向过程 我用的js 面向对象有点难用 主要是自己水平不够 )

    算法关键5 对于特殊牌型用的鸵鸟算法 我们两觉得我们两运气没这么好,会碰到特殊牌型(主要是代码写的差,不会 反思自己,两个菜鸡)

    下表格是算法中使用的各个数组的用途

    使用的数组名 其功能
    cardsUse[13] 用于看存放的牌是否使用 初始值为false 使用后为true
    cardsTotal[13] 用于存放*2 这样的完整字符串
    cardsDegree[13] 用于存放牌的等级 2就是等级2以此类推
    cardsType[13] 牌的类型 0表示# 1表示$ 2 表示& 3表示*(这样后期渲染到页面的时候方便)
    cardsNumber[13] 这个等级的牌有几张 K有4张 对应的K数就有几张
    cardsTypeNumber[4] 用于存放#$&*有几张 比如cardTypeNumber[0]=4 表示 #号牌有四张 方便找同花的情况
    houdun[5] 用于存放找到的后墩
    zhongdun[5] 用于存放找到的中墩
    qiandun[3] 用于存放找到的前墩
    sendCards[3] 用于存放处理好的牌,然后发给后端接口

    下表格是使用到的函数

    使用的函数 功能
    PlayAndShowCards() 用于从接口获得信息,以及调用各个函数,以及返回处理后的牌给接口
    setCards() 用于初始各个数组的情况,处理最开始从接口获得的信息
    decideHoudun() 用于选择出后墩
    decideZhongdun() 用于选择出中墩
    decideQiandun() 用于选择出前墩
    showCards() 用于渲染到页面上(改变img属性的src 显示牌在界面上)

    算法流程图

    后墩流程图

    中墩思路和后墩差不多,不同的是其中中墩需要判断牌是否被使用过,即cardsUsed[i]是否为false,如果true表示牌被使用了。则不能接着使用

    关键代码解释

    重要的有价值的代码片段

      var cards=this.card.split(" ");
      for(var i=0;i<=12;i++){
      
        this.cardsUse[i]=false;
        this.cardsTotal[i]=cards[i];
        this.cardsType[i]=cards[i].slice(0,1);
      
        if(this.cardsType[i]==="#"){
          this.cardsTypeNumber[0]++;
          this.cardsType[i]=0;
        }else if(this.cardsType[i]==="$"){
          this.cardsTypeNumber[1]++;
          this.cardsType[i]=1
        }else if(this.cardsType[i]==="&"){
          this.cardsTypeNumber[2]++;
          this.cardsType[i]=2
        }else if(this.cardsType[i]==="*"){
          this.cardsTypeNumber[3]++;
          this.cardsType[i]=3
        }
       
        if(cards[i].slice(1,2)==="A"){
          this.cardsDegree[i]=14;
          this.cardsDegree[i]=parseInt(this.cardsDegree[i]);
          this.cardsNumber[14]++;
        }else if(cards[i].slice(1,2)==="K"){
          this.cardsNumber[13]++;
          this.cardsDegree[i]=13;
        }else if(cards[i].slice(1,2)==="Q"){
          this.cardsDegree[i]=12;
          this.cardsNumber[12]++;
        }else if(cards[i].slice(1,2)==="J"){
          this.cardsNumber[11]++;
          this.cardsDegree[i]=11;
        }else if(cards[i].slice(1,2)==="1"){
          this.cardsDegree[i]=10;
          this.cardsNumber[10]++;
        }
        else{
          this.cardsDegree[i]=parseInt(cards[i].slice(1,2));
          this.cardsNumber[this.cardsDegree[i]]++;
        }
      
    }
    //以下是通过等级给这些数值排序
    for (var i = this.cardsDegree.length - 1; i > 0; i--) {
        for (var j = 0; j < i; j++) {
          if (this.cardsDegree[j] < this.cardsDegree[j + 1]) {
            [this.cardsDegree[j], this.cardsDegree[j + 1]] = [this.cardsDegree[j + 1], this.cardsDegree[j]];
            [this.cardsTotal[j], this.cardsTotal[j + 1]] = [this.cardsTotal[j + 1], this.cardsTotal[j]];
            [this.cardsType[j], this.cardsType[j + 1]] = [this.cardsType[j + 1], this.cardsType[j]];
          }
        }
      }
    

    根据接口接收到的信息,split(' ')把信息分出,再通过slice()完善各个数组的信息

    cardsDegree和cardsTotal和cardsType和cardsUse这些一一对应关系(不用对象数组是因为js对象数组太难用了 不要笑我憨憨)

    通过数组cardsDegree 可以比较牌大小 用于排序查看等级等

    通过数值cardsType表示对应的值牌的类型

    通过cardsNumber数组(下标表示牌的等级)数值表示该等级的牌有几张 排序时不用动cardsNumber它本身就是排好了顺序(为0就表示没有该牌 或者被用掉了)

    eg:

    cardsDegree[0]=13 表示最大的牌为等级为13的牌 也就是K

    cardsType[0]=3 该牌类型为*

    cardsNumber[13]=4 表示等级13的牌有4张 也就是K有四张的意思

    //找牌   
       var houdun=[]
       var flag=-1;
        var max1=0;//有多少重复的牌
         var max2=0;
      
             //看是否有炸弹 有葫芦(重复牌数)
       for(var i=1;i<=14;i++)
       {
         if(max1<this.cardsNumber[i])
         max1=this.cardsNumber[i];
       }
       //看是否有同花
       for(var i=0;i<4;i++)
       {
         if(max2<this.cardsTypeNumber[i])
           {
             
             max2=this.cardsTypeNumber[i];
           }
       }
     
     
       if(max1>2){
    
            flag=-1;
          
           for(var j=1;j<=14;j++){
                 
                 if(this.cardsNumber[j]==5-max1)                                  
                 {
                   flag=j;
                   break
                 }
               }
           
               
           if(flag>0){
    
             for(var j=0;j<=12;j++)
             {
               
               if(this.cardsDegree[j]===flag&&this.cardsUse[j]===false)
               {
                 
                 this.cardsUse[j]=true;
                 this.cardsNumber[this.cardsDegree[j]]--
                 this.cardsTypeNumber[this.cardsType[j]]--
                 houdun.push(this.cardsTotal[j]); 
               }
             }
           for(var j=1;j<=14;j++){ 
                 if(this.cardsNumber[j]==max1)
                 {
                   flag=j;  
                   
                 }
               }
                   
               for(var j=0;j<=12;j++)
               {
                 
                 if(this.cardsDegree[j]===flag)
                 {
                   
                   this.cardsUse[j]=true;
                   this.cardsNumber[this.cardsDegree[j]]--;
                   this.cardsTypeNumber[this.cardsType[j]]--
                   houdun.push(this.cardsTotal[j]);
                   
                 }
               }
        
           }
           //拆双数的情况
          else if(max1===4&&flag===-1){
                 
             for(var j=1;j<=14;j++){
                 if(this.cardsNumber[j]==5-max1+1)                                  
                 {
                   flag=j;
                  
                   break
                 }
               }
             for(var j=0;j<=12;j++)
             {
               
               if(this.cardsDegree[j]===flag&&this.cardsUse[j]===false)
               {
                 
                 this.cardsUse[j]=true;
                 this.cardsNumber[this.cardsDegree[j]]--
                 this.cardsTypeNumber[this.cardsType[j]]--
                 houdun.push(this.cardsTotal[j]); 
                 break;
               }
             }
     
               for(var j=1;j<=14;j++){ 
                 if(this.cardsNumber[j]==max1&&this.cardsUse[this.cardsNumber[j]]===false)
                 {
                   flag=j;  
                   
                 }
               }
                   
               for(var j=0;j<=12;j++)
               {
                 
                 if(this.cardsDegree[j]===flag)
                 {
                   
                   this.cardsUse[j]=true;
                   this.cardsNumber[this.cardsDegree[j]]--;
                   this.cardsTypeNumber[this.cardsType[j]]--
                   houdun.push(this.cardsTotal[j]);
                   
                 }
               }
        
               
             }
         }
             
               
       //同花
       if(max2>=5&&flag<0){
         for(var i=0;i<=3;i++){
           if(this.cardsTypeNumber[i]>=5)
           {
             
               flag=i
               
           }
         }
         for(var i=0;i<13;i++)
         {
           if(this.cardsType[i]===flag&&houdun.length<=4){
             
             //this.cardsType[i]--
             this.cardsTypeNumber[this.cardsType[i]]--
             this.cardsNumber[this.cardsDegree[i]]--
             this.cardsUse[i]=true
             houdun.push(this.cardsTotal[i])
            
           }
         }
       }
       //只有2张
       else if(max1<=2){
         for(var i=0;i<=12;i++){
          
           if(this.cardsNumber[this.cardsDegree[i]]===2&&houdun.length<4)
           {
             
             houdun.push(this.cardsTotal[i])
            this.cardsTypeNumber[this.cardsType[i]]--
            
             this.cardsUse[i]=true
               
           }
           
         }
           for(var i=0;i<=12;i++){
           if(this.cardsUse[i]===true){
             this.cardsNumber[this.cardsDegree[i]]=0
           }
         }
             for(var j=1;j<=14;j++){
                 
                 if(this.cardsNumber[j]===1)                                  
                 {
                   flag=j;
                   break
                 }
               }
           for(var j=0;j<=12;j++)
           {
             
             if(this.cardsDegree[j]===flag)
             {
               
               this.cardsUse[j]=true;
               this.cardsTypeNumber[this.cardsType[j]]--
               houdun.push(this.cardsTotal[j]);
               
             }
           }
       }
    

    分出后墩的代码(具体解释可以看前面的流程图)

    性能分析与改进

    改进思路

    图片加载的时候太慢了,(而且我图片有特别多)算法我没有用到太多的循环,性能差就差在加载资源的时候,改进思路是减小资源大小,思路是使用webp格式的图片,其具有更优的图像数据压缩算法,同等画面质量下,体积比jpg、png少了25%以上,

    性能分析图和程序中消耗最大的函数

    性能分析图

    消耗最大的函数是PlayAndShowCards()

    单元测试

    单元测试代码

    import Vue from 'vue'
    import RankingList from '@/components/RankingList'
    
    describe('RankingList.vue', () => {
    it('should render correct contents', () => {
    const Constructor = Vue.extend(RankingList)
    const vm = new Constructor().$mount()
    expect(vm.$el.querySelector('.bg .username').textContent)
     .toEqual('用户名')
     expect(vm.$el.querySelector('.bg .userscore').textContent)
     .toEqual('得分')
    })
    })
    
    
    describe('HistoryList.vue', () => {
    it('should render correct contents', () => {
    const Constructor = Vue.extend(HistoryList)
    const vm = new Constructor().$mount()
    expect(vm.$el.querySelector('.loginbg .buttons img').src)
     .toBe('http://localhost/assets/ReturnGame.png')
    
    })
    })
    
    

    单元测试的函数以及思路

    ​ 测试的是部分组件是否成功渲染 构造测试函数的思路是看图片是否能够成功渲染(为啥不测试js的函数 因为数据的内容我们两用图片显示到界面上了 最重要的是加上vue的测试我不太熟悉 学了一段时间好像还是不能熟练的拿去测试js的函数 两个人都不太会用vue的单元测试 )我们两把两个主要页面的组件都进行了测试

    GitHub的代码签入记录

    遇到的代码模块异常或结对困难及解决方法

    (ps 问题尝试解决收获与前面的点一一对应 1对应1 )

    问题描述

    1. 一开始考虑到算法占主要部分 所以打算用java来写 但是发现java界面处理和网络接口处理对我两来说有点难办,决定界面用vue 算法用java,从而实现算法和界面分离(想象美好现实残忍
    2. 用js写算法 决定用对象数组存每个卡牌的信息 后来发现。。。js的对象数组真真真真太难用了 我用数组名[i].属性名这样取一个属性值取不出 会报一个 "TypeError:cannot read property '属性名' of undefined 这样的错误
    3. vue的token又是一个完全没有接触过的知识点
    4. vue的网络接口应用不熟练,以及vuex共享数据,还有localStorage
    5. 前端界面的自适应
    6. 前端的单元测试

    做过哪些尝试

    1. 研究很多百度上的资料 发现我目前学到的知识还做不到 还要再学很多新知识(javaee的框架 可惜我servlet都还在入门 最近都没怎么有空学它了 而且这几天是来不及的学框架并且运用上的 我tcl 如果有大佬几天实现了算法和界面分离 别嘲笑我菜)
    2. 这个问题整了我好久 百度也没有百度到说明结果 去后来查了js对象数组看了几篇博客 发现要for item in 数组名[i]一个个遍历js的属性才能取 当我发现怎么解决了致命问题又来了for(var i=12;i<=0;i++)这样倒序去读这个对象数组它又报错了 我也是有小(da)脾气的人呢 我最后用好几个数组存每张卡牌信息
    3. 百度vue的token 一篇篇博客慢慢看 看实例看源码 知道了Token是什么
    4. 百度一个个研究,先写几个小的组件测试熟悉以下(
    5. 多看看样例
    6. 太难了,了解了单元测试是什么,但是如何对vue单元测试又是一个新的知识点,接着百度学习法

    是否解决

    1. 算解决了?我用js写算法 虽然会不会有点憨憨(好吧我觉得很憨)
    2. 解决了,我不用js的对象数组了!!!我用好几个数组存卡片的信息(很傻 导致代码可读性比较差)
    3. 解决了!!!
    4. 搞定了!!!
    5. 算解决一半,(自打脸,自适应不行啊,我觉得也是我这次写的代码不足之处,写到总结的时候发现自己还有好多不足需要改进)
    6. 解决了!!!

    有何收获

    1. 我明确了实现算法和界面分离还要在学哪些知识点(虽然我还做不到把她们分离)
    2. js学的不过关 对象数组都用不清楚 强化了一些js对象数组的使用方法(我觉得有点(te bie)难用 如果有人知道咋用 请指出)
    3. 本来对Token完全没概念 现在知道了token是一种身份的验证,在大多数网站中,登录的时候都会携带token,去访问其他页面,token就想当于一种令牌。可以判断用户是否登录状态。
    4. 本来不太熟悉的,这次就加深理解了。包括对localStorage的理解都有了了解(果然实践出真知哈哈)
    5. 我以为前端很简单的,这次知道了前端我还要接着学很多,太多不足了
    6. 对单元测试和vue都有了新的了解

    评价队友

    值得学习的地方

    高星身为一个前端兼职美工,眼光还是很好的

    菜鸡互啄冲压!!!

    不足的地方

    不足的地方当然是没有!! (。・∀・)ノ

    学习进度

    周数 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
    1 200+ 200+ 20 20 学了Axure,以及原型设计,学了一些js(团队作业需要所有去学习了)
    2 2000++ 2000+ 50 50 对js以及vue有了进一步了解
    3 800+ 3000+ 20 20 了解了axios以及localStorage
    4 500+ 3500+ 4 4 服务器部署

    学习新知识的资料

    资料地址 说明
    vue的Token认证 Token认证的代码实例,边看代码边学Token
    vue的axios使用 vue的axios使用详细代码,配合官网使用
    vue单元测试 vue的单元测试了解
    vue如何单元测试 讲解了一些vue如何单元测试
    vue单元测试 vue的官网单元测试说明
    前端性能 了解前端性能相关
    前端性能优化 了解前端性能如何优化

    (还有一些七零八碎的就没有列入了)

    界面

    写好的程序挂载服务器上啦,可以瞅瞅(账号密码hello,也可以自己注册)、

    自适应没写好,建议全屏,如果全屏界面还是很奇怪,(dbq我太菜了,我们两的游戏本上看正常的,如果是在什么超大的屏幕dbq)

    加载会有些慢请耐心等待(资源加载太多了)

    欢迎光临慧珺和高星的13水

  • 相关阅读:
    ubuntu18.04管理redis
    Mac Vmware虚拟机重启后没有网络
    记Spark写数据到Elasticsearch的报错
    Spark基础和RDD
    PHP日期处理
    集群命令
    hadoop集群时间同步
    HBase读写流程
    Flume简介
    Linux 常用快捷键
  • 原文地址:https://www.cnblogs.com/ronghuijun/p/11681324.html
Copyright © 2020-2023  润新知