第二次结对编程作业

一、

二、给出具体分工

鲍冰如:主要负责后端代码,一些前端,对算法进行性能分析,进行单元测试,以及博客除接口部分外的撰写
苏凯婷:主要负责前端代码,写出主要框架,美化页面并且进行定位和交互处理,接口的使用,以及博客的接口部分
第一次做游戏完全没经验,所以一开始我们的分工不是很明确,前端和后端有些是一起做的,但是后来我们逐渐确定了需求,进一步分工,实现各自负责的部分,并学习需要使用到的技术,接着对完成各部分功能,对代码进行整合,进行性能分析和单元测试,并编写博客。

三、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 算法
...
原文地址:https://www.cnblogs.com/katy0308/p/11741837.html