博客链接和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封装好,导致我们两接口部分的代码不太好(简直差到没眼看)这部分应该再学习,每次写代码都发现好多不足和不会(前端没有我们两想象中的那么容易,菜是真菜)
代码组织与内部实现
- 工程结构
- src中结构(内部实现设计)
- 对代码详细说明
首先代码有五个主要界面 (登入界面,开始界面,游戏牌型显示界面,排行榜显示界面,历史记录界面)其中注册界面当作一个弹窗写在登入界面中,历史记录详情也是作为一个弹窗写在历史记录中(没有在另外写两个主要界面)
界面名称 界面功能 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 )
问题描述
- 一开始考虑到算法占主要部分 所以打算用java来写 但是发现java界面处理和网络接口处理对我两来说有点难办,决定界面用vue 算法用java,从而实现算法和界面分离(
想象美好现实残忍)- 用js写算法 决定用对象数组存每个卡牌的信息 后来发现。。。js的对象数组真真真真太难用了 我用
数组名[i].属性名
这样取一个属性值取不出 会报一个 "TypeError:cannot read property '属性名' of undefined 这样的错误- vue的token又是一个完全没有接触过的知识点
- vue的网络接口应用不熟练,以及vuex共享数据,还有localStorage
- 前端界面的自适应
- 前端的单元测试
做过哪些尝试
- 研究很多百度上的资料 发现我目前学到的知识还做不到 还要再学很多新知识(javaee的框架 可惜我servlet都还在入门 最近都没怎么有空学它了 而且这几天是来不及的学框架并且运用上的 我tcl 如果有大佬几天实现了算法和界面分离 别嘲笑我菜)
- 这个问题整了我好久 百度也没有百度到说明结果 去后来查了js对象数组看了几篇博客 发现要
for item in 数组名[i]
一个个遍历js的属性才能取 当我发现怎么解决了致命问题又来了for(var i=12;i<=0;i++)
这样倒序去读这个对象数组它又报错了 我也是有小(da)脾气的人呢 我最后用好几个数组存每张卡牌信息- 百度vue的token 一篇篇博客慢慢看 看实例看源码 知道了Token是什么
- 百度一个个研究,先写几个小的组件测试熟悉以下(
- 多看看样例
- 太难了,了解了单元测试是什么,但是如何对vue单元测试又是一个新的知识点,接着百度学习法
是否解决
- 算解决了?我用js写算法 虽然会不会有点憨憨(好吧我觉得很憨)
- 解决了,我不用js的对象数组了!!!我用好几个数组存卡片的信息(很傻 导致代码可读性比较差)
- 解决了!!!
- 搞定了!!!
- 算解决一半,(自打脸,自适应不行啊,我觉得也是我这次写的代码不足之处,写到总结的时候发现自己还有好多不足需要改进)
- 解决了!!!
有何收获
- 我明确了实现算法和界面分离还要在学哪些知识点(虽然我还做不到把她们分离)
- js学的不过关 对象数组都用不清楚 强化了一些js对象数组的使用方法(我觉得有点(te bie)难用 如果有人知道咋用 请指出)
- 本来对Token完全没概念 现在知道了token是一种身份的验证,在大多数网站中,登录的时候都会携带token,去访问其他页面,token就想当于一种令牌。可以判断用户是否登录状态。
- 本来不太熟悉的,这次就加深理解了。包括对localStorage的理解都有了了解(果然实践出真知哈哈)
- 我以为前端很简单的,这次知道了前端我还要接着学很多,太多不足了
- 对单元测试和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)
加载会有些慢请耐心等待(资源加载太多了)