• 第二次结对编程作业


    1、链接

    队友博客链接戳这里
    本次作业博客链接戳这里
    github项目地址
    UI演示视频

    2、具体分工

    • 王景弘:负责实现AI部分,设计出牌算法,编写单元测试,提供算法思路、关键、改进和性能分析等博客内容。
    • 陈靖雯:负责实现UI部分,用接口连接算法形成最终文件,提供UI部分博客内容,用markdown格式编写博客内容。

    3、PSP表格

    PSP2.1 Personal Software Process Stages 预估耗时
    (小时)
    实际耗时
    (小时)
    Planning 计划 1 1.5
    · Estimate · 估计这个任务需要多少时间 45 50
    Development 开发 5 5
    · Analysis · 需求分析 (包括学习新技术) 8 7.5
    · Design · 生成设计文档 0.5 0.5
    · Design Review · 设计复审 0.2 0.3
    · Coding Standard · 代码规范 (为目前的开发制定或选择合适的规范) 1 1
    · Design · 具体设计 4 5
    · Coding · 具体编码 15 20
    · Code Review · 代码复审 1 2
    · Test · 测试(自我测试,修改代码,提交修改) 2 3
    Reporting 报告 1 0.8
    · Test Report · 测试报告 1 0.5
    · Size Measurement · 计算工作量 0.1 0.1
    · Postmortem & Process Improvement Plan · 事后总结, 并提出改进计划 0.5 0.5
      · 合计 45.3 50.2

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

    (1)网络接口的使用

    按照所给的接口文档,调用所给接口。本次作业采用html、css、javascript实现UI部分,需要新建一个XMLHttpRequest对象,向所给地址发送GET或POST请求,需要在控制台观察是否响应以便修改错误。成功响应的请求会返回需要的内容。发送的数据用JSON.stringify()转换成json字符串发送,返回的json字符串用JSON.parse()转换为JS对象,再逐一匹配标签id显示到页面中或进行其他操作。由于用到Token认证,需要用cookie保存token值并加入到请求头中。

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

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

    算法关键:

    • 实现的时候需要用到很多重复的switch和if/else语句。
    • 发到的牌按大小排完序之后,存入数组中,按照相同的牌的数量来分类。
    • 给每种牌型赋予权重值。
    • 利用switch语句不断的找到手牌中所有的牌型。
    • 之后将最大的拿出来留给后墩,然后剩下的牌再去寻找最大的牌型留给中墩,前墩的牌型就放在最后用剩下来的三张牌补进去,直到三道都有牌型。
    • 再把牌放到Choice.java中创建的三个数组中(即前中后墩)
    • 最后转换为接口所需的数据格式

    关键实现部分流程图

    5、关键代码解释

    AI部分:
    最开始发到牌后,将牌从小到大排序,存储到数组中。

    	public void change(List<Card> handCard)
    	{
    	    Collections.sort(handCard,new Comparator<Card>()          //从小到大排序
    		{
    			public int compare(Card c1, Card c2) 
    			{  
    				int i = c1.rank - c2.rank;  
    				if(i == 0)  
    				    return c1.type - c2.type;    
    				return i;
    			}   
    		});
    	arrange(handCard);        //将牌整理到arr中
    

    将排好序的牌按照相同的牌的数量来对数组进行分类

        for(int i=0;i<handCard.size();i++)
    	{ 
    		if((i+1)<handCard.size()&&handCard.get(i).rank==handCard.get(i+1).rank)
    		if((i+2)<handCard.size()&&handCard.get(i).rank==handCard.get(i+2).rank)
    		if((i+3)<handCard.size()&&handCard.get(i).rank==handCard.get(i+3).rank)    //四张相同的牌
    		{
    			arr.ranknum4.addAll(handCard.subList(i,i+4));
    			i+=3;
    		}
    		else                                               //三张相同的牌
    		{
    			arr.ranknum3.addAll(handCard.subList(i,i+3));
    		    i+=2;
    		}
    		else                                   //两张相同的牌
    		{
    			arr.ranknum2.addAll(handCard.subList(i,i+2));
    			i+=1;
    		}
    		else                                  //没有相同的牌
    		{
    			arr.ranknum1.add(handCard.get(i));
    		}
    	}
    

    通过判断读取出的对子数目和剩下的牌的数目来判断是否有牌型
    此处为判断是否有四套三条和五对三条

        if(arr.ranknum3.size()==12||(arr.ranknum3.size()==9&&arr.ranknum4.size()==4))              //四套三条
    	{
    		choice=tochoice(handCard);
    		choice.headType="sitaosantiao";
    		return;
    	}
    	if(arr.ranknum2.size()==10&&arr.ranknum3.size()==3)       //五对三条
    	{
    		choice=tochoice(handCard);
    		choice.headType="wuduisantiao";
    		return;
    	}
    

    通过arr.ranknum4.size和arr.ranknum3.size的大小来判断牌型,并且改变Choice.java中的前中后墩数组(choice.headType/midType/endType)来进行出牌时的文字说明

    	if(arr.ranknum4.size()==4)                  //尾道为铁支
    	{
    		choice.end.addAll(arr.ranknum4);
    		card.removeAll(choice.end);
    		arrange(card);
    		if(arr.ranknum3.size()==6)              //中道是葫芦
    		{
    			if(!arr.ranknum2.isEmpty())
    			{
    				choice.mid.addAll(arr.ranknum3.subList(3, 6));
    				choice.mid.addAll(arr.ranknum2);
    				choice.midType="hulu";
    				choice.head.addAll(arr.ranknum3.subList(0, 3));
    				choice.headType="santiao";
    				card.removeAll(choice.head);
    				card.removeAll(choice.mid);
    			    choice.end.addAll(card);
    				choice.endType="tiezhi";
    				return;
    			}
    			else
    			{
    				choice.mid.addAll(arr.ranknum3.subList(1, 6));
    				card.removeAll(choice.mid);
    				choice.end.add(card.get(0));
    				choice.head.addAll(card.subList(1, 4));
    				choice.endType="tiezhi";
    				choice.midType="hulu";
    				choice.headType="wulong";
    				return;
    			}
    		}
    

    UI部分:
    登录的请求成功后会返回一个token,要用cookie保存,之后的请求要用split函数从cookie中提取出token放在其他需要token的请求的请求头中。

    xhr.addEventListener("readystatechange", function () {
          if (this.readyState === this.DONE) {
          console.log(this.responseText);
          var JsonObj = JSON.parse(this.responseText);
          if(JsonObj.status==0)
          {
            document.cookie = JsonObj.data.token;
            valid();
          }
          }
          });
    
        var token = document.cookie.split(";")[0];
        xhr.setRequestHeader("x-auth-token",token);
    

    进行下一次查询时需要删除之前表格中存在的数据,用到deleteRow函数将存在的行删除,若按从下标小到大的顺序删除,在删除过程中行的下标会不断改变,所以要从大到小删除。

        var tb = document.getElementById('table');
        var rowNum=tb.rows.length;
        for (var j=rowNum-1;j>=0;j--) //下标会变化,要从后往前删
        {
            tb.deleteRow(j);
        }
    

    6、性能分析与改进

    (1)描述改进思路

    一开始只是单纯的想全用if/else语句来进行所有牌型的罗列但发现工程量实在是太大了,还是得逐步分析才能得到解法。后面想到先对花色和点数综合排序之后填入数组进行第一次分类(分点数大小的类),然后用判断数组大小的方式来进行第二次分类(分牌型的类,如对子三条之类的),再根据第二次分类出的结果来判断有没有特殊牌型(如全大全小、至尊青龙、五对三条之类的),最后给每种判断出的牌型赋予权重,用以判断什么牌型该放在什么位置。在进行完判断牌型的步骤之后再进行一下文字说明。

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

    7、单元测试

    public static void main(String[] args) {
            List<Card> allCards = new ArrayList<>();
            Random random = new Random();
            CardController cardController = new CardController();
            for (int i = 1;i <= 4; i++){
                for (int j = 1; j <= 13; j++){
                    allCards.add(new Card(j,i));
                }
            }
    
            for (int i = 0; i < 10; i++){
                List<Card> tempCards = new ArrayList<>();
                List<Card> cards = new ArrayList<>();
                tempCards.addAll(allCards);
                for (int j = 0; j < 13; j++){
                    Card card = tempCards.get(random.nextInt(52 - j));
                    cards.add(card);
                    tempCards.remove(card);
                }
                String cardStr = "";
                for (int j = 0; j < 13; j++){
                    Card card = cards.get(j);
                    cardStr += card.toString();
                    if (j < 12)
                        cardStr += " ";
                }
                JSONObject jsonObject = new JSONObject();
                try {
                    jsonObject.put("id",0);
                    jsonObject.put("card",cardStr);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
    
                System.out.println(cardController.card2(jsonObject.toString()));;
    
            }
        }
    

    每次随机构造13张牌,并且转化为符合json格式的字符串,再调用接口,得出结果

    8、Github的代码签入记录


    9、遇到的代码模块异常或结对困难及解决办法

    (1)问题描述

    UI部分:

    • 不熟悉如何将json数据显示在页面中。
    • 接口调用不知道如何实现。
    • http请求一直失败,显示未授权。

    AI部分:

    • 因为刚开始接触java,关键的算法也是一筹莫展。
    • 在调用接口的时候也遇到了不会调接口,每个请求的写法不会,无数次请求失败。

    (2)做过哪些尝试

    UI部分:

    • 在百度等渠道疯狂搜索解决方法,查找之前练习的代码。
    • 询问同学。

    AI部分:

    • 接口和java算法的问题都是老老实实看资料学习

    (3)是否解决

    UI部分:
    已解决。(以下每一点与问题一一对应)

    • 正确使用javascript,理清标签父子关系,使用json.parse()将json格式转化成js对象。
    • 接口文档有code generation,根据需要修改。
    • token值没有保存,设置的请求头有错,使用了document.cookie解决。

    AI部分:

    • 已解决。

    (4)有何收获

    因为刚刚接触java很多知识都不甚了解
    这次比较复杂的程序设计也算是逼迫我在ddl之前尝试了一次敏捷开发
    虽然很多代码都是对着查到的资料现学现用,但也算是收获了不少java语法、算法相关的知识


    10、评价你的队友

    (1)值得学习的地方

    • 界面设计得很漂亮,对UI设计有经验。

    (2)需要改进的地方

    • 没啥需要改进的地方已经做得很好了

    11、学习进度条

    第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
    1 476 476 15 15 熟悉了java的基本语法
    2 421 897 10 25 通过练习,掌握了数组的多种用法
    3 1300 2197 25 50 完善了剩余的算法,学习了接口的使用

    制作UI时查找到的实用资料

    HTML页面跳转的5种方法
    用js实现动态添加表格数据
    JSON.parse()
    js中设置元素class的三种方法小结
    js中删除table里所有行
    详细介绍NW.js基本使用
    HTML网页打包成EXE可执行文件
    将Token添加到请求头Header中
    JSON.parse() 与 JSON.stringify() 简单使用
    Session与Token认证机制 前后端分离下如何登录
    js使用sessionStorage、cookie保存token

  • 相关阅读:
    PHP命名空间
    php各个版本的区别
    MySQL的读写分离---主从复制、主主复制
    redis的安装和php的redis扩展
    php的设计模式
    git命令详情
    memcache和redis的对比
    mysql性能优化
    MySQL的分区、分表、集群
    nginx的反向代理和负载均衡
  • 原文地址:https://www.cnblogs.com/wjh-031702531/p/11681451.html
Copyright © 2020-2023  润新知