1、博客链接
- 结对同学的博客链接: 张逸杰 吴智勇
- 本作业博客链接
- Github项目地址
2、具体分工
- 张逸杰:负责前端Ui的设计实现
- 吴智勇:负责后端的算法
- 刘汪洋:负责网络接口的实现及博客撰写
3、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 60 |
Estimate | 估计这个任务需要多少时间 | 20 | 30 |
Development | 开发 | 360 | 480 |
Analysis | 需求分析 (包括学习新技术) | 100 | 120 |
Design Spec | 生成设计文档 | 10 | 20 |
Design Review | 设计复审 | 15 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 60 | 60 |
Design | 具体设计 | 180 | 200 |
Coding | 具体编码 | 2500 | 3000 |
Code Review | 代码复审 | 60 | 60 |
Test | 测试(自我测试,修改代码,提交修改) | 80 | 100 |
Reporting | 报告 | 120 | 180 |
Test Repor | 测试报告 | 30 | 45 |
Size Measurement | 计算工作量 | 30 | 45 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 40 | 60 |
合计 | 3635 | 4480 |
4、解题思路描述与设计实现说明
网络接口的使用
调用 Pycharm自带的requests库中的get/post函数实现get/post请求,把登录、注册、出牌、排行榜等操作所需要的请求直接写在了窗口变换的主函数中,使用时直接运行主函数"十三水"即可。
具体代码示例:
- 登录接口:
global token,user_id
url = "http://api.revth.com/auth/login"
headers = {'content-type': "application/json"}
data = {
"username": username,
"password": password
}
res = requests.post(url, data=json.dumps(data), headers=headers)
info = res.json()
if info['status'] == 0:
token = info['data']['token']
user_id = info['data']['user_id']
else:
print('登录失败,请检查你的密码是否正确!')
- 发牌接口:
def dzjm_fp():#对战界面发牌功能
global cards
url = 'http://api.revth.com/game/open'
headers = {'x-auth-token': n}
res = requests.post(url, headers=headers)
info = res.json()
print('开启战局')
id = info['data']['id']
print(id)
cards = info['data']['card']
print('当前手牌:', cards)
代码组织与内部实现设计
前端代码组织与内部实现设计
利用Qt designer将设计好的界面自动转化为py文件,再自己编写界面的转换,运行及连接服务器的接口代码。
后端代码组织与内部实现设计
1.首先先判断是否是特殊牌型,如果是的话,直接分成三墩输出。
2.不是特殊牌型的话,把这十三张牌随机先取出五张做底墩,再取出五张牌做中墩,剩下三张做前墩,之后判断是否会出现倒水的情况,如果会,则舍弃,如果不会,如果是第一个,则直接记录为最大牌型,如果不会是第一个,则与之前的最大牌型进行比较,胜出则取代其成为最大牌型。
3.遍历所有情况,找出最大牌型,就是所出的牌。
算法的关键与关键实现部分流程图
算法的关键
算法关键应该是对牌型合法的判断和判牌的策略,经过多次测试验证,我当前所选择的判牌策略在大多情况下能够实现最优,但还是不免有翻车的情况,这说明我这个策略并不是最优的。
关键实现部分流程图
5、关键代码解释
前端:
- 通过信号与信号槽达到通过按钮连接及跳转页面。
url='http://api.revth.com/auth/register2'
form_data={
"username":ui5.lineEdit.text(),
"password":ui5.lineEdit_2.text(),
"student_number":ui5.lineEdit_3.text(),
"student_password":ui5.lineEdit_4.text()
}
headers = {
"Content-Type": "application/json"
}
response = requests.post(url=url, headers=headers, data=json.dumps(form_data), verify=False)
print(response.text)
res = response.json()
if res['status']== 0:
widget1.show()
widget4.hide()
ui5.pushButton.clicked.connect(lambda:zcjm_enter_yhjm())
后端:
- 特殊牌型与普通牌型的判断分类
def pickcard(string):
card = find(string)
if special.zzql(card)!=0 or special.ytl(card)!=0 or special.sftx(card)!=0 or special.sth(card)!=0 or special.sths(card)!=0 \
or special.ssz(card)!=0 or special.wdst(card)!=0 or special.qd(card)!=0 or special.qx(card)!=0 or special.ldb(card)!=0 \
or special.cys(card)!=0 or special.sehz(card)!=0 or special.sgcs(card)!=0 or special.stst(card)!=0:
list = specialcard(string)
return list
else:
list = string.split()
list1 = [' ', ' ', ' ']
front = ' '
middle = ' '
bottom = ' '
temp1 = [-1, -1]
temp2 = [-1, -1]
temp3 = [-1, -1]
for i in itertools.combinations(list,5):
xxx = copy.deepcopy(list)
for y in i:
xxx.remove(y)
for j in itertools.combinations(xxx,5):
p = copy.deepcopy(xxx)
for t in j:
p.remove(t)
string1 = ' '.join(i)
string2 = ' '.join(j)
string3 = ' '.join(p)
frontvalue = check1(string1)
middlevalue = check1(string2)
bottomvalue = check2(string3)
if frontvalue[0] > middlevalue[0] or (frontvalue[0] == middlevalue[0] and frontvalue[1] >= middlevalue[1]):
if middlevalue[0] > bottomvalue[0] or (middlevalue[0] == bottomvalue[0] and middlevalue[1] >= bottomvalue[1]):
if front == ' ':
front = string1
middle = string2
bottom = string3
temp1 = frontvalue
temp2 = middlevalue
temp3 = bottomvalue
else:
win1 = 0
win2 = 0
win3 = 0
if frontvalue[0] > temp1[0]:
win1 = 1
elif frontvalue[0] == temp1[0]:
if frontvalue[1] > temp1[1]:
win1 = 1
elif frontvalue[1] == temp1[1]:
win1 = 0
elif frontvalue[1] < temp1[1]:
win1 = -1
elif frontvalue[0] < temp1[0]:
win1 = -1
if middlevalue[0] > temp2[0]:
win2 = 1
elif middlevalue[0] == temp2[0]:
if middlevalue[1] > temp2[1]:
win2 = 1
elif middlevalue[1] == temp2[1]:
win2 = 0
elif middlevalue[1] < temp2[1]:
win2 = -1
elif middlevalue[0] < temp2[0]:
win2 = -1
if bottomvalue[0] > temp3[0] :
win3 = 1
elif bottomvalue[0] == temp3[0]:
if bottomvalue[1] > temp3[1]:
win3 = 1
elif bottomvalue[1] == temp3[1]:
win3 = 0
elif bottomvalue[1] < temp3[1]:
win3 = -1
elif bottomvalue[0] < temp3[0]:
win3 = -1
if win1 + win2 + win3 > 0:
front = string1
middle = string2
bottom = string3
temp1 = frontvalue
temp2 = middlevalue
temp3 = bottomvalue
list1[0] = bottom
list1[1] = middle
list1[2] = front
return list1
- 三张牌的牌型的判断拆分(前墩)
def check2(string):#三张牌的判断牌型
num = findd(string)
if num[0] == num[1] == num[2]:
temp = [4,num[0]]
return temp
elif num[0] == num[1]:
temp = [2,num[0]]
return temp
elif num[0] == num[2]:
temp = [2,num[0]]
return temp
elif num[1] == num[2]:
temp = [2,num[1]]
return temp
elif num[0] != num[1] and num[0] != num[2] and num[1] != num[2]:
temp = [1,max(num[0],num[1],num[2])]
return temp
- 五张牌的牌型的判断拆分(中墩,后墩)
def check1(string):#五张牌的判断牌型
num = findd(string)
changenum = sorted(num)
color = findcolor(string)
if color[0] == color[1] == color[2] == color[3] == color[4] and changenum[4] == changenum[3]+1 == changenum[2]+2 ==\
changenum[1]+3 == changenum[0]+4:
temp = [9,changenum[4]]
return temp
if changenum[0] == changenum[1] == changenum[2] == changenum[3] or changenum[1] == changenum[2] == changenum[3] == \
changenum[4]:
temp = [8,changenum[2]]
return temp
if (changenum[0] == changenum[1] and changenum[2] == changenum[3] == changenum[4]) \
or (changenum[0] == changenum[1] == changenum[2] and changenum[3] == changenum[4]):
temp = [7,changenum[2]]
return temp
if color[0] == color[1] == color[2] == color[3] == color[4]:
temp = [6,changenum[4]]
return temp
if changenum[4] == changenum[3]+1 == changenum[2]+2 == changenum[1]+3 == changenum[0]+4:
temp = [5,changenum[4]]
return temp
if (changenum[0] == changenum[1] == changenum[2] ) or (changenum[1] == changenum[2] == changenum[3]) or \
changenum[2] == changenum[3] == changenum[4]:
temp = [4,changenum[2]]
return temp
if changenum[0] == changenum[1] and changenum[2] == changenum[3]:
if changenum[1]+1 == changenum[3]:
temp = [2.5,changenum[3]]
else:
temp = [2,changenum[3]]
return temp
if changenum[0] == changenum[1] and changenum[3] == changenum[4]:
if changenum[1] + 1 == changenum[3]:
temp = [2.5, changenum[3]]
else:
temp = [2, changenum[3]]
return temp
if changenum[1] == changenum[2] and changenum[3] == changenum[4]:
if changenum[1]+1 == changenum[3]:
temp = [2.5,changenum[3]]
else:
temp = [2,changenum[3]]
return temp
if changenum[0] == changenum[1]:
temp = [2,changenum[0]]
return temp
if changenum[1] == changenum[2]:
temp = [2, changenum[1]]
return temp
if changenum[2] == changenum[3]:
temp = [2, changenum[2]]
return temp
if changenum[3] == changenum[4]:
temp = [2, changenum[3]]
return temp
temp = [1,changenum[4]]
return temp
6、性能分析与改进
改进的思路
一开始是打算从底墩开始写出各种牌型所应对的情况,如底墩是同花顺,那么应当取哪些牌作为这个同花顺合适。后来发现即使写了4,5百行也是刚开始,而且量大且不重复,极容易出bug,所以换了一种遍历所有出牌情况的方法,比较简单而且不容易出bug。
性能分析图
使用visual studio 2019自带的Python性能分析工具得到
程序中消耗最大的函数
消耗最大的函数是主函数十三水.py
7、单元测试
项目部分单元测试代码
-
下图是测试用到的函数,代码比较长,限于篇幅不全展开
测试数据:随机生成,不特意构造。 测试思路:利用随机生成的数据对各个函数进行测试,可完成对其实用性和可靠性检测。
8、Github的代码签入记录
9、遇到的代码模块异常及解决方法
-
问题描述一:
在编程过程中,遇到了变量的赋值问题,在函数中,会对传来的变量进行改变。
做过的尝试
需要进行深复制。
是否解决
已解决
收获
对python的传参数有了更好的理解。
-
问题描述二:
在运行过程中,发现zeros赋值函数运行时间极长。
做过的尝试
采用手动赋值的方法,极大的改善了运行时间。
是否解决
已解决
收获
有时候手动赋值会更加快运行时间。
-
问题描述三:
用Qt designer做Ui设计时,不知道怎么实现界面转换。
做过的尝试
通过百度教程和动手不断尝试。
是否解决
已解决
收获
学会了如何用Qt designer做页面设计。
-
问题描述四:
在编写网络接口代码时,不知道如何将得到的返回值显示在窗口或界面上。
做过的尝试
想用标签或者文本框来实现。
是否解决
尚未完全解决。
收获
学会了简单的网络接口实现,有了基本的了解,但还不够熟悉。
10、评价队友
-
评价逸杰:
-
值得学习的地方
逸杰学习新知识很投入,是最早开始着手做十三水的,也在督促我们要抓紧时间,事实也证明他是对的。
-
需要改进的地方
空闲时间可以多去学习新知识提升自己。
-
评价智勇:
-
值得学习的地方
算法能力很强,学习新知识很快,很多天都在熬夜。
-
需要改进的地方
希望合理分配时间,全都挤在最后时间会很赶,很累。
11、学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 10 | 10 | 学习原型设计工具和简单的Python实现图形界面 |
2 | 500 | 500 | 20 | 30 | 学习pyqt5,着手设计界面,尝试从服务器获取数据,编写代码 |
3 | 1200 | 1700 | 50 | 80 | 学习python,实现特殊牌型和普通的判断算法,并逐步优化 |