第二次结对编程作业
一、
王镇隆 (博文链接)(Github项目地址)fork了珊珊的仓库
陈珊珊 (博文链接)(Github项目地址) 主仓库(评分请查看此仓库,其余连个仓库均为辅助仓库,内容不全)
吴珂雨 (博文链接)(Github项目地址) fork了珊珊的仓库
二、给出具体分工
陈珊珊:主要负责前端代码,写出主要框架,对算法进行性能分析,提供部分文档资料。
吴珂雨:主要负责前端代码,美化页面并且进行定位和交互处理,文档撰写。
王镇隆:主要负责后端代码,进行单元测试,以及接口的使用,提供部分文档资料。
我们在分析和确定需求后,开始准备实现各自负责的部分,并学习需要使用到的技术。接着逐步完成各个功能,进行性能分析和单元测试,并编写博客。
三、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 75 |
· Estimate | · 估计这个任务需要多少时间 | 60 | 75 |
Development | 开发 | 15740 | 19620 |
· Analysis | · 需求分析 (包括学习新技术) | 7200 | 7920 |
· Design Spec | · 生成设计文档 | 150 | 180 |
· Design Review | · 设计复审 | 50 | 75 |
· Coding Standard | · 代码规范 (为目前的开发制定或选择合适的规范) | 120 | 150 |
· Design | · 具体设计 | 480 | 600 |
· Coding | · 具体编码 | 7200 | 10080 |
· Code Review | · 代码复审 | 240 | 255 |
· Test | · 测试(自我测试,修改代码,提交修改) | 300 | 360 |
Reporting | 报告 | 150 | 130 |
· Test Report | · 测试报告 | 60 | 45 |
· Size Measurement | · 计算工作量 | 40 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出改进计划 | 50 | 55 |
· 合计 | 15950 | 19825 |
四、解题思路描述与设计实现说明
(1)网络接口的使用
因为我们的前端是使用js写的,所以接口是js的类型,因为有样例所以比较简单。
具体说明在以下代码中:
var data = JSON.stringify({
"id":id_1,
"card":[qans1,qans2,qans3]
});
//data根据是否需要提交数据,如果不用则改为null
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
console.log(this.responseText);
var JsonObject = JSON.parse(this.responseText);
//在这里加入根据返回的值进行的行为
}
});
xhr.open("POST", "https://api.shisanshui.rtxux.xyz/game/submit");//选择POST或者GET
xhr.setRequestHeader("x-auth-token",token); //token也是根据需要,决定这句要不要
xhr.setRequestHeader("content-type", "application/json");//json也是根据需要决定这句要不要
xhr.send(data);
(2)代码组织与内部实现设计(类图)
(3)说明算法的关键与关键实现部分流程图
算法关键:
Step.1将牌型处理成数组,进行排列组合,使得所有的牌型都计算出来。
Step.2在保留一个最大的得分值和得分牌型,之后遍历所有的组合,计算每一种牌型得分,如果这种牌型符合条件,且得分大于之前的最大得分,则更新最大的得分值和得分牌型。
Step.3便利完所有牌型之后,最大的得分牌型就是我们要出的牌型。
流程图:
五、关键代码解释
(1)判断出取十三张牌并分成三墩的所有可能情况:
用used数组来判断这十三张牌分别是属于哪一墩的,1表示属于为前墩,0表示属于中墩,2表示属于后墩。
def find2(pos, n):
if pos == 13 and n == 0:
score()
return True
if pos == 13:
return False
while used[pos] == 1:
pos += 1
if pos == 13:
return False
if n > 0:
used[pos] = 2
find2(pos + 1, n - 1)
used[pos] = 0
find2(pos + 1, n)
def find1(pos, n):
global ans
if pos == 13 and n == 0:
find2(0, 5)
return True
if pos == 13:
return False
if n > 0:
used[pos] = 1
find1(pos + 1, n - 1)
used[pos] = 0
find1(pos + 1, n)
(2)获取一墩牌的分数的算法:
score1()用于判断前墩的牌型和获取前墩的分数:
用数组a[]表示牌上的数字
if a[0]==a[1]and a[1]==a[2]
表示三张牌一样大
if a[0]==a[1] or a[1]==a[2]
表示对子
剩下的情况就是乌龙
def score1(a, b):
if a[0]==a[1]and a[1]==a[2]:
return 3+(a[0]+a[1]+a[2])/10000
if a[0]==a[1] or a[1]==a[2]:
return 2+(a[0]+a[1]+a[2])/10000
return 1+(a[0]+a[1]+a[2])/10000
score2()用于判断中墩和后墩的牌型和获取中墩和后墩的分数:
用数组a[]表示牌上的数字,数组b[]表示花色
用if语句和node标记判断是哪一种牌型,具体在以下代码中注释
def socre2(a,b):
ex = 0
for i in a:
ex += i
ex = ex / 10000
node = 1
for i in range(4)://判断是否为顺子,花色是否相等
if a[i]+1 != a[i+1] or b[i]!=b[i+1]:
node = 0
break
if node == 1://表示该墩牌型为同花
return 15+ex
node = 1
for i in range(4)://判断是否为顺子
if a[i]+1 != a[i+1]:
node = 0
break
if node == 1://表示该墩牌型为同花顺
return 13+ex
node = 1
for i in range(3)://判断是否有四张牌大小一致
if a[i] != a[i+1]:
node = 0
break
if node == 1://表示该墩牌型为炸弹
return 10+ex
node = 1
for i in range(1,4)://判断是否有三张牌大小一致
if a[i] != a[i + 1]:
node = 0
break
if node == 1:
return 10+ex
if a[1]==a[2] and a[1] == a[0] and a[3]==a[4]://表示该墩牌型为葫芦
return 8+ex
if a[4] == a[3] and a[3] == a[2] and a[1] == a[0]://表示该墩牌型为葫芦
return 8+ex
node = 1
for i in range(4):
if b[i] != b[i + 1]://判断花色是否相等
node = 0
break
if node == 1:
return 6+ex
if a[0]+1==a[1]and a[1]+1==a[2]and a[2]+1==a[3]and a[3]+1==a[4]://表示该墩牌型为顺子
return 5+ex
if (a[0]==a[1]and a[1]==a[2])or (a[2]==a[1]and a[2]==a[3]) or (a[2]==a[3]and a[3]==a[4])://表示该墩牌型为三条
return 4+ex
node =0
for i in range(4):
if a[i]==a[i+1]://表示该墩牌型为二对子
node +=1
if node !=0:
return node+ex//表示该墩牌型为对子
return 1+ex//表示该墩牌型为乌龙
六、性能分析与改进
(1)改进思路
改进前的思路:
先进行判断,判断是否为特殊牌型,之后再枚举所有牌型,得出最大出牌方案。
存在问题:
然后发现有些牌型比如三顺子之类的本来就是最大的牌型,在最开始进行一个特殊牌型的判断其实是没有必要的,会造更多的消耗。
改进后的思路:
直接枚举所有牌型得出的方案就会是最大牌型,减少了消耗。
(2)性能分析图
(3)消耗最大的函数
由以上性能分析图可以得出消耗最大的函数为score()。
def score():
num1=0;num2=0;num0=0
for i in range(13):
if used[i]==0:
num0+=1
elif used[i]==1:
num1+=1
else:
num2+=1
global maxnc,qans2,qans1,qans3,ttt
a = [];b = [];c=[];d=[];e=[];f=[]
for i in range(13):
if used[i] == 1:
a.append(pai[i])
b.append(paise[i])
ans1 = score1(a,b)
for i in range(13):
if used[i] == 0:
c.append (pai[i])
d.append(paise[i])
for i in range(4):
for j in range(4-i):
if c[j] > c[j+1]:
c[j],c[j+1] = c[j+1],c[j]
d[j],d[j+1] = d[j+1],d[j]
ans2 = socre2(c,d)
for i in range(13):
if used[i] == 2:
e.append(pai[i])
f.append(paise[i])
for i in range(4):
for j in range(4-i):
if e[j] > e[j+1]:
e[j],e[j+1] = e[j+1],e[j]
f[j],f[j+1] = f[j+1],f[j]
ans3 = socre2(e,f)
if ans1<=ans2 and ans2<=ans3:
if ans1+ans2+ans3 > maxnc:
maxnc = ans1+ans2+ans3
qans1 = lol(3,a,b)
qans2 = lol(5,c,d)
qans3 = lol(5,e,f)
七、单元测试
(1)测试函数说明:
单元测试 | 测试项 | 被测试代码 | 功能 |
---|---|---|---|
test_lol | 分别测试3张牌和5张牌的情况 | main.py | 将牌型转换成字符串 |
test_score2 | 测试5张牌的情况 | main.py | 将牌型转换成分数 |
(2)单元测试代码:
进行了十组单元测试,单元测试代码如下:
import unittest
from run_main import *
class TestRun_main(unittest.TestCase):
def test_lol(self):
self.assertEqual("&J*Q$K", lol(3, [11, 12, 13], [0,3,1]))
self.assertEqual("&10$A#A*A&A", lol(5, [10, 14, 14, 14, 14], [0, 1, 2, 3, 0]))
self.assertEqual("&10&J&Q&K&A", lol(5, [10, 11, 12, 13, 14], [0, 0, 0, 0, 0]))
self.assertEqual("&10$J&Q*K#A", lol(5, [10, 11, 12, 13, 14], [0, 1, 0, 3, 2]))
self.assertEqual("&7$7&8*8#10", lol(5, [7, 7, 8, 8, 10], [0, 1, 0, 3, 2]))
def test_score2(self):
self.assertEqual(12.0025, socre2([3, 4, 5, 6, 7], [0, 0, 0, 0, 0]))
self.assertEqual(10.0019, socre2([3, 3, 3, 3, 7], [0, 1, 2, 3, 0]))
self.assertEqual(6.0027, socre2([2, 3, 4, 8, 10], [0, 0, 0, 0, 0]))
self.assertEqual(5.002, socre2([2, 3, 4, 5, 6], [0, 2, 0, 2, 1]))
self.assertEqual(4.0029, socre2([5, 5, 5, 6, 8], [0, 1, 3, 0, 2]))
if __name__ == '__main__':
unittest.main()
(3)测试结果:
(4)构造测试数据的思路:
test_lol部分:考虑到有3张牌的情况和5张牌的情况,5张牌的情况更多,所以5张牌的测试数据更多,3张牌的数据采用了乌龙,5张牌的数据采用了二对子、顺子、铁支、同花顺四种情况,这样乌龙和具有牌型的情况都能够被测试到。
test_score2部分:函数本身是用于进行中墩和后墩的分数转换,因为5张牌的情况更多,所以用了这个函数,数据采用了同花顺、铁支、乌龙、顺子、三条五种情况,这样乌龙和具有牌型的情况都能够被测试到。
八、Github的代码签入记录
九、遇到的代码模块异常或结对困难及解决方法
(1)牌型处理
问题描述:
在进行牌型处理的时候,由于十三张牌需要用字符串来表示,不知道要如何用代码去表示和处理扑克牌及其花色,如果这个问题不能得到解决,算法将完全不能得到推进。
做过哪些尝试:
首先想到用字符来表示,后面改成用二维数组表示某种花色、某种牌拥有,但效果都不够理想,都有一部分优点,但也有缺点。
是否解决:
是,解决了。最后使用了最简单的方法,利用两个数组,第一个数组用来表示牌的数字,第二个数组用来表示牌的花色。
有何收获:
遇上困难或者瓶颈的时候,要和队友多沟通,自己一个人一直想如何去解决难题,很容易钻牛角尖,这时候,队友以旁人的视角来看待问题,可能更容易想出好的解决方法。
(2)制作弹窗
问题描述:
和队友商量之后决定把一些做好的页面比如排行榜、往期对战结果等页面改成弹窗,会达到更好的效果。但是发现事情没有想象中那么简单,由于我们设计的往期对战结果详情是要在往期对战结果记录页面点击战局ID按钮才能够打开,但是往期对战结果已经是一个弹窗了,也就是说要在一个弹窗里显示另外一个弹窗,因为这次作业才学了前端,对弹窗的制作不熟悉,不知道代码应该放在该按钮的前面还是后面。
做过哪些尝试:
首先尝试将代码放在了div按钮的前面,结果发现不行,会改变原有弹窗的样式。接着尝试放在了div按钮的中间,但是显示出来的内容不是在弹窗中。
是否解决:
是,解决了。最后选择放在了div按钮的后面。
有何收获:
通过这次作业,学会了弹窗的使用,前端的很多知识仍需学习。
(3)弹窗重合
问题描述:
在弹框中再制作一个弹框,但是发现会和原来的弹窗重合,所以想要设计点击弹窗中的一个按钮,跳出一个弹窗,并将原有的弹窗遮盖住。
做过哪些尝试:
尝试调整弹窗背景的颜色,透明度,但是都没有成功。
是否解决:
是,解决了。制作了一个遮罩,调整其中中一个变量z-index的大小,设置成9999的时候可以完全遮盖住原来的弹窗,如果是设置成-1,则成透明。
有何收获:
学会了如何使用遮盖,使得页面的效果更好,仍需多多了解一些前端的属性,让自己能够将前端做得更好。
十、评价你的队友
总体评价:
这是一次非常愉快的合作。
首先在作业布置下来以后我们会开一次会进行分工,沟通使用什么技术去实现自己负责的一部分内容,在实现各部分功能的过程中我们也会在群里进行遇到的问题的反馈一起探讨。
并且分工明确,大家各司其职,互相配合,互相帮助。
最重要的是队友都非常的随和靠谱,从不会说“不”,都会尽力完成自己负责的部分,并且为队友提供解决困难的想法。
DDL前和队友一起在活动室熬夜的感觉挺有趣的,切实体会到了第一次博客作业中提到的“一起熬夜,一起吐槽,一起说说笑笑,一起打代码”的感觉,学到和很多东西,也收获了美好的回忆。
(1)吴珂雨:
值得学习的地方:
珂雨想问题的时候想得都很全面,我们两个是一起做前端的,她常常会注意到页面中的一些细节的处理,比如说我们一开始把所有的东西都做成了页面,她想到这样可能后端在做数据的时候可能会比较麻烦,然后我们两个就一起把页面做成了弹窗,减轻后端的工作量,合作起来很愉快!
需要改进的地方:
可能也许,在时间比较紧迫的时候,可以少那么一点点纠结的时候,不过追求完美是对工作认真的表现!
(2)王镇隆
值得学习的地方:
镇隆的代码能力很强,主要负责这次的算法部分,而且他的学习能力也很强,在没有前端的基础上,就承担了写接口的部分,对我和珂雨的问题也会认真地解释,性格很好,为人随和,是一个很好的合作伙伴。
需要改进的地方:
在文案方面相对弱一些,表达比较的简洁,但是通过他的解释,我们都能理解他的意思,不过这样我们三个人在能力方面刚好可以互补!
十一、学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 14 | 14 | 学习了相关的一些语言 |
2 | 800 | 800 | 30 | 44 | 学会了异常处理、Json格式等以前没有遇到过的问题 |
3 | 0 | 800 | 18 | 62 | 学会了使用Axure设计界面 |
4 | 0 | 800 | 53 | 115 | 学习html、css、js的使用 |
5 | 2000+ | 2800+ | 81 | 196 | 真正进行前端的制作 |
6 | 1000+ | 3800+ | 20 | 216 | 对初步做好的前端界面进行修改,并且学会了一些有关github的操作 |
… | … | … | … | … | … |