• 结对编程作业


    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 战士。
    • 特别鸣谢
      感谢郑浩彬,陈志良,周浩东三位同学的热心帮助
  • 相关阅读:
    分治
    #include<algorithm>
    c++标准模板库的使用
    mysql_day03
    mysql_day02
    mysql_day01
    mongodb的安装
    迭代器和生成器简单介绍
    File文件操作
    数据类型
  • 原文地址:https://www.cnblogs.com/Es-war/p/15452317.html
Copyright © 2020-2023  润新知