第二次结对编程作业

拜拜、爱过 提交于 2019-12-03 00:04:51

1.各链接

结对同学博客连接
本作业博客连接
Github项目地址
UI视频百度网盘
UI视频b站

2.具体分工

王嵚:负责前端实现、博客内容指导and部分材料提供
陈荣杰:负责后端算法、博客撰写

3.PSP表格

  • 王嵚
PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 90
·Estimate ·估计这个任务需要多少时间 2600 2900
Development 开发 3250 3960
·Analysis ·需求分析 (包括学习新技术) 1800 2000
·Design Spec ·生成设计文档 90 60
·Design Review ·设计复审 120 120
·Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 50
·Coding ·具体编码 400 560
·Code Review ·代码复审 450 720
·Test ·测试(自我测试,修改代码,提交修改) 240 300
Reporting 报告 130 115
·Test Repor ·测试报告 50 60
·Size Measurement · 计算工作量 20 10
·Postmortem & Process Improvement Plan ·事后总结, 并提出过程改进计划 20 10
合计 3440 4165
  • 陈荣杰
PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 30
·Estimate ·估计这个任务需要多少时间 2200 2600
Development 开发 400 460
·Analysis ·需求分析 (包括学习新技术) 120 240
·Design Spec ·生成设计文档 40 30
·Design Review ·设计复审 30 20
·Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 30
·Coding ·具体编码 1000 1200
·Code Review ·代码复审 90 90
·Test ·测试(自我测试,修改代码,提交修改) 300 300
Reporting 报告 60 60
·Test Repor ·测试报告 60 60
·Size Measurement · 计算工作量 20 20
·Postmortem & Process Improvement Plan ·事后总结, 并提出过程改进计划 30 30
合计 2000 2400

4.解题思路描述与设计实现说明

网络接口的使用

根据样例的提示,调用 requests库中的get/post函数实现get/post请求,把如登录、注册等等操作所需要的请求封装成 不同函数放在一个httpfunctions.py文件下,使用时导入此文件就可以了。
具体代码如下:

def  entry(account):#注册+绑定接口
    url='http://api.revth.com/auth/login'
    data={
        "username": account["username"],
        "password": account["password"]
    }
    headers={
        "Content-Type":"application/json"
    }
    response=requests.post(url=url,headers=headers,data=json.dumps(data),verify=False);
    print(response.text)
    r = response.json()
    return r

代码组织与内部实现设计(类图)

  • 后端算法代码组织与内部实现设计
    如果是特殊牌的话,服务器会自动识别,也就是说只要分堆就好,因此不考虑特殊牌的判别,直接对拿到的手牌进行组合,利用组合的思想对所有牌型进行比较,择取最优选择。
  • 前端代码组织与内部实现设计

说明算法的关键与关键实现部分流程图

  • 算法关键
    • 分墩算法:对手牌进行组合,遍历一副手牌可以组成的所有情况,将当前的组合与当前的最优组合进行比较,按照等级及牌面对应权重矩阵赋权值,三墩相加再比大小,比到最后可以得出一个权值最大的组合,就选中这个组合为最优。
    • 因此算法关键应该是对牌型合法的判断和判牌的策略,初始定的赋权值策略是按照牌型等级及牌面直接赋值,可在实测是产生了许多意料之外的bug,debug到自闭,后来了解到有权重矩阵的存在,摸了摸头顶,果断选择改变策略。
  • 关键实现部分流程图

    5.关键代码解释

  • 后端
    • 先处理传入字符串,将其转化为列表类型数据,每个列表的第一个值为牌面值,第二个值为花色,方便操作,再利用组合的思想对所有牌型进行比较,择取最优牌型
    def do(lll):#算法主函数,执行分墩步骤
    
      sda = [#十三水权重矩阵,亲测可用,建议上手
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   JUNK
              [0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 4, 7, 14, 33],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   PAIR
              [0, 46, 48, 50, 51, 54, 56, 60, 63, 68, 74, 81, 89, 97],
              [0, 2, 3, 4, 4, 5, 7, 8, 10, 12, 15, 19, 24, 33],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 3]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   TWO_PAIRS
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 36, 37, 38, 40, 44, 46, 49, 54, 57, 62, 64, 0],
              [0, 0, 2, 3, 4, 4, 6, 7, 8, 10, 11, 13, 13, 0]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   TWO_PAIRS
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 31, 38, 39, 41, 45, 47, 50, 55, 58, 63, 65, 0],
              [0, 0, 3, 4, 5, 5, 7, 8, 9, 11, 12, 14, 14, 0]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   TRIPLE
              [0, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100],
              [0, 63, 65, 69, 71, 72, 73, 73, 73, 74, 74, 75, 75, 75],
              [0, 11, 12, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   STRAIGHT
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 0, 0, 77, 78, 81, 83, 85, 87, 88, 90, 91, 92],
              [0, 0, 0, 0, 16, 17, 20, 22, 24, 26, 28, 32, 33, 36]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   FLUSH
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 0, 0, 0, 0, 93, 93, 93, 93, 94, 95, 97, 98],
              [0, 0, 0, 0, 0, 0, 36, 36, 37, 38, 40, 44, 49, 61]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   FULL_HOUSE
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 98, 98, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100],
              [0, 64, 67, 70, 71, 73, 75, 77, 80, 82, 85, 88, 91, 94]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   FOUR_OF_A_KIND
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100],
              [0, 93, 94, 95, 95, 96, 96, 96, 97, 97, 98, 98, 98, 98]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   STRAIGHT_FLUSH
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0],
              [0, 0, 0, 0, 98, 98, 99, 99, 99, 99, 99, 99, 100, 0]],
          [  # 1   2   3   4   5   6   7   8   9   T   J   Q   K   A   ROYAL_FLUSH
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100],
              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100]]
      ];
      card=lll
      card_type=["","",""]#牌型
      matrix=[[0,0,],[0,0,],[0,0,],[0,0,],[0,0,],[0,0,],[0,0,],[0,0,],[0,0,],[0,0,],[0,0,],[0,0,],[0,0,]]#拿到的牌的列表,即为牌库
      dun=[[],[],[]]#分墩后存储的位置
      j=0;
      a=0;
      max=0
      for pai in card:#遍历字符串,转换需要的列表数据即拿到的牌
          if pai=="*":i=4;
          elif  pai=="$":i=1;
          elif pai == "&":
              i = 2;
          elif pai=="#":i=3;
          if pai.isdigit():
              if pai=="1":matrix[j][0]=10;matrix[j][1]=i;j=j+1;
              elif pai!="0":matrix[j][0]=int(pai);matrix[j][1]=i;j=j+1;
          elif pai=="J":matrix[j][0]=11;matrix[j][1]=i;j=j+1;
          elif pai=="Q":matrix[j][0]=12;matrix[j][1]=i;j=j+1;
          elif pai=="K":matrix[j][0]=13;matrix[j][1]=i;j=j+1;
          elif pai=="A":matrix[j][0]=14;matrix[j][1]=i;j=j+1;
    
      for i in combinations(matrix, 5):#调用combinations函数得出matrix的组合
          sum=0;
          test= list(i)
          test_data1 = matrix.copy()#备份牌库
          test = sorted(test, key=lambda x: (x[0], -x[1]))#排序,以牌面升序排序
          if test[0] in test_data1:#将选出的牌从牌库中去掉以免影响后续操作
              test_data1.remove(test[0])
          if test[1] in test_data1:
              test_data1.remove(test[1])
          if test[2] in test_data1:
              test_data1.remove(test[2])
          if test[3] in test_data1:
              test_data1.remove(test[3])
          if test[4] in test_data1:
              test_data1.remove(test[4])
          for k in combinations(test_data1, 5):#从牌库剩余的8张牌中,抽出5张得到中墩,余下3张为后墩
              test_data2=test_data1.copy()#备份
              if k[0] in test_data2:#将抽出的牌从备份牌库中去掉
                  test_data2.remove(k[0])
              if k[1] in test_data2:
                  test_data2.remove(k[1])
              if k[2] in test_data2:
                  test_data2.remove(k[2])
              if k[3] in test_data2:
                  test_data2.remove(k[3])
              if k[4] in test_data1:
                  test_data2.remove(k[4])
              pp=list(k)
              test1=list(sorted(pp, key=lambda x: (x[0], -x[1])))#排序,以牌面升序排序
              test2=list(sorted(test_data2,key=lambda x: (x[0], -x[1])))#排序,以牌面升序排序
              list1=list(operation(test))#判断各个分墩的牌型以及此牌型中牌面最大的值,如炸弹中炸弹的牌面、两对中最大的对子牌面,返回牌型等级及牌面最大值
              list2=list(operation(test1))
              list3=list(ope(test2))
              if list1[0] < list2[0] or list2[0] < list3[0] or list1[0] == list2[0] and list1[1] < list2[1] or list3[0] ==list2[0] and list2[1] < list3[1]or (list1[0]==list2[0]==7and list1[2]<list2[2]):#根据牌型等级及牌面最大值判断分墩是否合法,若不合法则跳过后续步骤直接下一个循环
                  continue
              else:
                  value1 = sda[list1[0] - 1][2][list1[1] - 1]#从权重表中取得对应权值
                  value2 = sda[list2[0] - 1][1][list2[1] - 1]
                  value3 = sda[list3[0] - 1][0][list3[1] - 1]
    
                  sum = value1 + value2 + value3
                  if sum > max:#比较三墩总权值,大的留下
                      dun = [test2, test1, test]
                      max = sum
                      card_type[2] = get_type(list1[0])#将牌型对应名称赋值进对应墩
                      card_type[1] = get_type(list2[0])
                      card_type[0] = get_type(list3[0])
      s = jiema(dun)#将处理完后的列表转换为字符串数据
      s=s+["三墩牌型:"]+card_type
      print(s)#输出分墩情况及牌型
      return s
  • 判断各个分墩的牌型以及此牌型中牌面最大的值,如炸弹中炸弹的牌面、两对中最大的对子牌面,返回牌型等级及牌面最大值
def operation(list):#判别牌型,返回牌型对应数字与此牌型中关键牌值,传入参数升序排列的列表
    sum=[0,0,0];
    a=[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]]#a[0]代表此位置对应牌的牌面,a[1]代表此牌面的牌有几张,可通过a[1]判断炸弹、葫芦、三条等有多张同牌面的牌型,
    for list1 in list:
        for count in range(5):
            if a[count][0]==list1[0]:
                a[count][1]=a[count][1]+1
                break
            elif a[count][0]==0:
                a[count][0] = list1[0]
                a[count][1] = a[count][1] + 1
                break
        count=0
    if(list[0][0]+4==list[1][0]+3==list[2][0]+2==list[3][0]+1==list[4][0]):#按序排列
        if list[0][1]==list[1][1]==list[2][1]==list[3][1]==list[4][1]:#同花色
            sum=[10,list[4][0]]#同花顺
            return sum
    else:
        count=0
        for jojo in a:
            if jojo[1]==4:
                sum= [9,jojo[0]]#炸弹
                return sum
            elif jojo[1]==3:
                    if a[a.index(jojo)+1][1]==2:
                        sum= [8,jojo[0]]#葫芦
                        return sum
                    elif list[0][1]==list[1][1]==list[2][1]==list[3][1]==list[4][1]:
                        sum=[7,list[4][0],list[3][0]]#同花
                        return sum
                    elif (list[0][0] + 4 == list[1][0] + 3 == list[2][0] + 2 == list[3][0] + 1 == list[4][0]):
                        sum=[6,list[4][0],list[3][0]]#顺子
                        return sum
                    else :
                        sum=[5,jojo[0]]#三条
                        return sum
            elif jojo[1]==2:
                if a[1][1]==3:
                    sum= [8,a[1][0]]#葫芦
                    return sum
                elif list[0][1] == list[1][1] == list[2][1] == list[3][1] == list[4][1]:
                    sum =[7, list[4][0],list[3][0]] # 同花
                    return sum
                elif (list[0][0] + 4 == list[1][0] + 3 == list[2][0] + 2 == list[3][0] + 1 == list[4][0]):
                    sum = [6,list[4][0]]  # 顺子
                    return sum
                elif a[a.index(jojo)+1][1]==2:
                    if jojo[0]+1==a[a.index(jojo)+1][0]:
                        sum= [4,a[a.index(jojo)+1][0]]#连队
                        return sum
                    else:
                        sum=[3,a[a.index(jojo)+1][0]] #两对
                        return sum
                elif a[a.index(jojo)+2][1]==2:
                    sum =[3 ,a[a.index(jojo)+2][0]] # 两对
                    return sum
                else:
                    sum=[2,jojo[0]] #对子
                    return sum
        if list[0][1] == list[1][1] == list[2][1] == list[3][1] == list[4][1]:
            sum =[7 ,list[4][0] ,list[3][0]]# 同花
            return sum
        elif (list[0][0] + 4 == list[1][0] + 3 == list[2][0] + 2 == list[3][0] + 1 == list[4][0]):
            sum =  [6 ,list[4][0]] # 顺子
            return sum

        else:
            sum =  [1,list[4][0]]
            return sum
    if list[0][1] == list[1][1] == list[2][1] == list[3][1] == list[4][1]:
        sum =[7,list[4][0]]   # 同花
        return sum
    elif (list[0][0] + 4 == list[1][0] + 3 == list[2][0] + 2 == list[3][0] + 1 == list[4][0]):
        sum = [6 ,list[4][0]] # 顺子
        return sum

    else:
        sum =  [1,list[4][0]]#散牌
        return sum
    return sum
def ope(list3):#求前墩的牌型,返回值与operation()相同
    sum=0
    if list3[0][0]==list3[1][0]==list3[2][0]:#三条
        sum=[5,list3[2][0]]
        return sum
    elif list3[0][0]==list3[1][0]:#对子
        sum=[2,list3[1][0]]
        return sum
    elif list3[2][0]==list3[1][0]:#对子
        sum=[2,list3[1][0]]
        return sum
    else:
        sum=[1,list3[2][0]]#散牌
        return sum
  • 前端

6.性能分析与改进

改进的思路

  • 一开始定的权值策略不佳,导致容易出现葫芦拆成三条和对子,甚至偶尔会出现不合法的情况,后来苦心查阅各方资料问大佬同学,了解到权值矩阵的存在,一用上就体会到前所未有的顺滑
  • 用了权值矩阵之后,偶尔还是会有报错,多次debug后发现是在判断合不合法的条件语句出问题,后面改成判断是否不合法,相对简单一些。
  • 一开始憋着一股劲,一股脑把算法敲出来,后来发现敲出来的东西bug实在太多,其实应该再开始前就先查阅资料,了解背景,然后打码的时候也得保持冷静,大脑发热万万不可

性能分析图和程序中消耗最大的函数

本次性能分析是利用cprofile--python性能分析工具

7.单元测试

  • 下图是测试用到的函数,代码比较长,限于篇幅不全展开
    • 主函数代码展示
    class TestDo(TestCase):
      def test_best_cards_type():
          card_dic = []
          for i in ['*', '#', '&', '$']:
              for j in ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']:
                  card_dic.append(i + j)
          num = 10
          while num:
              cards = ""
              s = copy(card_dic)
              for i in range(13):
                  j = numpy.random.randint(0, len(s))
                  if i != 12:
                      cards += s[j] + ' '
                  else:
                      cards += s[j]
                  s.pop(j)
              num -= 1
              do(cards)
      test_best_cards_type()
  • 对do函数即分牌函数进行单元测试
    测试数据:随机生成,不特意构造
    测试思路:利用随机生成的数据对division进行测试,可完成对其实用性和可靠性检测
  • 利用coverage对测试程序进行覆盖率分析

    • 分析图

8.Github的代码签入记录


9.遇到的困难及解决方法

  • 陈荣杰
困难描述 解决尝试 是否解决 有何收获
排行榜、历史战局、登录注册要从服务器得到数据并展示 查找资料、根据十三水api上的样例来仿作、求助队友 第一次尝试调用网络接口,requests真的好用
分墩赋权值算法不够优 查阅资料、询问大佬 原来还有十三水权值矩阵这东西,真好用嘿嘿
如何设计算法实现最优牌型的选择 第一下想法是判断自己有何牌型,然后从牌型到的到小的剔除,再重新判断,后面用了组合的想法来判牌、分牌 学习了组合数算法,itertools库真好用
  • 王嵚
困难描述 解决尝试 是否解决 有何收获
能够实现前端的工具很多,不知道用哪种比较好比价简单 之前做第一次个人编程的时候下载了pycharm,也学了一点py,b站上也有关于pyqt5详细的教程,于是决定用pyqt5做 粗浅地学习了一些pyqt5
熟悉的同学中没有和我用相同工具写前端的,代码出现错误时没有腿报 看博客,看视频,自己乱试 锻炼了自学习能力

10.评价队友

  • 值得学习的地方
    承当了更多的任务,算法、接口、博客的大部分内容,互相进度配合的较好,算法靠谱上分利器,接口好用拿来就能用,一定程度上监督了我缓慢的进度,让效率低的我可以专心学习并完成前端内容
  • 需要改进的地方
    没有,超棒的!

11.学习进度条

  • 王嵚
第N周 新增代码(行) 累计代码(行 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 0 0
2 500 500 20 20 pyqt没学会多少python的语法倒是学了不少
3 1000 1500 30 50 实现了前端
  • 荣杰
第N周 新增代码(行) 累计代码(行 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
4 0 0 10 10 对项目的需求分析和原型设计的了解更深
6 800+ 800+ 5 15 十三水出牌算法的实现,以及py自带堆的学习
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!