一、
- 031702312鲍冰如(博客链接) Github项目地址)主仓库
031702304苏凯婷(博客链接)(Github项目地址)
二、给出具体分工
鲍冰如:主要负责后端代码,一些前端,对算法进行性能分析,进行单元测试,以及博客除接口部分外的撰写
苏凯婷:主要负责前端代码,写出主要框架,美化页面并且进行定位和交互处理,接口的使用,以及博客的接口部分
第一次做游戏完全没经验,所以一开始我们的分工不是很明确,前端和后端有些是一起做的,但是后来我们逐渐确定了需求,进一步分工,实现各自负责的部分,并学习需要使用到的技术,接着对完成各部分功能,对代码进行整合,进行性能分析和单元测试,并编写博客。三、PSP表格
PSP2.1 Personnal Software
Process Stagese预估耗时
(分钟)实际耗时
(分钟)Planning 计划 60 75 * Estimate * 估计这个任务需要多少时间 60 75 Development 开发 10080 10080 * Analysis * 需求分析(包括学习新技术) 720 720 * Design Spec * 生成设计文档 150 150 * Design Review * 设计复审 50 75 * Coding Standard * 代码规范(为目前的开发制定合适的规范) 120 150 * Design * 具体设计 300 500 * Coding * 具体编码 3000 3000 * Code Review * 代码复审 200 200 * Test * 测试(自我测试,修改代码,提交修改) 300 320 Reporting 报告 100 110 * Test Repor * 测试报告 60 45 * Size Measurement * 计算工作量 30 30 * Postmortem & Process
Improvement Plan* 事后总结,并提出过程改进计划 60 70 * 合计 15230 15530 四、解题思路描述与设计实现说明
(1)网络接口的使用
(2)代码组织与内部实现设计(类图)
(3)说明算法的关键与关键实现部分流程图
算法关键:
Step.1 暴力枚举后中墩的情况,剩余牌自动归为前墩;
Step.2 对于枚举结束之后的三墩,进行合法性检测;
Step.3 对于合法的一组手牌,我们用权重数组算出这组牌赢的概率的估计值,利用估计值进行牌组的选择。
流程图:
五、关键代码解释
代码比较长,关键代码即暴力枚举十三章牌可能组成的牌型,从单张(junks)到顺子(straights),每种牌型从大到小排序,再从中枚举组合出后中前墩的可能出牌模式,利用权重数组进行胜率的计算,不断替换迭代寻找胜率最高的牌组。
def RecommendHands( card_list ): sz = len(card_list) q = [] heapq.heapify(q) nw_hands = [] #last O(n^5) using the brute force to enumerate the combination of the last hands for i in range(0,sz,1): nw_hands.append(card_list[i]) for j in range(i+1,sz,1): nw_hands.append(card_list[j]) for k in range(j+1,sz,1): nw_hands.append(card_list[k]) for g in range(k+1,sz,1): nw_hands.append(card_list[g]) for t in range(g+1,sz,1): nw_hands.append(card_list[t]) _c = nw_hands; _w = get_weight(_c, 2) heapq.heappush(q, Hands(_c, -_w)) if len(q) > hyper_n: _ = heapq.heappop(q) nw_hands.pop() nw_hands.pop() nw_hands.pop() nw_hands.pop() nw_hands.pop() last_hands = []; middle_hands = []; header_hands = [] while len(q)>0: last_hands.append(heapq.heappop(q)) #last_hands = [ ([(1,1),(1,2),(1,3),(1,4),(1,5)],weight), (), (), ... () ].dtype = Hands([(),()],w) for _ in last_hands: #fir every last_hands choose the middle_hands and header_hands tp_card_list = card_list.copy() for i in _.list: for j in range(len(tp_card_list)): if tp_card_list[j] == i: tp_card_list.pop(j) break #now tp_card_list contain only 8 cards for middle and header sz = len(tp_card_list) for i in range(0,sz,1): nw_hands.append(tp_card_list[i]) for j in range(i+1,sz,1): nw_hands.append(tp_card_list[j]) for k in range(j+1,sz,1): nw_hands.append(tp_card_list[k]) for g in range(k+1,sz,1): nw_hands.append(tp_card_list[g]) for t in range(g+1,sz,1): nw_hands.append(tp_card_list[t]) _c = nw_hands; _w = get_weight(_c, 1) heapq.heappush(q, Hands(_c, -_w)) if len(q) > hyper_n: _ = heapq.heappop(q) nw_hands.pop() nw_hands.pop() nw_hands.pop() nw_hands.pop() nw_hands.pop() while len(q)>0: X = heapq.heappop(q) middle_hands.append(X) tp2_card_list = tp_card_list.copy() szz = len(middle_hands) for i in middle_hands[szz-1].list: for j in range(len(tp2_card_list)): if i == tp2_card_list[j]: tp2_card_list.pop(j) break _c = tp2_card_list; _w = get_weight(tp2_card_list, 0) header_hands.append(Hands(_c, -_w)) my_hands = [] for lst in last_hands: for j in range(hyper_n): my_hands.append( [(header_hands[j].list, header_hands[j].weight), (middle_hands[j].list, middle_hands[j].weight), (lst.list, lst.weight)] ) #for i in my_hands: # print(i) return my_hands #middle O(n^5) using the brute force to enumrate the combination of the middle hands #first the rest, no choise to choose
六、性能分析与改进
(1)改进思路
改进前的思路:设计实现时,第一种方法是使用爆搜或者10重for的嵌套,简单粗暴;第二种方法是爆搜或者5重for,先选出后墩,再选出中墩,即后中墩是分开选择的。
存在问题:第一种方法时间会很久;第二种方法虽然比第一种方法快很多,但是会出现倒水的情况,即无法保证三墩的大小关系。
改进后的思路:采用先搜模式,匹配原始牌组,最后出牌的方法
(2)性能分析
ncalls tottime percall cumtime percall 64 0.001 0.000 15.890 0.248 Algorithm_fight.py:142(get_battle) 32 0.030 0.001 8.021 0.251 Algorithm_fight.py:155(decode_data) 32 0.001 0.000 3.058 0.096 Algorithm_fight.py:168(my_choose) 800 0.000 0.000 0.000 0.000 Algorithm_fight.py:180(<lambda>) 32 0.001 0.000 7.483 0.234 Algorithm_fight.py:204(send_2_system) 32 0.055 0.002 26.529 0.829 Algorithm_fight.py:221(_start) 1 0.000 0.000 26.530 26.530 Algorithm_fight.py:239(main) 416 0.000 0.000 0.000 0.000 Algorithm_fight.py:29(chg) 1 0.000 0.000 0.000 0.000 Algorithm_fight.py:37(Hands) 50944 0.019 0.000 0.025 0.000 Algorithm_fight.py:38(__init__) 292872 0.034 0.000 0.034 0.000 Algorithm_fight.py:42(__lt__) 32 0.125 0.004 3.056 0.096 Algorithm_fight.py:49(RecommendHands) 1 0.000 0.000 26.767 26.767 Algorithm_fight.py:7(<module>) 1 0.000 0.000 0.000 0.000 GetWeight.py:7(<module>) 50944 0.240 0.000 2.790 0.000 GetWeight.py:99(get_weight)
参数解释:
ncalls:表示函数调用的次数。 tottime:表示指定函数的总的运行时间,除掉函数中调用子函数的运行时间。 percall:(第一个 percall)等于 tottime/ncalls。 cumtime:表示该函数及其所有子函数的调用运行的时间,即函数开始调用到返回的时间。 percall:(第二个 percall)即函数运行一次的平均时间,等于 cumtime/ncalls。 filename:lineno(function):每个函数调用的具体信息
(3)消耗最大的函数
def AutoRecommend(tp_cards, junks, pairs, triples, booms, straights, flushs, _2_pairs, _32_tps): _third = booms + _32_tps + flushs + straights + triples + _2_pairs + pairs + junks _second = booms + _32_tps + flushs + straights + triples + _2_pairs + pairs + junks _first = triples + pairs + junks nw_cards = tp_cards.copy() nw_cards.sort(key=lambda x:-x[1]) #print("nw_cards = ", nw_cards) q = [] hyper_n = 20 my_weight = [1/5, 2/5, 2/5] heapq.heapify(q) for i in _third: #print("i = ",i) nwcs = nw_cards.copy() #print("nwcs0 ", nwcs) tail = i.copy() for ii in tail: nwcs.remove(ii) tp_nwcs0 = nwcs.copy() #print("nwcs after ii ", nwcs) tp_tail = tail.copy() for j in _second: nwcs = tp_nwcs0.copy() #print("j = ",j) tail = tp_tail.copy() mid = j.copy() flg = 1 for jj in mid: if ( jj in nwcs ): nwcs.remove(jj) else: flg = 0 break if ( flg == 0 ): continue tp_nwcs1 = nwcs.copy() #print("nwcs = ", nwcs) #print("nwcs after jj ", nwcs) tp_mid = mid.copy() for k in _first: nwcs = tp_nwcs1.copy() #print("k = ",k) tail = tp_tail.copy() mid = tp_mid.copy() head = k.copy() flg = 1 for kk in head: if ( kk in nwcs): nwcs.remove(kk) else: flg = 0 break if ( flg == 0 ): continue #print("nwcs the rest ", nwcs) #print("nw_cards_0 = ", head, mid, tail) #complete the head #print(head); print(mid); print(tail) tpp = nw_cards.copy() #print("tpp = ", tpp) for hd in head: tpp.remove(hd) for mi in mid: tpp.remove(mi) for tl in tail: tpp.remove(tl) pos = 0 while ( pos < len(tpp) ): if ( len(head) < 3 and pos < len(tpp) ): head.append(tpp[pos]) pos += 1 if ( len(mid) < 5 and pos < len(tpp) ): mid.append(tpp[pos]) pos += 1 if ( len(tail) < 5 and pos < len(tpp) ): tail.append(tpp[pos]) pos += 1 w_h, w_m, w_t = get_weight(head,0), get_weight(mid,1), get_weight(tail,2) nw_w = (np.array([w_h,w_m,w_t])*np.array(my_weight)).sum() #print("nw_cards_1 = ", head, mid, tail) chk_val = chk_ordered(head, mid, tail) if ( chk_val[0] == 1 ): if ( len(q) < hyper_n ): heapq.heappush(q,HandCard(head+mid+tail, nw_w)) else: if ( nw_w > q[0].weight ): heapq.heappushpop(q, HandCard(head+mid+tail, nw_w)) result_cards = [] while ( len(q) > 0 ): result_cards.append(heapq.heappop(q)) ret = result_cards[len(result_cards)-1].list return ret
七、单元测试
构造思路
使用unittest.TestSuite() 按牌型顺序进行判牌单元测试 pair 2pairs triple boom straight flush fullhouse分别判断 输出判断的值
单元测试代码
#按牌型顺序进行判牌单元测试 #pair 2pairs triple boom straight flush fullhouse分别判断 class UnitTest(unittest.TestCase): @classmethod def setUpClass(self): pass @classmethod def tearDownClass(self): pass def tst_jdg_pair(self): ct = 0 for i in open('./UnitTest-in.txt').readlines(): ct += 1 nw = []; cards = i.split() for j in cards: nw.append((suit_sa[j[0]],number_sa[j[1:len(j)]])) #print(nw) print("card %d " % (ct), end="") print(jdg_pair(nw)) def tst_jdg_2pairs(self): ct = 0 for i in open('./UnitTest-in.txt').readlines(): nw = []; cards = i.split() for j in cards: nw.append((suit_sa[j[0]],number_sa[j[1:len(j)]])) #print(nw) ct += 1 print("card %d " % (ct), end="") print(jdg_2pairs(nw)) def tst_jdg_triple(self): ct = 0 for i in open('./UnitTest-in.txt').readlines(): nw = []; cards = i.split() for j in cards: nw.append((suit_sa[j[0]],number_sa[j[1:len(j)]])) #print(nw) ct += 1 print("card %d " % (ct), end="") print(jdg_triple(nw)) def tst_jdg_boom(self): ct = 0 for i in open('./UnitTest-in.txt').readlines(): nw = []; cards = i.split() for j in cards: nw.append((suit_sa[j[0]],number_sa[j[1:len(j)]])) #print(nw) ct += 1 print("card %d " % (ct), end="") print(jdg_boom(nw)) def tst_jdg_straight(self): ct = 0 for i in open('./UnitTest-in.txt').readlines(): nw = []; cards = i.split() for j in cards: nw.append((suit_sa[j[0]],number_sa[j[1:len(j)]])) #print(nw) ct += 1 print("card %d " % (ct), end="") print(jdg_boom(nw)) def tst_jdg_flush(self): ct = 0 for i in open('./UnitTest-in.txt').readlines(): nw = []; cards = i.split() for j in cards: nw.append((suit_sa[j[0]],number_sa[j[1:len(j)]])) #print(nw) ct += 1 print("card %d " % (ct), end="") print(jdg_flush(nw)) def tst_jdg_fullhouse(self): ct = 0 for i in open('./UnitTest-in.txt').readlines(): nw = []; cards = i.split() for j in cards: nw.append((suit_sa[j[0]],number_sa[j[1:len(j)]])) #print(nw) ct += 1 print("card %d " % (ct), end="") print(jdg_fullhouse(nw)) test_lists = ["tst_jdg_pair","tst_jdg_2pairs","tst_jdg_triple","tst_jdg_boom","tst_jdg_straight","tst_jdg_flush","tst_jdg_fullhouse"] if __name__ == "__main__": for i in test_lists: suit = unittest.TestSuite() suit.addTest(UnitTest(i)) runner = unittest.TextTestRunner() runner.run(suit)
八、Github的代码签入记录
九、遇到的代码模块异常或结对困难及解决办法
(1)没有经验,知识不足
问题描述:由于以前都没有学过关于前端还有js的知识,所以一开始不知道怎么分工,虽然前人的经验是一人前端一人算法,但是脑子一抽我们俩一开始好像都觉得自己不太能胜任算法部分,所以前端和算法我们俩都有参与。
做过哪些尝试:边学边敲代码,前端每个人写一部分最后整合起来,算法也一起研究了
是否解决:是,解决了,还好前端比较容易写,边学边写代码没有很困难,js也认真学了
有何收获:对前端有了更深的了解,增加了实战经验。学习了js,十分受用
(2)制作弹窗
问题描述:原型设计的时候便把排行榜,往期对战结果,往期对战结果详情都做成弹窗形式,在往期对战结果按钮中点击战局ID按钮能显示出往期对战结果详情,在弹窗中再制作一个弹窗,由于这次作业才学了前端,对弹窗的制作不熟悉
做过哪些尝试:尝试将代码放在div按钮的前中后
是否解决:是,解决了,将代码放在div按钮的后面,和js结合点击触发弹窗的效果
有何收获:学会了弹窗的使用,了解了更多变量的作用,学会将前端和js结合使用
十、评价你的队友
苏凯婷:
值得学习的地方:
值得学习的地方太多了,队友很关心我们结对编程的进度,所以经常会叫上我一起去教室,杜绝宿舍摸鱼行为;主动扛下弹窗的工作;实现前端的时候,由于我们有了新的想法,对弹窗的设计做出改变,她总会问我意见,一提就改,说一不二,到后来我觉得她的审美以及非常OK,完美;还有接口也是她完成的,很感动;没有怪我摸鱼,很感动;我们已经不是鱼精组合了,因为她变成跃了龙门的鲤鱼!她很优秀!(我开始忏悔),一定是命运把她派到我身边,我能想到最浪漫的事,就是和她一起完成十三水编程作业,因为有她,我的编程生活都不枯燥了,充满信心和期待地完成了任务,夸不完了......
需要改进的地方:
就算摸鱼的队友是同伴同学也不能心怀大爱,她总是包容我本人的摸鱼行为,其实我内心墙裂要求被队友压榨(不是。
鲍冰如:
值得学习的地方:
感谢鲍老师疯狂暴夸,感觉鲍老师比较的稳重(看起来比较)无论是面前的困难怎么样的大,鲍老师都很稳,没有像我容易打鸡血也容易变得很丧,我需要鲍老师一起就不会想要摸鱼,后期加上我们两个都有接入网这门课需要考试,任务就突然加重了,鲍老师主动包揽了博客的而大部分,令我十分感动,而且我觉得鲍老师的办事效率更高。我们鱼精组合圆满完成任务(划掉)
需要改进的地方:
没有。如果,只是如果,希望接入网不要考试QAQ.
十一、学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 10 | 10 | 学习axure的使用方法 |
2 | 2000+ | 2000+ | 130 | 140 | 学习html,css,js,前端制作 |
3 | 1000+ | 3000+ | 20 | 160 | 算法 |
... |