一、结对情况
牌友 | 博客地址 | GitHub地址 |
---|---|---|
031702209 陈湘怡 | 福州 | 快乐 |
031702228 许培荣 | 十三水 | 嘿嘿! |
UI界面视频展示:
点我点我
二、具体分工
许培荣:AI算法与部分博客撰写
陈湘怡:UI界面实现和部分博客撰写
三、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
· Planning | · 计划 | 60 | 90 |
· Estimate | · 估计这个任务需要多少时间 | 20 | 30 |
· Development | · 开发 | 240 | 250 |
· Analysis | · 需求分析 (包括学习新技术) | 1000 | 1080 |
· Design Spec | · 生成设计文档 | 240 | 300 |
· Design Review | · 设计复审 | 20 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 25 | 20 |
· Design | · 具体设计 | 1200 | 1500 |
· Coding | · 具体编码 | 600 | 800 |
· Code Review | · 代码复审 | 45 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 150 | 180 |
· Reporting | · 报告 | 100 | 120 |
· Test Repor | · 测试报告 | 30 | 30 |
· Size Measurement | · 计算工作量 | 10 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 20 |
· 合计 | 3750 | 4520 |
四、解题思路描述与设计实现说明
1.网络接口的使用
<img src="https://img2018.cnblogs.com/blog/1797575/201910/1797575-20191015175304284-531465048.png" width=600,height=800>
2.代码组织与内部实现设计
-
ai部分
代码组织
<img src="https://img2018.cnblogs.com/blog/1797262/201910/1797262-20191015162814425-1857415110.jpg" width=800,height=1000>
程序分为三个文件,ai.py做主要的操作流程,对手牌的操作函数放在pocket.py(emmm英语不好的我命名的时候搞错了。。),最后选取出三堆牌放在choosethree.py,分别先选取后墩中墩头墩,getbottom(),getmiddle(),gethead()。
内部实现
<img src="https://img2018.cnblogs.com/blog/1797262/201910/1797262-20191015170300475-1946211727.png" width=800,height=1000>
ai先调用网络接口部分登录,开启战局,接到牌后调用pocket.py(依次整理手牌,得到手上的牌型,返回给ai,然后在choosethree.py 中把所有可能的牌型组合一遍根据自己给的权值对每个组合进行评估。 -
ui部分
代码组织
分别实现各个界面的代码,最后用在ui_function()中封装页面并编写页面的槽函数,使各个页面之间达到交互的效果。
内部实现设计
使用Pycharm实现,Qt Designer设计。根据之前的原型设计,用Qt Designer还原各个界面并编辑信号/槽,生成ui文件,然后用pyuic转成py文件,最后编写一个代码实现他们的槽函数以及连接服务器等。
3.算法的关键与关键实现部分流程图
算法关键
算法关键是再对牌型的组合上,因为每一种牌型的处理方式几乎都不一样,所以被我处理成的数据结构也会不太一样,所以在我实现起来就会略显笨拙,代码过于冗余。应该进行重构。
实现流程
# init rankdict and suitdict
sdt, rdt = initdict()
# 整理手牌添加到 rankdict 和 suitdict
# 顺便将手牌排序下
rdt = arrange(mycard, rdt)
mycard = [] + [c for r in rdt for c in rdt[r]]
sdt = arrangesdt(mycard, sdt)
remycard = [c for c in reversed(mycard)]
# 整理出所有拥有的牌型
ct = getct(rdt, sdt)
# 获取三堆牌
head, middle, bottom = getPile(mycard, rdt, sdt, ct)
<img src="https://img2018.cnblogs.com/blog/1797575/201910/1797575-20191015175432350-1395842718.png" width=1000,height=1100>
五、关键代码解释
-
ai部分
以上为组合选取bottom的代码,其他两堆类似。
可以看到代码对手牌中所有牌型的key值进行遍历,然后把所有组合结果放入队列中在等会再去出于中墩的牌祝贺,同时计算出入的牌的分数,一并放入。在最后计算三墩牌的分数 -
ui部分
服务器模块
连接服务器,获取和保存注册信息,获取历史对局和排行榜的相关信息。
<img src="https://img2018.cnblogs.com/blog/1797575/201910/1797575-20191015223758287-859252742.png" width=600,height=800>
登录模块
通过服务器返回状态status判断该用户名密码是否已注册,若status返回值为0说明已注册,则进入菜单页面,否则不允许进入,可以选择去注册页面注册后再进入。
# 登录按钮的槽函数
def b_login(self):
#print('aaaaaaaaaaa')
if self.login:
'''
login post
'''
paw = self.lineEdit.text()
user = self.lineEdit_3.text()
if not paw == '' and not user == '':
status, pid = login(user, paw)
if status == 0:
self.hide()
w = winmenu(window)
w.setFixedSize(1039, 757)
# w.move(100, 100)
w.show()
else:
print('username or password is wrong!')
else:
print('please input username and password!')
else:
'''
register
'''
paw = self.lineEdit.text()
user = self.lineEdit_3.text()
if not paw == '' and not user == '':
status, pid = register(user, paw)
if status:
'''
以下函数达到切换页面效果
即关闭当前窗口 对新的想要的界面实例化
然后show出来
'''
self.hide()
w = winmenu(window)
w.setFixedSize(1039, 757)
# w.move(100, 100)
w.show()
else:
print('Username already registered!')
else:
print('please input username and password!')
各种槽函数
每个页面都有槽函数,此处抓个栗子
#排行榜中"下一页"的槽函数
def nextpage(self):
if self.page < self.long:
self.page += 1
quece = (self.page - 1) * 4 + 1
self.label_4.setText(str(self.page))
self.rank1.setText('No.{}'.format(quece))
self.rank2.setText('No.{}'.format(quece + 1))
self.rank3.setText('No.{}'.format(quece + 2))
self.rank4.setText('No.{}'.format(quece + 3))
self.id1.setText(str(self.res[(self.page - 1) * 4]['name']))
self.id2.setText(str(self.res[(self.page - 1) * 4 + 1]['name']))
self.id3.setText(str(self.res[(self.page - 1) * 4 + 2]['name']))
self.id4.setText(str(self.res[(self.page - 1) * 4 + 3]['name']))
self.point1.setText(str(self.res[(self.page - 1) * 4]['score']))
self.point2.setText(str(self.res[(self.page - 1) * 4 + 1]['score']))
self.point3.setText(str(self.res[(self.page - 1) * 4 + 2]['score']))
self.point4.setText(str(self.res[(self.page - 1) * 4 + 3]['score']))
六、性能分析与改进
改进思路
将一些不必要的组合直接进行剪枝,比如当已经组合出比较大的牌型,有一些比较小的牌型可以适当地减少组合,比如以选取炸弹,就不必对两对子进行组合。尽量去除冗余。或者在每次选取组合牌型是和之前的历史最高分数相比,如果相差较多可不加入队列。
性能分析图
七、单元测试
class MyTestCase(unittest.TestCase):
def test_sanshunzi(self):
list0 = shisanshui.list0
result = shisanshui.sanshunzi(list0)
self.assertEqual(result, 1)
if __name__ == '__main__':
unittest.main()
八、贴出Github的代码签入记录
九、遇到的代码模块异常或结对困难及解决方法
1.问题描述
- ai部分
组合算法过于杂乱,日常出bug - ui部分
1.不熟悉工具,无法百分百还原原型设计
2.在ui转换成py文件上遇到了死亡错误“No module named 'xxxx'”
3.Qt Designer中的文本框不能编辑槽函数
4.各种小雷
5.使用pyqt制作ui界面之疯狂踩坑,一直报错Process finished with exit code -1073740791 (0xC0000409)
2.做过哪些尝试
- ai部分
缝缝补补的修复bug - ui部分
1.看教程,百度,询问队友
2.如图,疯狂百度,然后并没有解决,惆怅了两天,在队友两分钟的指引下解决了QAQ(对不起,我太菜了)
<img src="https://img2018.cnblogs.com/blog/1797575/201910/1797575-20191015182736913-1860935557.png" width=500,height=800>
3.换个按钮元件就可以(...是我妄想了,我以为它和原型设计一样方便)
4.多看看,实在不行问队友,队友万能(打call!打call! )
3.是否解决
- ai部分
解决 - ui部分
都已解决
4.有何收获
许培荣
收获了好多深夜打代码的快乐时光:)
陈湘怡:
很明显,我又get到两个工具的使用方法。一开始看到PyQt5教程有一百多节,内心是拒绝的,尝试无教程摸索,果然对那些英语属性元件名感到头秃,进展很慢,后面选择看我需要的章节,加上队友的帮助,逐渐上手~ 对前端也有更有兴趣了一些,当然,是在我做出界面之后QAQ,之前不断踩坑的感觉真的难受,后面都习以为常了,踩坑是必然的,嗯,不是我菜。
十、评价你的队友
开始了,上图
1.值得学习的地方
编程能力max,不仅AI全权负责,队友还会帮我解决UI的难题,太强惹
2.需要改进的地方
没有,有就是太强了
十一、学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 8 | 8 | 学习使用原型工具 |
2 | 0 | 0 | 8 | 16 | 学习pyqt和pycharm的使用 |
3 | 162 | 162 | 20 | 36 | 学习链接服务器等 |
4 | 270 | 432 | 34 | 70 | 学习各种槽函数等交互,UI实现 |