结对编程作业

https://github.com/sl0805/teamwork

组员姓名 分工 博客链接
孙巧(组长) 组织和安排工作分工、前端原型大致模板设计、前端原型实现、在线对战的实现 https://www.cnblogs.com/sl0805/p/15456219.html
邹莹 原型设计美化、本地对战的后端实现、撰写博客 https://www.cnblogs.com/zouzou-1/p/15451584.html

一、原型设计

1.1结对作业的设计说明

https://orgnext.modao.cc/app/8e557a845bf40c0817c337bd854badfaf9941e7a

  • 采用的原型开发工具:

    • 采用墨刀进行开发。
  • 首页界面:

    • 此页面可通过点击进入登录界面,也可通过点击进入游戏规则页面
  • 首页原型图:

    • image-20211023222605347
  • 登录界面:

    • 通过首页链接,需要用户提供学号和密码进行登录,而后链接至进入房间界面,同时点击返回首页
  • 登录界面原型图:

  • 进入房间界面:

    • 登录界面点击登录链接。在本界面提供创建房间选择,链接至模式选择界面加入房间选择输入房间号,进入游戏界面;这两种状态。
  • 进入房间界面原型图:

  • 模式选择界面:

    • 通过进入房间界面链接,提供本地模式、人机模式、在线模式选择,其中选择在线模式后,会弹出房间号,点击确定进入游戏界面。其余模式直接进入游戏界面
  • 模式选择原型图:

  • 游戏界面:

    • 游戏的主要界面,用户操作和最后结果判断弹出。可直接返回终止游戏至进入房间界面
  • 游戏界面原型图:
    -

  • 游戏规则界面:

    • 首页链接,目的在于为新手提供规则。可返回至游戏首页。应用轮播图实现规则图片的播放。
  • 游戏规则界面原型图:

  • 原型整体文字阐述

    • 用户打开程序,首先进入首页,点击登录游戏进入登录界面,登录成功进入进入房间界面,通过点击加入房间直接进入游戏界面,或者通过点击创建房间进入模式选择界面之后在选择模式进入游戏界面,同时游戏结束可以回到进入房间界面
  • 原型界面逻辑图

1.2遇到的困难及解决方法:

  • 困难描述

    制作游戏界面的时候,寻找不到一整幅牌的图片以供设计和后来的原型实现。

  • 解决过程

    从开始的不断从各种图片网站上查找(找不到完整的或者需要会员),到转变思路查找有没有扑克牌制作软件,果不其然是有的,之后通过这个软件只做了一整幅牌。

  • 有何收获

    首先具备了简单制作扑克牌牌面的能力了;其次遇到问题想想多种解决思路。

二、原型设计实现

2.1代码实现思路:

  • 网络接口的使用:

    • 登录接口:用于获取用户的token值,并在之后请求其他接口时配置进header中,避免“鉴权失败”的情况发生。以下是对接口的请求代码:
    • 创建房间接口:只需将token装进header中进行访问,可设置房间为公开或私有,返回值中包含房间uuid。
    • 查询房间与加入房间接口:可以通过用户token查询到已有的房间列表,选择自己要加入的房间号,在设置的文本框中输入uuid并进入游戏。也可以同时在两个设备上操作,自己加入自己创建的房间。
    • 对于获取上一步操作接口的使用:用于获取牌局进展和更新界面纸牌分布,和计算结果,完成托管所需要的信息存储。以下是对接口的请求代码:
  • 代码组织与内部实现设计(类图):

    • 后端的代码组织:
    • 前端的代码组织:
  • 说明算法的关键与关键实现部分流程图:

    • 后端认为关键的算法:

      • 关键的算法一定是托管函数这一块,但是写的太简单,没有达到很好的效果。
      • 流程图
    • 前端认为关键的算法:

      • 应该是获取上一步操作后对两个玩家以及放置区牌的整理分析部分,设计收牌等等函数
      • 获取从手牌中出的是哪张牌
  • 贴出你认为重要的/有价值的代码片段,并解释:

    • 后端认为最有价值的代码片段是接口函数,原因是:实现了接口函数,才能实现前后端沟通。一下是一个简单的代码片段:

      # 本地托管功能和实现人机对战的那个机的出牌
      @app.route("/trusteeship_api", methods=["POST"])
      def trusteeship_api():
          req_data = request.get_json()
          player = req_data.get("player")  # 用户 p1=0,p2=1
          return_list=trusteeship(player)
          # 检验放置的牌是否花色相同,并完成牌归入想要操作者的牌里
          recover = check_placementarea(player)  # p1收走:1 p2:2 无:0
          # 检测牌局是否结束
          winner = calculate_for_winner()  # p1win:1 ; p2win:2 ; 平局:3 ;游戏未结束:0
          word =str(player) + ' ' + str(return_list[1]) + ' ' + return_list[0]
          load = []
          data = {"last_code": word, "recover": recover, "winner": winner, "p1_pokers":p1_pokers, "p2_pokers":p2_pokers,"placearea_pokers":placementarea_pokers}
          load.append(data)
          return jsonify(code=200, data=load, msg="操作成功")
      
    • 前端认为最有价值的代码片段是在线对战时判断是否收牌以及获取出牌样式的函数,原因是:前者设计玩家手牌和放置区的更新,后者涉及wxml界面及js之间的数据传递

      //判断是否收牌,并执行收牌操作
        take_card:function(out_card){
          var that=this
          if(out_card==this.data.placearea_pokers[this.data.placearea_pokers.length-1]){//收牌
            this.setData({
              player1:this.data.player1.concat(this.data.placearea_pokers),
              placearea_pokers:[]
            })
            return true
          }
          else{//不收牌
            this.setData({
              //在末尾加一张牌
              placearea_pokers:this.data.placearea_pokers.splice(-1,0,out_card)
            })
            return false
          }
        }
      
      //获取从手牌取出的牌是什么,只要知道p1就好了
        whatCard:function(e){
          if(e.currentTarget.id=='C1'){
              this.setData({
                card:this.data.player1_C[this.data.player1_C.length-1]
              })
          }
          else if(e.currentTarget.id=='D1'){
              this.setData({
                card:this.data.player1_D[this.data.player1_D.length-1]
              })
          }
          else if(e.currentTarget.id=="H1"){
              this.setData({
                card:this.data.player1_H[this.data.player1_H.length-1]
              })
          }
          else{
            this.setData({
              card:this.data.player1_S[this.data.player1_S.length-1]
            })
          }
        },
      
      
      
  • 以下主要是对后端(实现本地对战部分)的分析,前端因为使用微信小程序开发者工具,对于单元测试的方法在网上暂没找到合适的教程和案例。

  • 性能分析与改进:

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

      这是对后端进行的性能分析,因为需要接口的特殊性,所以耗时最大的函数全都是与接口配置有关的。其他自行编写的函数在它们的耗时下显得微不足道。以下是其中一个接口函数。

      @app.route("/action", methods=["POST"])
      def action():
         req_data = request.get_json()
         player = req_data.get("player")  # 用户 p1=0,p2=1
         type = req_data.get("type")  # 操作类型 type默认为0:0为翻开卡组的牌,1为从手牌打出
         card = req_data.get("card")  # 当type参数为1时需要填写,例如:S1 或 D10 或 HQ 或 C7 等(格式为花色缩写+1~K标编号,花色见备注 黑桃:S红桃:H梅花:C方块:D
         if type == 0:
             card = extract_from_cardset(player)
         else:
             extract_from_hand(player, card)
         # 检验放置的牌是否花色相同,并完成牌归入想要操作者的牌里
         recover = check_placementarea(player)  # p1收走:1 p2:2 无:0
         # 检测牌局是否结束
         winner = calculate_for_winner()  # p1win:1 ; p2win:2 ; 平局:3 ;游戏未结束:0
         print("放置区:", placementarea_pokers)
         print("p1:", p1_pokers)
         print("p2:", p2_pokers)
         word =str(player) + ' ' + str(type) + ' ' + card
         load = []
         data = {"last_code": word, "recover": recover, "winner": winner, "p1_pokers":p1_pokers, "p2_pokers":p2_pokers,"placearea_pokers":placementarea_pokers}
         load.append(data)
         return jsonify(code=200, data=load, msg="操作成功")```
      
    • 由以上的分析其实看不出自行编写的函数的性能,所以以下是按照函数调用顺序,执行了100000次后获得的性能分析,可以看出check_placementarea()函数耗时最长,原因在于需要对进行吃牌的操作,不断调整列表元素。

      # 检验放置的牌是否花色相同,并完成牌归入想要操作者的牌里
      def check_placementarea(player):
          last = len(placementarea_pokers)
          flag = 0
          if last > 1:
              if placementarea_pokers[last - 1][0] == placementarea_pokers[last - 2][0]:
                  if player == 0:
                      while (len(placementarea_pokers) > 0):
                          p2_pokers.append(placementarea_pokers.pop())
                      flag = 1
                  else:
                      while (len(placementarea_pokers) > 0):
                          p2_pokers.append(placementarea_pokers.pop())
                      flag = 2
      
          return flag
      
    • 改进思路:

      首先当然是对于接口的改进,查询资料有很多对于python+flask的改进,但他们改进的对象是非常复杂的,所以对于我们的问题帮助不大;对于自行编写的代码的改进思路是能不能减少对消耗量大的函数的调用(函数用于检测是否有吃牌现象,减少调用的可能性不大),还有就是能不能更加快速的完成两个列表元素的转移。以上是所有的改进思路。
      首先是wxml界面和js界面之间的数据传递,在这方面使用还不熟练。其次是在连续访问接口上,使用了递归函数,刚开始是打算用while循环,后来发现使用不了,上网查资料发现可以用setTimeout函数,但是学了好一会儿没学会,就暂时放弃了。还有就是界面设计不太行,对css的一些属性操作还不是很ok,界面交互性差。接下来的改进会朝着这三个方面发展。

  • 展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路:

    • 对构造纸牌函数和从牌组里抽取纸牌的函数进行测试;测试思路生成52张牌,那么抽取53次,前52次一定是返回牌面信息,最后一次返回none;测试代码(测试的函数说明在代码注释里),及测试结果:

      import unittest
      from action import init_card_set,extract_from_cardset
      
      class extract_form_hand_Test(unittest.TestCase):
          def test_extract_from_hand(self):
              #init_card_set为构造牌组函数,一共52张,测试重点在于是否生成了52张
              init_card_set()
              i=52
              while i>0:
                  #extract_from_cardset(1),P1操作者从牌组里抽取纸牌,返回牌面,类似'SQ'
                  card=extract_from_cardset(1)
                  i=i-1
                  assert card!='none'
              card=extract_from_cardset(1)
              assert card=='none'
      
    • 对检测是否吃牌情况的函数的单元测试;被测试的函数就是一个检测是否出现吃牌情况的函数;数据构造思路:直接引用生成一副纸牌并洗牌的代码,生成一个具有随机性的牌组,在这种情况下,对最后两张牌进行判断花色是否相同。测试代码如下:(及测试结果)

      import random
      import unittest
      from action import check_placementarea, placementarea_pokers
      
      pokers1 = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
      pokers_color = ['H', 'S', 'C', 'D']  # 黑桃:S;红桃:H;梅花:C;方块:D SQ:黑桃Q
      
      class test_check(unittest.TestCase):
          def test_check_placementarea(self):
              i = 100
              while i > 0:
                  flag = 0
                  for t in pokers_color:  # 初始化牌组
                      for j in pokers1:
                          card = t + j
                          placementarea_pokers.append(card)
                  random.shuffle(placementarea_pokers)  # 洗牌
                  if placementarea_pokers[51][0] == placementarea_pokers[50][0]:  # 检测花色是否相同
                      flag = 1
                  result = check_placementarea(0)  # 检测是否有吃牌现象,此时假设为P1回合操作,因为P1回合吃牌返回1,未吃牌返回0
                  assert flag == result  # 判断
                  while (len(placementarea_pokers) > 0):
                      placementarea_pokers.pop()
                  i = i - 1
      
    • 同时,这次作业对于小组自己写的接口用postman进行了测试,减少了前后端不必要的沟通。

2.2贴出Github的代码签入记录,合理记录commit信息。

2.3遇到的代码模块异常或结对困难及解决方法。

  • 困难描述

    在最紧张的时间里,发现已完成的代码和模块设计无法满足作业要求,导致后端的部分代码不能用,前端压力剧增。
    最大的问题是,一开始理解错了接口返回值的意思,导致前端写的一些代码逻辑上出了问题,等到发现的时候已经快到ddl了。还有测试在线对战上,直到最后一天才发现可以手机和电脑自己测试,但是因为校园网很卡,电脑登着vpn又没办法手机调试,因此频繁的登出登入vpn耗费了不少时间,很麻烦。还有就是不是很能明白先后手问题,导致做在线部分一直磕磕绊绊。

  • 解决过程

    分析问题所在,肯定解决方法只有前端埋头苦写。那么为了争取时间,后端转化工作重心,去完成博客等其他任务,保证前端有尽量多的时间去解决问题。
    在完成前端过程中,不断地debug,找出自己逻辑不对的地方,但是最后还是有些地方没改出来。在遇到自己之前没有学过的知识的时候,就通过上网查百度。在遇到理解性问题的时候和同学进行讨论。

  • 有何收获

    首先,遇到这种问题一定是前期没有设计好,所以一定要在前期做好准备工作,深刻地明白了理解对题目的意义和接口的具体作用是多么重要。

    其次,坦然接受,然后赶紧拿出最有利于现状的解决方案。

2.4评价你的队友。

  • 孙巧对邹莹的评价

    • 值得学习的地方
      担当与责任:很有责任心,在发现我们的项目出现种种问题的时候,主动去把一些剩余任务完成了,让我在ddl快到的时候也不至于太惊慌。
      能力:学习能力强,在合作过程中觉得对方的进度很快。
      心态:有我不会但我会学并且能好的自信,而且遇事不会惊慌。
    • 需要改进的地方
      有一定的拖延症,要在结对的过程中更加主动的学习一些东西。
  • 邹莹(博客本人)对孙巧的评价

    • 值得学习的地方

      担当与责任:组长的带头能力很棒,很照顾我这个小白,所有的模块都是组长带起来,我再边学边写。

      心态:心态超棒,我们在完成作业的过程中遇到了不小的困难,前端的要一下就增大了许多,但是依旧很稳的完成了。

    • 需要改进的地方

      如果能够多组织一些讨论和交流就更好了,因为有一部分的时间都再等待对方动手自己再开始,当然这也有我的责任,想偷懒没去主动。

2.5此次结对作业的PSP和学习进度条(每周追加)

  • PSP
PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 60 70
Development 开发
· Analysis · 需求分析 (包括学习新技术) 240 250
· Design Spec · 生成设计文档 60 50
· Design Review · 设计复审 30 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 20
· Design · 具体设计 240 250
· Coding · 具体编码 2100 2000
· Code Review · 代码复审 60 160
· Test · 测试(自我测试,修改代码,提交修改) 120 60
Reporting 报告
· Test Repor · 测试报告 240 220
· Size Measurement · 计算工作量 30 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 120 100
· 合计 3330 3220
  • 孙巧的PSP和学习进度条
第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 0 0 5 5 探讨分工与大致原型,选择原型设计工具和实现形式
2 0 0 6 11 学习使用墨刀和完成原型设计雏形
3 300+ 300+ 20 31 学习微信小程序开发过程,学习微信开发者工具使用
4 2000+ 2000+ 40 71 实现原型设计,学习接口连接,实现前端的本地对战功能、在前端实现在线对战功能
  • 邹莹(博客本人)的PSP和学习进度条
第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 0 0 5 5 探讨分工与大致原型,选择原型设计工具和实现形式
2 0 0 6 11 学习使用墨刀和完成补充队友的原型设计以及美化、理顺逻辑
3 0 0 12 23 学习微信开发者工具的使用,学习python后端知识(数据库、flask)
4 244 244 31 54 实现本地对战(对战、托管)的后端,写了对于在线对战的存储和托管的后端(由于不可抗因素没法使用),学习python的单元测试,学习github知识,撰写博客

三、心得

3.1孙巧(组长)的心得体会:

  • 对于作业难度的想法:
    我觉得对我来说还是有一定难度的,为了这次作业,我熬了很多个夜,虽然最后结果不是很ok。我之前只知道一些名词的意思是什么,没有动手自己操作过,一上手就问题重重。通过这次作业,学习了不少新知识,也是第一次使用微信小程序编写代码。
  • 对于作业的感想:
    这个打牌方式是我之前没有接触过的,玩起来很有趣,但是要让我写出这样一个小游戏还是有一定困难的。
  • 对之后学习的启发:
    之后会继续去提升自己的前端水平以及学习能力,还要提高自己的审美和逻辑处理、理解能力,不然做出来的程序应该都不会有人想用。
  • 其他:
    大三以来做了几次的结对作业,搭档一直都是同一个人,虽然我们一起踩过不少坑,但是也有一种共同成长的感觉,觉得还是很美好的。

3.2邹莹(博客本人)的心得体会:

  • 对于作业难度的想法:

    对于作业难度的想法,首先一定是觉得难,但不是不可完成、完全无法下手的。这次作业要求学习的新知识不少,起码对于自己来说,除了python语言是上一次学习了的之外,剩下的所有都是要现学现用的。不过,这对大部分人来说应该是一样的。所以,其实作业对于我们这部分人来说就是迫使我们去学习新的知识,比的是大家的学习能力和时间的规划能力,当然还有两人的合作完美程度。综上,从我的角度来看,这次结对作业是真的可以很好地展示和区分每个人的综合能力的,所以难度应该是属于困难模式的。

  • 对于作业的感想:

    对于作业的感想,首先是猪尾巴玩起来很有趣,感谢介绍了这么一款游戏,能让完全不会玩牌的我,在过年的时候也能展现自己的牌技。其次是作业内容很丰富,比如最开始的github的基本使用,到现在readme等编写,说实话,如果作业不要求,我也许很久很久都不知道这些东西。总而言之,对作业是又爱又恨吧。

  • 对之后学习的启发:

    其实经过这两次的作业,能明显感觉得自己的在进步,一直以来自己的学习都处于一个很被动的状态,尽管现在也是被作业驱动着学习,但不同于最开始的满腹牢骚、迟迟不愿面对,能够很坦然的、和迅速的面对作业和新知识的学习了。

    启发一:与其一直说我不会,不如早点去百度相关知识。

    启发二:有疑问就直接去问知道的人。因为自己瞎琢磨最后得到的答案错误几率太大,时间也浪费了。

    启发三:勤劳(感觉不恰当,但是找不到更好的词去形容,文学素养亟待提高),尽管很老套,但在这两次作业的完成过程中,勤劳地学习和打代码是最关键的。

  • 其他:

    感谢我的组长,这次作业如果没有她带着大方向走得话,我可能连怎么开始学习都不知道。也感谢她能够忍受我逻辑奇怪的代码。以后争取写出优美的代码!还有最后一点就是,对于组长还是抱有愧疚的,如果我能主动脱离后端这个标签主动学习JavaScript这些前端知识,不至于让后面几天都是她一人在孤军奋战,连一个讨论的人都没有。最后的最后,感恩!

posted @ 2021-10-24 22:59  zouzouy  阅读(67)  评论(0编辑  收藏  举报