结对编程作业

https://github.com/929580032/CardGame

姓名 具体分工 博客链接
谢文灏 游戏逻辑、AI算法 https://www.cnblogs.com/duanledexian/p/15430572.html
宁乙浩 原型设计、素材收集 https://www.cnblogs.com/ningdada007/p/15456005.html

一、原型设计

*1.1 - 设计说明:

原型作品链接:https://modao.cc/app/design/pbkuibfpcrweh8jv

本次原型模型设计工具是墨刀,具体原型设计如下:

  • 游戏开始界面:提供双人对战、人机对战和在线对战三个模式供玩家选择。

  • 游戏对战界面:

  • 玩家抽牌:

  • 玩家出牌:

  • 玩家吃牌:

  • 登录界面:用户通过输入学号和密码进行登录操作,并跳转至房间在线界面

  • 在线界面:用户可以创建对局或者输入uuid加入对局

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

​ 在使用原型模型设计工具的时候遇到了各种各样的困难,由于是第一次使用墨刀原型设计工具,对各种组件的使用和事件的添加不熟悉,花费了很多时间去学习如何使用界面组件的使用和事件的添加功能,并且在进行原型设计的时候,要对收集的图片素材根据自己的需求进行一定的处理,所以我们团队还花了许多时间去学习PS,来更好地对图片进行切图、裁剪等处理操作。

*1.3 - 收获:

​ 我们学会使用了许多设计工具,如墨刀、PS等工具,感受到了原型设计在开发过程中的重要性,原型设计可以起到沟通的作用,方便我们队伍两人之间的沟通交流,更好地理解互相的想法,也感受到了PS对处理图片方面上功能非常的强大、非常的便捷。

二、原型设计实现

*2.1 - 代码实现思路:

*[2.1.1]网络接口的使用:

创建了一个网络接口工具类,其中使用了Java中的okhttp库来实现对网络接口的请求和接收,并使用Java中的fastjson库来实现对接收到的json字段的处理。

网络接口工具类UML类图:

  • login(String, String):接收参数学号和密码,返回字符串token,实现网络接口中的登录功能;

  • joinGame(String, String):接收参数token和对局标识uuid,实现网络接口中的加入对局功能;

  • createGame(String, String):接收参数token和private(是否为私人项目),返回对局标识uuid,实现网络接口中的创建对局功能;

  • getLastOperation(String, String):接收参数token 和对局标识uuid,返回Json字符串交给程序进一步处理,实现网络接口中的获取上步操作功能;

  • doPlayerOperation(String, String, PvpMain):接收参数token、对局标识uuid和游戏界面对象PvpMain,返回卡牌对象Card,实现网络接口中的执行玩家抽牌操作功能;

  • doPlayerOperation(String, String, String):接收参数token、对局标识uuid和卡牌名称,实现网络接口中的执行玩家出牌操作功能。

在线对战时,创建一个线程,线程一直循环运行,使用网络接口工具类中的getLastOperation()方法,来不断请求网络接口来获取上步操作:

public void run() {
        super.run();
        String last = null;
        while (true) {
            try {
                Thread.sleep(1);
                String jsonString = NetworkInterfaceUtils.getLastOperation(pvpMain.token, pvpMain.uuid);
                doAction(jsonString ,last);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

*[2.1.2]代码组织与内部实现设计:

卡牌类:

  • name:卡牌图片文件的名字;

  • color:卡牌花色;

  • up:判断卡牌是否正面朝上;

  • canClick:判断卡牌是否可以点击,当卡牌在牌堆或放置区时为false,当卡牌在玩家手牌里时为true;

  • moveCard():玩家点击手牌时,手牌向上或向下移动,再点击时手牌回到原来的位置;

  • turnFront():玩家抽牌时,卡牌从背面翻到正面。

抽牌操作类(实现监听器接口,来实现抽牌按钮点击操作):

  • turnOver(Card):执行玩家抽牌操作,将牌堆顶上的牌移动到放置区顶上;

  • eatCard():判断当前抽出的牌是否跟放置区顶上的牌花色一样,若一样,则玩家获取放置区内所有手牌;

  • gameEnd():判断当前牌堆数量是否为0,若是,则弹出游戏结束的窗口并判断哪一方获胜;

  • switchPlayer():轮到对方进行操作。

出牌操作类(实现监听器接口,来实现出牌按钮点击操作):

  • placement(Card):执行玩家抽牌操作,将玩家手牌内中被选中的卡牌放入放置区顶上;
  • eatCard():判断当前抽出的牌是否跟放置区顶上的牌花色一样,若一样,则玩家获取放置区内所有手牌;
  • switchPlayer():轮到对方进行操作。

游戏界面类:

  • buttonsInit():初始化游戏按钮,按钮的位置摆放以及外观设置,出牌按钮添加监听器(出牌操作类),抽牌按钮添加监听器(抽牌操作类);

  • CardInit():初始化牌堆卡牌,牌堆和放置区的位置摆放,牌堆中的卡牌进行洗牌操作;

  • jLabelsInit():游戏文字内容的位置摆放和外观设置。

*[2.1.3]算法的关键与关键实现部分流程图:

  • 游戏界面逻辑没有什么算法

  • AI算法

    如果牌比对面少且满足以下任何一种情况就抽牌

    • 抽牌后可以保证不会吃牌

    • 牌堆数 * 2 + 放置区牌数 + 自己手牌数 < 对方手牌数

    • 牌堆数为1且抽牌后无论吃牌或者不吃都获胜

    • 无论出哪张牌都会吃牌

    • 牌堆数小于等于5,如果吃牌后牌数小于对方的手牌的一半

    如果不满足以上条件不能抽牌的话,就选择出牌,出牌分为以下几种情况:

    • 对面没有手牌,就出当前牌堆中花色最多的牌

    • 牌堆数小于等于5,出当前牌堆中花色最多的牌

    • 对面有手牌的话,就出自己手牌中花色对多的牌

  • 部分代码展示:

    //如果牌比对面少且牌堆数 * 2 + 放置区牌数 + 自己手牌数 < 对方手牌数,这样一直抽牌就能获胜
    if (pvpMain.cardList.size() * 2 + playerCards2.size() + pvpMain.cardsPlacement.size() < playerCards.size()) {
                turnOverAction.turnOver();
                return;
    }
    
    //线程一直循环运行,使用网络接口工具类中的getLastOperation()方法,来不断请求网络接口来获取上步操作
    public void run() {
        super.run();
        String last = null;
        while (true) {
            try {
                Thread.sleep(1);
                String jsonString = NetworkInterfaceUtils.getLastOperation(pvpMain.token, pvpMain.uuid);
                doAction(jsonString ,last);
                last = jsonString;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    //牌比对面少且抽牌后可以保证不会吃牌,就抽牌
    if (playerCards2.size() < playerCards.size() && (pvpMain.cardsPlacement.size() == 0 || map.get(pvpMain.cardsPlacement.get(pvpMain.cardsPlacement.size() - 1).color) == 0)) {
      turnOverAction.turnOver();
      return;
    }
    
    //牌堆数小于等于5,如果吃牌后牌数小于对方的手牌的一半
    if (pvpMain.cardList.size() <= 5 && playerCards2.size() + pvpMain.cardsPlacement.size() + 1 < playerCards.size() / 2) {
      turnOverAction.turnOver();
      return;
    }
    

*[2.1.4]性能分析与改进:

  • 经过性能分析工具jprofiler分析出性能消耗最大的函数,是线程中获取上步操作的函数,由于线程不停的发送请求,造成了严重的耗时
  • 改进思路:减少线程每次获取上步操作的时间间隔,时间间隔太短会造成严重的耗时,时间间隔太长对用户体验不好,用户点一下过很久界面才能更新,在多次测试下,找到了两者的平衡点,大约是0.5s

*[2.1.5]单元测试:

  • 测试游戏开始时,洗牌是否成功:

    public void testShuffleCards() {
        List<Card> initCardList = new ArrayList<>();
        List<Card> cardList = new ArrayList<>();
        for (int i = 1; i <= 52; i++) {
            String color = "";
            switch ((i - 1) / 13) {
                case 0: color = "黑桃";break;
                case 1: color = "草花";break;
                case 2: color = "红桃";break;
                case 3: color = "方块";break;
            }
            Card card = new Card(null, Integer.toString(i), false, color, (i % 13 == 0) ? 13 : i % 13, null);
            initCardList.add(card);
            cardList.add(card);
        }
        Collections.shuffle(cardList);
        boolean testResult = false;
        for (int i = 0; i < 52; i++) {
            if (!initCardList.get(i).equals(cardList.get(i))) {
                testResult = true;
                break;
            }
        }
        Assert.assertTrue(testResult);
    }
    

*2.2 - Github的代码签入记录:

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

  • 在编写代码的过程中,遇到了各种各样的代码模块异常,其中最令我棘手且花费了我最多时间的是在我编写网络接口部分的代码的时候,经常会因为发送请求中参数设置不正确或是请求中json字段不正确,出现异常返回错误信息,debug了很久才找到错误;

  • 由于是第一次使用Java中的swing来编写界面程序,不太熟悉界面中组件的使用,经常会在玩家抽牌、出牌的时候报空指针异常,最后在网上搜索相关资料后才发现了问题所在之处;

  • 由于之前都是一个人写代码,这次是两人组队完成作业,会出现很多困难,经常出现一个人提出一个改进设计的想法,但是另一方在代码上无法实现的尴尬情况;两个人的具体分工也是个困难,后来我们根据各自擅长的方向来进行具体分工。

    收获

    第一次进行结对编程我们有很多收获,虽然结对的过程中遇到了不少的困难,但是两个人的力量总是大于一个人的力量,两人合理地分工可以让开发效率大大提高。

*2.4 - 评价队友:

  • 谢文灏:

    队员宁乙浩是一个非常上进的人,热爱学习新技术,学习原型设计工具墨刀的使用和PS的使用,搜集图片素材,并花了很长时间进行原型设计,帮助我测试游戏是否能够正常运行,也帮我找了许多的bug,并且提出了很多改善游戏的好想法。需要改进的地方是代码水平不高,debug能力不强。

  • 宁乙浩:

    队友谢文灏非常热爱学习,很多时候都能看见他在敲代码。执行能力巨强,想到一个好的点子能够快速的敲出来,一直能够积极乐观的面对困难,不会做的东西就立马去学,从零开始学习各种东西,不抱怨。非常感谢队友给我积极向上的精神。

*2.5 - PSP:

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

*2.6 - 学习进度条:

N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
1 378 378 6 6 熟悉原型模型设计工具墨刀的基本使用。
2 575 953 7 13 熟悉Java语言swing和awt的基本使用以及界面组件component的基本使用。
3 586 1539 8 21 熟悉了Java语言中swing中鼠标监听器的使用和接口测试工具postman的使用。
4 512 2051 16 40 学会使用了Java中的okhttp库对网络接口进行请求和接收,以及Java中的fastjson对接收Json字段的处理。

三、心得

  • 队长谢文灏:

    刚开始看到题目要求的时候,看了好久才看明白,题目要求涉及到了许多部分,如界面设计UI、AI和网络接口等,一开始连界面设计都不知道要用什么语言来实现,但是在看到舍友在用python中的pygame来做界面设计时,我就想起了java中也有swing来进行界面设计,花了几天从零开始学swing,在网上看视频学习,做了个贪吃蛇游戏来练手,在网上搜索一些卡牌游戏的代码来学习,后来实现网络接口的时候,庆幸自己之前学了一些java后端和一些前端知识,这些能够帮助我理解网络接口的文档说明,在网上搜索java的HTTP库时,发现了一个巨好用的网络接口测试工具postman,帮助我更好地实现网络接口。

  • 队员宁乙浩:

    本次结对作业,看到题目的时候感觉自己是弄不出来了的,写代码能力比较弱。队友就很强,很快就完成了基本的代码。我就负责原型设计,由于第一次接触原型设计,花了很多时间去了解使用墨刀原型设计的工具,收集素材。由于个人比较菜,设计的东西可能达不到预计的效果。也学习了一点PS的使用,让我又多学会了一种工具的使用。

posted @ 2021-10-24 22:17  ningdada  阅读(23)  评论(0编辑  收藏  举报