结对编程作业

https://github.com/uryyy16/Card-game

姓名 博客链接 具体分工
黄慧卿 https://www.cnblogs.com/2636r/p/15440455.html 1.设计原型 2.编写网络接口 3.编写界面 4.研究托管算法
刘璐瑀 https://www.cnblogs.com/Es-war/p/15452317.html 1.设计原型 2.编写 js 代码 3.研究托管算法

一、原型设计

https://modao.cc/app/a4c1256fc26a4af04f45bd73e153fec069099124
1.说明你所采用的原型开发工具
采用的原型工具:MockingBot

  • 登录界面:用户输入账号密码,若账号密码正确,点击“确定”后可跳转到游戏模式选择界面;不正确,则弹出提示信息,告知用户“账号或密码错误”。

  • 游戏模式选择界面:包括三种模式:人人对战,在线对战,人机对战。若选择人人对战和人机对战,则直接进入游戏界面;若选择在线对战,则进入创建或加入房间。

  • 创建或加入房间界面:包括两个选择:创建房间、加入房间。若选择创建房间,则跳转至创建房间界面;若选择加入房间界面,则跳转至加入加入房间界面

  • 创建房间界面:创建成功后,剪切板自动复制该对局邀请码,点击“确定”后跳转至游戏界面。

  • 加入房间界面:输入正确邀请码,进入该房间,跳转至游戏界面。

  • 游戏界面:三个界面基本相同,除了在线对战界面增加托管功能。

  • 游戏结束界面:显示游戏结果。

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

  • 困难描述
    • 之前从未接触过原型设计,由于这次是卡牌游戏,所以很自然地联想到了斗地主,想要模仿它的设计。我们两个人都不会PS抠图,最开始的时候,手动截取了52张不同的扑克,想要借助QQ的截图工具来截扑克牌,但是这样麻烦不说,图片大小尺寸也不一致,后期定位起来十分麻烦。
    • 不知道要设计出什么风格的界面。
    • 设计游戏界面时,起初的想法是将所有的牌都展示出来,受限于手机屏幕的大小,这样设计会显得较为杂乱。
  • 解决过程
    • 后来有同学提醒我们说可以在淘宝买斗地主素材,于是斥巨资5元购入一堆斗地主素材。
    • 游戏名为“猪尾巴”,而小猪佩奇动画的画风简单、明丽、可爱,决定选用小猪佩奇的图片当做界面的背景,界面输入框、按钮等均设置为透明。
    • 简化游戏界面,用一张扑克背面来表示卡组,卡池区只显示当前顶部花色,玩家手牌均只显示四种不同花色的牌及其数量,而不是具体展示出花色及数字。
  • 有何收获
    • 淘宝是万能的。
    • 初步学习到了原型设计相关的过程、步骤。
    • 多借鉴市面上设计精美的软件、小程序。
    • 一个清晰的原型设计可以为之后的实现打下良好的基础。

二、原型设计实现

1.代码实现思路

  • 网络接口的使用

    • 登录接口:将输入的账号密码数据通过该接口发送给服务器,请求成功后,获取 token,存入全局变量中,用于后续请求接口。
    • 创建对局接口:当用户选择创建房间时,发送请求至服务器,请求成功后获取相应的 uuid 作为邀请码,将此 uuid 存入全局变量中,用于后续游戏进行时向服务器发送具体操作,并可将此邀请码发送给想要邀请的用户,便于之后加入同一房间。
    • 加入房间接口:用户输入相应的邀请码后,将此邀请码作为其 uuid 存入全局变量中,用于后续游戏进行时向服务器发送具体操作。
    • 执行玩家操作接口:抽取手牌时,需告知服务器具体花色与数字;抽取卡组时,需向服务器发送请求,根据返回的参数获取所抽取牌的具体信息。
    • 获取上步操作接口:向服务器发送请求,询问当前是谁的回合,若为己方回合,根据返回信息,在本地对对方的上步操作进行及时更新处理,然后监听己方的具体操组,并通过“执行玩家操作接口”向服务器发送对应请求。
    • 获取对局信息接口:对局结束后,向服务器发送请求,根据返回的信息获取赢家信息,并记录到全局变量 winner 当中,根据房主及返回数据综合判断该对局赢家。
  • 代码组织与内部实现设计(类图)

    • 登录界面函数

      • inputName():绑定用户账号输入函数
      • inputPWD():绑定用户密码输入函数
      • login():登录函数
    • 模式选择界面函数

      • choose_1():绑定用户选择人人模式函数
      • choose_2():绑定用户选择在线模式函数
      • choose_3():绑定用户选择人机模式函数
    • 选择创建或加入房间界面函数:

      • createRoom(): 绑定用户选择创建房间函数
      • joinRoom():绑定用户选择加入房间函数
    • 创建房间界面函数

      • joingame():绑定用户选择创建房间函数
      • onLoad():监听页面加载函数:发送请求给服务器端
    • 加入房间界面函数

      • inputInfo():拿到用户输入信息函数
      • bindbtn():绑定用户点击“加入房间”按钮函数
    • 游戏界面函数

      • init()函数:初始化卡牌
      • judge_type()函数:判断所抽取的牌花色与卡池区花色是否一致
      • add_pool()函数:将所抽取的牌放入卡池区
      • take_from_inside()函数:玩家从手牌中抽取
      • take_from_outside()函数:玩家从卡组中抽取
      • judge_game_over()函数:判断对局是否结束
      • player()函数:绑定玩家点击卡牌事件函数
      • get_poke()函数:绑定玩家的点击卡组事件
      • watch():观察者
      • judge_type()函数:判断所抽取的牌花色与卡池区花色是否一致
      • tell_0()函数:告知服务器抽取卡组操作
      • tell_1()函数:告知服务器抽取手牌操作
      • AI_robot()函数:托管函数
      • change_robot()函数:切换托管
      • get_info()函数:结束时获取对局信息
      • get_last()函数:获取上步操作
      • onLoad()函数:加载页面
      • onUnload()函数:卸载页面函数
    • 游戏结束界面:

      • onLoad()函数:监听页面加载函数,根据全局变量winner渲染界面
      • onShow()函数:监听页面显示界面,根据全局变量winner渲染界面
  • 说明算法的关键与关键实现部分流程图
    三种模式的逻辑基本一致,在线对战在实现上略有不同,下面以在线对战为准,说明实现思路

  • 贴出你认为重要的/有价值的代码片段,并解释(2分)

      //获取上步操作
      get_last: function(){
        var op_ty
        current_player = 1
        var that = this
        this.get_info()
        this.data.interval = setInterval(function () {
            console.log('在线对战循环')
            current_player = 1
            wx.request({
                url: 'http://172.17.173.97:9000/api/game/' + app.globalData.uuid + '/last',
                method: "GET",
                header: {
                    "Authorization": wx.getStorageSync('token')
                },
                data: {
                },
                success: res => {
                    console.log(res)
                    if(res.data.code == 400){
                        clearInterval(that.data.interval)
                        that.get_info()
                        //that.judge_winner()
                    }
                    if(res.data.data.your_turn == true){
                        current_player = 0
                        clearInterval(that.data.interval)
                        console.log('为我的轮次')
                        that.setData({
                            msg: '己方回合'
                        })
                        //记录上步操作
                        var info = res.data.data.last_code
                        console.log('拿到对方的操作为:' + info)
                        if (info.length != 0){
                            current_player = 1
                            tmp_ty = info[4]
                            tmp_num = info[5]
                            op_ty = info[2]
                            console.log("拿到对方操作")
                            console.log(op_ty)
                            console.log(tmp_ty)
                            console.log(tmp_num)
                            if (info.length == 7)  tmp_num += info[6]
                            if (op_ty == '0')  that.take_from_outside()
                            else  that.take_from_inside(1)
                        }
                    }
                    else{
                        that.setData({
                            msg: '对方回合'
                        })
                    }
                },
                fail: res => {
                    console.log("123456" )
                    console.log(res)
                }
            })
        },1000)
        //this.get_info()
        console.log('退出循环')
        //记录玩家1的操作
        current_player = 0;
        if (deposit_0 == 1)  this.AI_robot()
      },
      watch: {                                    //观察者
          done: function(newValue,oldValue){
              if(newValue){                       //done为true,本回合结束
                  this.data.done = false;                     //开启下一回合
                  this.get_last();    //开启下一回合
              }
          },
          robot: function(newValue,oldValue){   //观察是否在托管状态
              if(newValue){
                  this.AI_robot();
              }
          }
        },
调用get_last()函数获取上步操作以及判断当前为谁的轮次,并与 watch()函数搭配使用,解决线程卡死问题。
  • 性能分析与改进
    有部分冗余代码,托管函数不够智能

  • 描述你改进的思路

    1.通过数据路径的写法,来将数据分批的传输到视图层中,减少一次性setData的数据大小
    2.减少setData的数据量
    3.合并setData的请求,减少通讯的次数
    2.压缩代码,清理无用的代码
    3.采用分包策略
    4.手动的“清理”后台界面

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

    程序中消耗最大的函数:get_last()
    不断向后端发送请求(每隔一秒),获取上步操作,直到当前回合为己方轮次,由于网络质量较差,耗时较长。

  • 展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路
    这次没有引入后端,全是在小程序的 js 文件中,通过 JavaScript 语言来实现游戏逻辑,所以并没有编写单元测试代码。代码的正确性在对战中检测,每次对局中观察具体出牌情况及处理的正误。

2.Github使用相关

  • README

  • .gitignore

  • 使用分支管理提交代码,使用pull request

  • 开源协议

  • Issues模板

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

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

  • 困难描述

    • 接口使用混乱,经常出 bug。
    • web 可以为每个鼠标点击事件设置一个独立的线程,一开始以为小程序也可以支持这样的操作,但是在在线对战中发现一旦运行 js 代码,无法监听到点击事件,输出进程 id 后发现原来是进程在循环体那部分上卡死了,虽然代码逻辑上没有错误,但是无法响应点击事件,就永远无法跳出循环;不使用循环,又不知道如何不断监听、记录玩家行为。
  • 解决过程

    • 认真研究各个接口,厘清各接口的正确使用方法、需要传递的参数,分析其对该游戏实现上的作用。
    • 了解到小程序可以引入“观察者”,一旦其所观察的值发生变化,可以立刻执行其内部逻辑代码。弃用循环体,引入观察者,不断监听具体数据的变化,以达到原先循环体的效果,并且不会导致进程的卡死。
  • 有何收获

    • 遇事不决,多问同学。
    • 学会了如何使用“观察者”。
    • 掌握了接口的使用方法。

4.评价你的队友

  • hhq
    • 值得学习的地方
      • 乐观向上的精神
      • 赶ddl的强大心脏
    • 需要改进的地方
      • 下次别赶 ddl 了
  • lly
    • 值得学习的地方
      • 无惧挑战的精神
      • 极限编程的魄力
    • 需要改进的地方
      • 下次别再一起当 ddl 战士了

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

  • 学习进度条(一周极限编程)
第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 3736 3736 54.3 54.3 熟悉微信小程序的相关语法,学习JavaScript、“观察者”的使用
  • PSP
PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
· Estimate · 估计这个任务需要多少时间 60 120
Development 开发
· Analysis · 需求分析 (包括学习新技术) 540 600
· Design Spec · 生成设计文档 40 30
· Design Review · 设计复审 60 120
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 30 10
· Design · 具体设计 300 600
· Coding · 具体编码 1000 1400
· Code Review · 代码复审 60 40
· Test · 测试(自我测试,修改代码,提交修改) 200 290
Reporting 报告
· Test Repor · 测试报告 30 10
· Size Measurement · 计算工作量 15 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 30 30
· 合计 2290 3260

三、心得

  • hhq
    • 前期觉得一个月时间绰绰有余,一直都是慢慢悠悠的状态。摆烂到最后一周才开始写这次作业,所有人都觉得我们两个这次真的写不完了。每天都是不断赶进度,最近一周的口头禅就是“我们要死了”,“我们可以活”。刚开始设计原型时,一顿乱搞之后又推翻重来,各种手忙脚乱。之前虽然写过 web 界面,但是不涉及到和后端的交互,因此查看接口文档时,也是一头雾水,再加上自己理解上的偏差,导致接口使用混乱,询问同学之后,才逐步弄明白究竟如何正确发送请求。但是服务器依托于校园网,校园网质量不佳,有时候会导致一些莫名其妙的 bug。之前使用 GitHub 时,都只是简单地使用一下上传功能,由于这次作业的硬性要求,极速学习了一些 GitHub 相关操作,进一步加深了对其的理解。这次结对编程结束之后,紧接着就是团队编程作业,希望这次好好规划,尽早完成自己地工作,不要再赶 ddl。
  • lly
    • ddl果然是第一生产力,此次为期一个月的编程作业,硬是拖成一周极限编程。知道我们这周才开始写作业的同学,基本上都觉得我们要完蛋了,每天都是“死去活来”的,一下觉得“我们可以活”,一下觉得“我们死定了”,如此反复循环。遇到了各种各样奇奇怪怪的 bug,小程序不像 web,天生地支持多线程,在运行其他代码时也可以响应鼠标点击事件。一开始由于线程卡死,我们所编写的小程序无法响应鼠标点击事件,多亏了同学提醒,才知道可以引入“观察者”来处理,跳出死循环。这次作业的过程中,我们两人基本都是一起完成的,时间太赶,再加上不知道如何使用后端来实现一些算法逻辑,所以将逻辑实现这部分全部放入 js 中,可能是不熟悉 js 吧,总是有些意想不到的 bug,关键这些 bug 还不是因为逻辑错误引起的,也是够玄学的,只能说“不要靠近,会变得不幸”。经历了这一周极限编程,身心俱疲,希望下次尽早规划,不要再当 ddl 战士。
  • 特别鸣谢
    感谢郑浩彬,陈志良,周浩东三位同学的热心帮助
posted @ 2021-10-24 23:00  urrry  阅读(53)  评论(0编辑  收藏  举报