一、
- 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的代码签入记录
(因为我们是在自己测试无误可以运行之后才放到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 | 算法 |
... |