目录
1.网络链接
2.分工
3.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 60 | 100 |
Estimate | 估计这个任务需要多少时间 | 60 | 100 |
Development | 开发 | 600 | 1000 |
Analysis | 需求分析 (包括学习新技术) | 600 | 600 |
Design Spec | 生成设计文档 | 30 | 40 |
Design Review | 设计复审 | 20 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
Design | 具体设计 | 50 | 60 |
Coding | 具体编码 | 1650 | 1800 |
Code Review | 代码复审 | 50 | 40 |
Test | 测试(自我测试,修改代码,提交修改) | 100 | 120 |
Reporting | 报告 | 100 | 100 |
Test Report | 测试报告 | 30 | 30 |
Size Measurement | 计算工作量 | 20 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 50 | 60 |
合计 | 3440 | 4120 |
4.解题思路分析和设计实现说明
(1)网络接口的使用
前端登录注册查看排行榜接口用JavaScript编写
//登录 <script> var username=document.getElementById("i1"); var password=document.getElementById("i2"); function t1(){ var data = JSON.stringify({ "username": username.value, "password": password.value }); if (username.value == "" ||password.value == "" ) { alert("请输入用户名或密码"); return ; } var xhr = new XMLHttpRequest(); xhr.addEventListener("readystatechange", function () { if (this.readyState === this.DONE) { console.log(this.responseText); var JsonObj = JSON.parse(this.responseText); if(JsonObj.status==0) { localStorage.setItem("token",JsonObj.data.token); localStorage.setItem("id",JsonObj.data.user_id); alert("登录成功"); window.location.href = "game.html"; } else if(JsonObj.status==1003) alert("教务处认证失败"); else alert("用户名未注册或密码错误"); } }); xhr.open("POST","http://api.revth.com/auth/login"); xhr.setRequestHeader("content-type", "application/json"); xhr.send(data); } </script> //注册 <script> var username=document.getElementById("i1"); var password=document.getElementById("i2"); function t1() { var data = JSON.stringify({ "username": username.value, "password": password.value, "student_number": document.getElementById("ii3").value, "student_password" :document.getElementById("ii4").value }); if (username.value == "" ||password.value == "" ) { alert("请输入用户名或密码"); return ; } if(document.getElementById("ii3").value==""||document.getElementById("ii4").value=="") { alert("请输入学号或教务处密码"); return ; } var xhr = new XMLHttpRequest(); //xhr.withCredentials = true; xhr.addEventListener("readystatechange", function () { if (this.readyState === this.DONE) { console.log(this.responseText); var JsonObj = JSON.parse(this.responseText); if(JsonObj.status==0) alert("注册成功,请登录"); else if(JsonObj.status==1001) alert("用户名已被使用"); else if(JsonObj.status==1002) alert("学号已绑定"); else if(JsonObj.status==1003) alert("教务处认证失败"); else alert(JsonObj.status) } }); xhr.open("POST", "http://api.revth.com/auth/register2"); xhr.setRequestHeader("content-type", "application/json"); xhr.send(data); } </script> //登录验证 function check(){ $.ajax({ url:'http://api.revth.com/auth/validate', type:'GET', headers:{ "Content-Type":'application/json', "X-Auth-Token":localStorage.getItem('token') }, dataType:'json', success:function(data){ if (data['data']['user_id']==null){ alert("登录过期,请重试"); window.location.href='登入.html'; } else{ document.getElementById("chupai").onclick=function () { document.getElementById("fapai").style.display="block" document.getElementById("chupai").style.display="none" } if (localStorage.getItem('card')!=null) showCard(); if (localStorage.getItem('gameid')!=null) document.getElementById('gameid').innerText="房间:"+localStorage.getItem('gameid'); } }, error:function(data){ alert("登录过期,请重试"); } }); } //开始对局 function openGame(){ $.ajax({ url:'http://api.revth.com/game/open', type:'POST', dataType:'json', headers:{ 'X-Auth-Token':localStorage.getItem('token') }, success:function(data){ document.getElementById("chupai").style.display="block"; document.getElementById("fapai").style.display="none"; document.getElementById('gameid').innerText="房间:"+data['data']['id']; localStorage.setItem("gameid",data['data']['id']); localStorage.setItem("card",data['data']['card']); showCard(); }, error:function(data){ alert("匹配异常,请重试"); } }); } //排行榜 function getRank(){ $.ajax({ url:'http://api.revth.com/rank', type:'GET', dataType:'json', success:function(data){ document.getElementById('paihangbang').style.display=""; for (var i=1;i<=5;++i){ var pid="pid"+i; var s="s"+i; var name="name"+i; document.getElementById(pid).innerText=data[i-1]['player_id']; document.getElementById(s).innerText=data[i-1]['score']; document.getElementById(name).innerText=data[i-1]['name']; } }, error:function(data){ alert("请求出错,请重试"); } }); } //历史战绩 function query(){ var username=document.getElementById('queryUser').value; $.ajax({ url:'http://api.revth.com/history/id', type:'GET', dataType:'json', headers:{ 'X-Auth-Token':localStorage.getItem('token') }, success:function(data){ console.log(data); }, error:function(data){ alert('查询失败,请重试'); } }); }
后端 开始游戏接口使用了requests库
import requests import cards_division url = 'http://api.revth.com' cards_list = [] max_list = [] '''开始游戏并出牌''' def newgameplay(): global url global cards_list global max_list f = open('token.txt') token = f.readline() f.close headers = {"X-Auth-Token" : token} r = requests.post(url+'/game/open', headers=headers) print(r.text) data = r.json() status = data['status'] game_id = data.get('data').get('id') cards_string = data.get('data').get('card') cards_list = cards_string.split(' ') max_list = cards_division.divide_cards(cards_list) front = ' '.join(max_list[10:13]) middle = ' '.join(max_list[5:10]) rear = ' '.join(max_list[0:5]) payload = { "id": game_id, "card": [ front, middle, rear ] } headers = { 'content-type': "application/json", "X-Auth-Token": token } r2 = requests.post(url+'/game/submit', json=payload, headers=headers) print(r2.text) data = r2.json() status2 = data.get("status") for i in range(0, 13): if max_list[i][0] == '*': max_list[i] = '^'+max_list[i][1:] return max_list
(2)代码组织与内部实现设计(类图)
(3)说明算法的关键与关键实现部分流程图
Step1:将牌’A‘转变成值为14的牌,并将得到的13张牌升序排序
def change_and_sort_cards(list): for i in range(0, 13): if list[i][1]=='A': list[i]=list[i][0]+'14' elif list[i][1]=='J': list[i] = list[i][0]+'11' elif list[i][1]=='Q': list[i]=list[i][0]+'12' elif list[i][1]=='K': list[i]=list[i][0]+'13' list.sort(key=lambda x:int(x[1:]))
Step2:遍历后墩
Step3:遍历完后墩判断是否继续再遍历中墩,
Step4:剩下的排归为前墩
Step5:判断各墩的牌的类型
Step6:判断并选出权值最大的情况
5.关键代码及解释
各种牌型的判断
def same_flower(list): flag = True if len(list) == 3: return 0 else: for i in range(1, 5): if list[0][0] != list[i][0]: flag = False break if flag: return TONGHUA + int(list[4][1:]) * P else: return 0 def shunzi(list): if len(list) == 3: return 0 else: flag = True for i in range(1, 5): if int(list[i][1:]) - 1 != int(list[i - 1][1:]): flag = False if flag: return SHUNZI + int(list[4][1:]) * P else: return 0 def zhadan(list): flag = True if len(list) == 3: return 0 else: if list[1][1:] != list[2][1:] or list[1][1:] != list[3][1:]: flag = False if list[1][1:] != list[0][1:] and list[4][1:] != list[3][1:]: flag = False if flag: return ZHADAN + int(list[2][1:]) * P else: return 0 def hulu(list): flag = True if len(list) == 3: return 0 else: if list[0][1:] != list[1][1:] or list[3][1:] != list[4][1:]: flag = False if list[3][1:] != list[2][1:] and list[1][1:] != list[2][1:]: flag = False if flag: return HULU+int(list[2][1:])*P else: return 0 def santiao(list): flag = False if list[0][1:] == list[1][1:] and list[0][1:] == list[2][1:]: return SANTIAO+int(list[2][1:])*P elif len(list) == 5: for i in range(0, 3): if list[i][1:] == list[i+1][1:] and list[i][1:] == list[i+2][1:]: flag=True break if flag: return SANTIAO+int(list[2][1:])*P else: return 0 else: return 0 def select_duizi(list): if len(list)==3: if list[0][1:] == list[1][1:] or list[2][1:] == list[1][1:]: return YIDUI+int(list[1][1:])*P else: return SANPAI+int(list[2][1:])*P elif len(list) == 5: tmp = [] i = 0 while i < 4: if list[i][1:] == list[i+1][1:]: tmp.append(i) i += 1 i += 1 if len(tmp) == 1: return YIDUI+int(list[tmp[0]][1:])*P elif len(tmp) == 2: if tmp[1]-tmp[0] == 2 and int(list[tmp[1]][1:])-int(list[tmp[0]][1:]) == 1: return LIANDUI+int(list[tmp[1]][1:])*P else: return ERDUI+int(list[tmp[1]][1:])*P elif len(tmp) == 0: return SANPAI+int(list[4][1:])*P
将牌分堆后找出权值最大的组合
def sort_cards(list): cards = [list[10:13], list[5:10], list[0:5]] scores = [0, 0, 0] for i in range(0, 3): if same_flower(cards[i]) == 0: scores[i] = zhadan(cards[i]) if scores[i] != 0: continue scores[i] = hulu(cards[i]) if scores[i] != 0: continue scores[i] = shunzi(cards[i]) if scores[i] != 0: continue scores[i] = santiao(cards[i]) if scores[i] != 0: continue scores[i] = select_duizi(cards[i]) else: scores[i] = same_flower(cards[i]) if shunzi(cards[i]) != 0: scores[i] += 3 if scores[0] > scores[1] or scores[1] > scores[2]: return [0, 0, 0] else: return scores啊啊
6.性能改进与分析
先Profile一下代码,结果如图:
- Call Graph
- Statistics
由图可知,消耗最大的是divide_cards2函数, 因为对于大多数情况,后墩中墩很多是散牌或一对。于是增加了一个判断函数,对于后墩情况较好的(权值较大),再继续遍历中墩,这样就大大提高了程序的效率。
7.单元测试
import cards_division import cards_sorting import openapi import unittest class test(unittest.TestCase): def test_cards_type(self): self.assertEqual(cards_sorting.select_duizi(['#2', '#2', '#2', '#2', '#2']), 4.25) self.assertEqual(cards_sorting.santiao(['#2', '#4', '#4', '#4', '#7']), 5.25) def test_divide(self): cards_division.divide_cards(['&2', '#3', '&4', '#K', '#4', '^2', '$2', '#5', '&6', '^4', '*$8', '&8', '#A']) self.assertEqual(cards_division.count, 72072) def test_openapi(self): self.assertEqual(openapi.sign_in('Cerberus', 'hyh990723'), 0) self.assertEqual(openapi.newgameplay(), 0) if __name__ == '__main__': unittest.main()
测试函数:
- 测试牌型判断函数能否正确返回权值
- 测试划分函数是否正常遍历所有情况
- 测试网络接口能否正常连接,提交的数据是否正确
思路:
使用Python中的unittest模块,对代码中容易出错但又无法直接根据最终结果进行定位的模块进行测试。
8.贴出Github代码迁入记录
啥也不知道写了啥就往上丢啥吧
9.遇到的代码模块异常或结对困难及解决方法
遇到困难:
胡康不会python,我不会java,大家都痛恨C++,然后他说去自学python,后面因为突然冒出来个接入网考试,时间大大缩水。
两个算法白痴,都想做前端,后来得知前端可以用网页写,然后胡康刚好会就轮到我这个弟弟写后端
由于没有经验,代码规范未做好,在将两人代码组合时出现了许多问题
尝试
尝试用python的pygame写前端,后来发现只要python的tkinter就可以完成,再后来发现python的PyQt挺好用,最后放弃了用python写前端。
疯狂用PS设计图片以满足前端的要求
是否解决
最终解决了双方语言不通的困难,前端用网页写,后端用python写,最后用什么ajax整到一起,
虽然还没整好。收货
感觉前端更好玩一点,直男审美是时候露一手了
更加深入的学习了python···
10.评价我的队友
黄宇航真的很优秀!不管是第一次结对作业还是第二次结对作业,他都是挑大头,很有责任感,细心,审美也不错,这次能和他做队友真的是赚到!缺点是什么?宇航没有缺点!
11.学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
··· | ··· | ··· | ··· | ··· | ··· |
6 | 431 | 431 | 15 | 23 | 通过学习了解了python列表一些更有用的使用方法,当然还有pygame和tkinter的白给学习时间 |
7 | 210 | 641 | 13 | 36 | 学习了一些JavaScript和H5的知识,原因大概就是计算机图形学要考试了,顺便学学前端,因为对前端感兴趣哈哈 |
8 | 116 | 757 | 7 | 43 | 尝试与前端对接,完成所有代码工作,学习了网络接口的知识 |