结对编程作业
https://github.com/jasfa/Catch_star_tail
姓名 | 个人博客链接 | 具体分工 |
---|---|---|
方静怡 | https://www.cnblogs.com/jasf/p/15452094.html | 原型设计、前端界面 |
张妍 | https://www.cnblogs.com/littleseasalt/p/15440039.html | AI算法、游戏逻辑 |
一、原型设计
[2.2.1]提供此次结对作业的设计说明,要求:
-
采用的原型开发工具:墨刀
-
图文并茂
- 说明与演示
-
加载游戏,进入【首页界面】,点击开始游戏后进入【获取用户信息界面】,点击取消,返回【首页界面】,点击确定,进入【模式选择界面】
-
在【模式选择界面】中,点击人人对战,进入【人人对战界面】;点击人机对战,进入【人机对战界面】
-
在【模式选择界面】中,点击在线对战,进入【在线对战登录界面】,输入学号和密码后点击确定,进入【在线对战选择界面】,点击创建房间后,进入【在线对战创建对局界面】
-
在【在线对战创建对局界面】中,获取到对局id后,点击进入,进入【在线对战创建对局等待界面】,等待别人加入,连接上后进入【对战界面】
-
在【在线对战创建对局界面】中,点击加入房间后,进入【在线对战加入对局界面】,点击加入房间后,进入【在线对战加入对局界面】,输入对局id后,点击确定,进入【对战界面】。对战结束后,跳转至【结算页面】,查看对局详情,点击选择模式,回到【模式选择页面】
-
- 说明与演示
[2.2.2]遇到的困难及解决方法:
-
困难描述:
在设计时不知道有的组件应该放哪,什么样的界面能更加适用于用户,使使用者看着更舒服,体验感更好。
-
解决过程:
与队友之间采用客户端和服务端的模式,把队友当作我的客户,我要尽可能满足我的客户对产品的需求。
-
有何收获:
通过制作原型设计,对原型设计有了更深刻的了解,并且掌握了Axure和墨刀两种原型设计软件的使用,并从中获得乐趣以及满足,极大提高热情度。
二、原型设计实现
[2.3.1]代码实现思路:
- 网络接口的使用
本次作业主要是应用微信开发者工具来完成一个微信小程序,小程序编写的主要是使用javescript、wxml和wxss这三种语言。本次网络接口的使用就是通过微信小程序自带的API——“wx.request”来接入接口。提供的接口中,主要分为两类,一类是登录接口,一类是用于实现在线游戏的接口。登录接口返回的“token”是其他接口能够使用的前提,除登录接口外的接口都需要再“header”中加入获取的“token”,不然则会返回鉴权错误。以下展示两个调用接口的代码:登录接口及创建对局接口。
//登录接口的调用
wx.request({
url: 'http://172.17.173.97:8080/api/user/login',
data: {
student_id:this.data.student_id,
password:this.data.password,
},
header: {
"Content-Type": "application/x-www-form-urlencoded" ,//用于post
},
method: 'POST',
success: function (res) {
console.log("res", res);
if(res.data.status == "200"){
app.globalData.token = res.data.data.token
wx.setStorageSync('token',res.data.data.token) //将获取的token存入本地缓存中,便于其他接口使用
wx.showToast({
title: '登陆成功',
icon: 'success',
duration: 1500
})
//登录成功跳转页面
wx.navigateTo({
url: '/pages/onlineselect/onlineselect',//跳转到模式选择页面
})
}
else{
wx.showToast({
title: '账号或密码错误',
icon: 'error',
duration: 1500
})
}
},
fail: function (res) {
wx.showToast({
title: '出错了,请重试',
icon: 'error',
duration: 1500
})
},
complete: function (res) { },
})
//创建对局接口
wx.request({
url: 'http://172.17.173.97:9000/api/game',
data: {
"private": true
},
header: {
"Authorization": wx.getStorageSync('token') //使用之前获取的token
},
method: 'POST',
success: function (res) {
console.log("res", res);
app.globalData.uuid = res.data.data.uuid //将uuid存储,提供给他人加入房间
console.log(app.globalData.uuid)
wx.showToast({
title: '创建成功',
icon: 'success',
duration: 1500
})
},
fail: function (res) {
console.log("res", res);
},
complete: function (res) { },
})
-
代码组织与内部实现设计
卡牌相关变量:
(1) stack_area[]//卡组剩余牌
(2) place_area[]//放置区剩余牌
(3) player1_area[]//玩家一手牌剩余牌
(4) player2_area[]//玩家二手牌剩余牌
(5) cnt_stack_area//卡组剩余牌数
(6) cnt_place_area//放置区剩余牌数
(7) cnt_player1_area//玩家一手牌剩余牌数
(8) cnt_player2_area//玩家二手牌剩余牌数
对局相关函数:
(1) match_stack()//卡组匹配操作
(2) match_player1()//玩家一手牌匹配操作
(3) match_player2()//玩家二手牌匹配操作
(4) eat_all1()//玩家一吃牌操作
(5) eat_all2()//玩家二吃牌操作
(6) host1()//玩家一托管操作
(7) host2()//玩家二托管操作
(8) analyse_op()//解析报文操作
(9) push_op()发送报文操作
-
说明算法的关键与关键实现部分流程图
整个游戏的核心算法在于对局的实现。对局分为两种,一种是本地的,一种是在线。这两者最大的差别在于,本地翻开卡组的时候可以立即获取卡组顶端是什么,而在线则需要先把翻开的操作put到接口中,通过监听函数的返回值得到翻开的牌是什么,再对页面和数据进行操作修改。本地的算法很简单,构造match_stack()和match_player_area()函数即可,点击事件直接触发函数调用,再修改轮次的值,避免非法操作即可。在线的算法则是出了之前的本地已有的两个函数外,还需要在构造touch_stack()和touch_player_area()函数,此时点击事件触发的函数则是这两个了,这两个会调用一个发送报文的函数,然后监听函数一旦收到正确的报文就进行解析,将解析出来的数据送入match_stack()和match_player_area()函数。
- 贴出你认为重要的/有价值的代码片段,并解释
match_stack1(){
var flag = 0
var place_area0 = this.data.place_area
var stack_area0 = this.data.stack_area
if(place_area0.length > 0){ //放置区有牌
var tmp1 = parseInt(place_area0[0] / 13)
var tmp2 = parseInt(stack_area0[0] / 13)
//console.log('tmp1:',tmp1,'tmp2:',tmp2)
if(tmp1 === tmp2 && place_area0.length > 0){ //花色相同且放置区有牌
flag = 1
place_area0.unshift(stack_area0[0]); //向数组的开头添加一个元素
stack_area0.shift(); //将数组中的数整体下移
this.setData({
stack_area:stack_area0
})
this.setback_stack()
//this.update_stack()
this.eat_all1()
}
}
if(flag === 0){ //放置区没牌
var tmp=stack_area0[0]
stack_area0.shift();
place_area0.unshift(tmp);
this.setData({
place_area:place_area0
})
this.setback_stack()
this.update_place()//更新放置区手牌
this.setData({
stack_area:stack_area0
})
this.setback_stack()
//this.update_stack()//更新卡组
}
},
这个函数主要的作用是用于玩家选择翻开卡组顶部时匹配卡组顶部和放置区顶部的,无论是出牌或者是抽牌,匹配的机制其实时一样的,我认为这段代码最具有价值是因为这其实是这个游戏能实现的核心,只有能做到花色匹配并且实现吃牌这一操作,游戏才能顺利进行。在这个函数中可以看出判断花色是否相同,是通过判断牌面对应值除以13后向下取整是否相同,在初始化的时候我选择将每种花色分布在同一个区间,使得同花色的值除以13并向下取整后会得到相同的值,便于匹配,其中我的设置是012,1325,2638,3951分别对应了红心、方片、黑桃和草花。
-
性能分析与改进
- 展示性能分析图和程序中消耗最大的函数
listen(){
let that = this
wx.request({
url: 'http://172.17.173.97:9000/api/game/' + app.globalData.uuid + '/last',
data: {
},
header: {
"Content-Type": 'application/json',
"Authorization": wx.getStorageSync('token'),
},
method: 'GET',
success: function (res) {
console.log("res", res);
if(res.data.code == "200"){
if(res.data.data.your_turn != that.data.your_turn ){
that.data.last_code = res.data.data.last_code
that.data.your_turn = res.data.data.your_turn
that.analyse_op();
}
}
else{
wx.request({
url: 'http://172.17.173.97:9000/api/game/' + app.globalData.uuid,
data: {
},
header: {
"Content-Type": 'application/json',
"Authorization": wx.getStorageSync('token'),
// "Content-Type": "application/x-www-form-urlencoded" 用于post
},
method: 'GET',
success: function (res) {
console.log("res", res);
if(res.data.code == "200"){
that.getcnt();
wx.navigateTo({
url: '/pages/jiesuan/jiesuan?cnt1='+cnt1+'&cnt2='+cnt2
})
}
},
fail: function (res) { },
complete: function (res) { },
})
}
},
fail: function (res) {
wx.showToast({
title: '出错了,请重试',
icon: 'error',
duration: 1500
})
},
complete: function (res) { },
})
},
上面这个函数是用于在线对局时监听报文的,它的作用时获取上一步操作,整个在线对局能够实现主要靠的就是获取的上一步操作,所以这个获取必须及时,那么监听的频率就要比较高,避免出现获取报文来不及处理的这种状况。
性能分析采用的是微信开发者中小程序自带的Audit,根据上面的性能分析图可以看出整个程序的性能还是很好的,其中没有通过性能检测的部分是因为this.setData()这个函数调用的次数太多了。
- 描述你改进的思路
改进的思路是将程序中调用this.setData()的部分涉及到的数据不再每次都先取出this.data中的数据,修改后再赋值回去,而是直接对this.data中的数据进行修改,这样后续就不需要调用this.data()这个函数赋值回去了,自然而然就减少了调用次数。
- 展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路
[2.3.2]贴出Github的代码签入记录,合理记录commit信息。
[2.3.3]遇到的代码模块异常或结对困难及解决方法。
-
困难描述:
实现在线对战的时候,一直获取不到正确的返回信息,除了登录接口外一直报错。
-
解决过程:
首先上网查找了类似问题,根据CSDN上的许多篇博客进行尝试,但仍然报错
(究其原因是我对于微信小程序编写这一块内容只是速成,并不熟练,出现了问题也不知道如何解决),后来通过求助测试组的同学和身边同样是做小程序的同学了解到,需要将返回的token放入本地缓存,且使用特定方法存取,才能得到正确的返回信息。 -
有何收获:
让我对于调用各种接口的方法有了更深层次的认识和理解,在实际操作中的碰壁和犯错,才能更加巩固自己的知识和提升自己的能力;还有除了网络,要懂得向他人求助。
[2.3.4]评价你的队友。
评价方静怡:
-
值得学习的地方:
(1)善于加恰当的注释使得后期找bug的时候会快一些;(2)对于原型设计这块比较细节,会去精美化一些页面,愿意花很多时间去扣细节,(3)原型设计的主题选择时,会有较多的想法,可以产生多种选择;(4)态度积极,不会轻言放弃;(5)时间安排上会更合理一些。
-
需要改进的地方:
偶尔过于在意小细节,忽视了主体的问题。
评价张妍:
-
值得学习的地方:
我感觉队友对我来说非常重要,真的没有想过我们俩在ddl驱动下能这么快做出前期看起来无法实现的东西,队友在算法上的能力强,debug能力也很强,而且能将看起来比较抽象的东西具体成一个个函数实现,我觉得这方面是我值得学习的地方,在冲刺ddl的过程中我们俩一起努力,一起熬夜,互相鼓励冲刺
-
需要改进的地方:
变量命名不清楚造成了后面的许多小错误,找了好久!!
[2.3.5]提供此次结对作业的PSP和学习进度条(每周追加)
PSP表格:
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 45 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 45 |
Development | 开发 | 2860 | 3300 |
· Analysis | · 需求分析 (包括学习新技术) | 840 | 1040 |
· Design Spec | · 生成设计文档 | 60 | 80 |
· Design Review | · 设计复审 | 100 | 100 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 80 |
· Design | · 具体设计 | 60 | 100 |
· Coding | · 具体编码 | 1500 | 1600 |
· Code Review | · 代码复审 | 120 | 180 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 135 | 135 |
· Test Repor | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 15 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 60 |
· 合计 | 3025 | 3480 |
方静怡:
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 3 | 3 | 原型设计主题确定,收集素材 |
2 | 0 | 0 | 24 | 27 | 制作初版的原型设计 |
3 | 500 | 500 | 13 | 40 | 学习微信小程序基本语言js,wxml,wxss,实现初步的原型设计 |
4 | 1000 | 1500 | 18 | 58 | 优化界面,修复异常,页面跳转 |
张妍:
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 1 | 1 | 熟悉规则,研读题目 |
2 | 0 | 0 | 8 | 9 | 学习微信小程序基本语言js,wxml,wxss |
3 | 1000 | 1000 | 10 | 19 | 实现本地人人对战 |
4 | 3500 | 4500 | 30 | 49 | 实现在线对战,增加ai算法 |
三、心得
方静怡:
- 在这次作业过程中,我自己大概经历了三个阶段。
- 首先是摆烂躺平期。刚发布作业的时候,发现时间竟然有一个月,又碰上假期,再加上网课,使我变得懒惰,在拖延症的作用下,就想着说,要不然先放一放,先歇一歇,反正时间还长,后面肯定会完成的,而且这个题目第一眼看上去感觉,这真的是我能实现的吗,在畏难情绪的驱使下,于是就一拖再拖。
- 接着是原型设计期。
在躺平了一段时间后终于反应过来要开始完成作业了,仔细读题后,发现竟然有点深奥,和队友打了几局后才渐渐明白其中的玩法,然后大概和队友分配了一下要做什么。
在分配任务时,因为没有深刻理解题意,同时没有仔细思考要怎么做,要怎么实现,会用到什么,具体要学习什么,总的来说就是读题不仔细,思考不透彻,研究不深入,所以竟然只是分配了她后端我前端,没有具体的考虑,这个功能谁实现,那个功能谁实现。
在这简略的分配之后,我首先开始了原型设计,讨论了游戏以宇航员为主题后,就开始了漫漫寻素材路,一开始激情特别高,搜索了用什么实现原型设计后,直接看了一整个晚上的网课学习了Axure,然后就到处搜集素材,开始绘制界面。寻找素材是相当痛苦的,我的目标是符合设想,符合主题,图片高清,时常翻找了许久都找不到一张符合我要求的图,在制作扑克牌时,由于我们想设计有特色的扑克牌,所以我只能一张一张将图片用ps抠图然后绘制扑克牌,而在寻找素材时,最后实在找不到了只能氪金了。绘制了四个界面后,我渐渐发现了这个软件有个不好的地方,就是许多组件在组件库中都找不到,而且字体有限,看起来太生硬了。于是,在舍友的推荐下,我转去用了墨刀做原型设计。- 再者是压力山大期。这段时间感觉突然多了特别多的课业压力,数学建模、数据库、linux、SDN等等,每一项都要做好久好久,感觉每天都在被ddl追着跑,把我一个人分成十个我都做不完,我也知道我们必须开始做原型设计实现了,但是真的抽不出时间,心有余而力不足。
- 最后是作业冲刺期了。好不容易将除了软工以外的所有ddl熬过去,马不停蹄地开始研究怎么实现,这时距离作业截止只有10天左右了,那时我根本不敢想到底能不能实现,只能硬着头皮熬夜看了2天关于微信小程序的介绍,这时才和队友具体分清楚你做什么我做什么。我主要负责界面设计,一开始因为并没有具体学过css、html、js的具体语法,只能靠稀缺的网课知识和用到什么搜什么的精神,实现前几个界面,所以有些代码规范写的不太好,然后到了对战界面就要和队友一起了,这一整周我们每天都在熬夜,甚至一起看过了凌晨四点的福大,真的蛮后悔没有早点开始的,放到最后把自己折磨的浑浑噩噩的。值得反思的是,要是能把任务平均分配到前中后期就不会使后期如此极限冲刺了。不过,最后总算还是能实现,而且感觉还不错,基本能实现原型设计中的东西,确实挺有成就感的。
张妍:
(1)刚拿到作业的时候其实是比较懵的,首先这个棋牌游戏并不大众,甚至可以说在做这次作业之前我没有见过这个游戏。由于对游戏十分陌生,最开始的一段时间就为了弄懂游戏的规则玩法,思索有没有一种比较优的策略耗费了很多的时间。通过和同学几次尝试性试玩以及在网络上搜到这个游戏的一些对战视频后,最终弄懂了游戏规则。
(2)结对作业和队友分工后,我负责的是AI算法和游戏逻辑实现部分。由于负责AI算法,我尝试性通过多实践来得到较优的策略,但是最终其实也没找一个很优的算法,因为这个游戏感觉运气成分太大了,你不知道后续会翻出什么牌导致你无法确定做下当前的决策,这是我觉得它与斗地主之类的棋牌游戏最不一样的地方。(所以我觉得我最后写的AI算法其实挺假的,我只推出了必胜局面,但不知道如何构造这个必胜局面)。
(3)吸取上次的教训,不再只停留于自己会的东西,这次选择了花费几天的时间先速成小程序相关的知识(感觉如果像之前一样只用C++写可能写不出来了),小程序主要用了js,wxml,wxss这三种语言,网课速成起来其实还是很快的(万事开头最难,只要开了那个头,后面会好很多)。
(4)感觉自己代码重复的地方太多了,很多地方可以优化成一个函数,被我直接分类写了,导致总码量看着很可怕,码量大也导致debug增加了难度;其实知道每个匹配机制的原理是相同的,如果优化封装成类,理论上每个对战页面的1000+行代码可以优化掉大半,但是因为时间安排不合理,没来得及优化了。写法很暴力,相当于是在枚举每一种情况,其实几千行代码中有一大半是通过复制黏贴修改细节的(但是有时修改着修改着会漏,导致后期花了大量时间debug)。
(5)时间的安排要合理一点,这次因为前期没有怎么做出个雏形,导致最后几天都是在熬夜写,熬夜debug,甚至快通宵的地步。