结对编程作业

作业相关

  • 王祺👈这是俺
  • 傅兴佳👈这是俺队友
  • huarong-road👈这是俺的Github项目地址(俺负责的AI部分)
  • Klotski👈这是俺队友Github项目地址(俺队友负责的小游戏部分)
  • 具体分工(表格)
王祺 傅兴佳
AI的设计 原型设计
AI的实现 原型设计的实现(微信小程序)

其实还是分的挺开的,不过关于图像处理和游戏求解的算法部分的思路我们会一起讨论


原型设计

工具

本次使用Axure RP进行小程序的原型设计,成品请在微信小程序内搜索:Klotski 无广告


设计说明

原先考虑简约多彩的配色方案,然而马上发现自己的配色实在不堪入目,于是选择了简约黑白灰的配色方案。

小程序分为3个逻辑页面:首页、游戏页面、排行页面。首页为一级页面,游戏页面、排行页面为二级页面

设计1

首页

点击按钮——开始游戏,进入游戏界面

设计2

点击按钮——难度,选择难度系数

设计3

点击按钮——Top榜,进入排行页面

设计4

点击图像——声音,开关游戏音效

设计5

点击图像——分享,将小程序分享给微信好友

游戏页面

点击按钮——开始,开始游戏,打乱数字,页面自动更新时间、步数

设计6

点击按钮——重置,一切回到未开始状态

设计7

游戏结束,点击确定按钮,回到未开始状态

设计8

排行页面

记录每个系数难度所花最短时间

设计9


结对的过程

为啥会组在一起呢?我想想大概有以下几点:

  • 老乡原则:都是物信大二转过来的难兄难弟
  • 就近原则:他当时组队时候就在我旁边
  • 聚类原则:技术栈相近,比较好交流

讨论


遇到的困难及解决方法

  • 困难描述:第一次画原型,还准备自己动手画状态栏之类的,不好画。
  • 解决尝试:之后上网查了才发现元件库这个好东西,直接复用,大大提高了效率
  • 收获:动手前多吸取前人经验,事半功倍

原型设计实现

空白方块的移动算法

	for (let i in numData) {
      if (index == i) {
        let x = '';
        // 当前点击的 上下左右 方向如果有空位的话,就互换位置
        if (numData[index - difficulty] && numData[index - difficulty].isEmpty) {  // 下
          x = index - difficulty;
        } else if (numData[index + Number(difficulty)] && numData[index + Number(difficulty)].isEmpty) {//上
          x = index + Number(difficulty);
        } else if (numData[index - 1] && numData[index - 1].isEmpty) {  // 左
          // 如果是在最左边的话,禁止向左移动
          for (let h = 1; h < difficulty; h++) {
            if (index == difficulty * h) return;
          }
          x = index - 1;
        } else if (numData[index + 1] && numData[index + 1].isEmpty) {  // 右
          // 如果是在最右边的话,禁止向右移动
          for (let h = 1; h < difficulty; h++) {
            if (index == difficulty * h - 1) return;
          }
          x = index + 1;
        } else {
          return; // 没有空位不操作
        }

        [numData[i], numData[x]] = [numData[x], numData[i]];
        step += 1;
        if(isAudioPlay=="true") {
          innerAudioContext.play(); // 移动效果音乐
        }

数据的持久化

 onLoad: function (options) {
    let that = this
    wx.getStorage({//第一次进入游戏,创建top榜数据格式,并存入缓存
      key:"top",  //缓存中的数据大约只能储存7天
      fail (res) {
        wx.setStorage({
          key:"top",
          data:that.data.top
          })
        }
    })
  },

AI设计实现

代码实现思路

网络接口的使用

此次作业,我们用的是现在比较主流的json

关于Java对象和json串之间的转换,我用的是阿里家的fastjson,使用时直接依赖导入就行啦!(Maven工程下)

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.73</version>
</dependency>

使用教程👈戳这里

json图片

Java对象与json串之间的转换问题解决了,那json串怎么发出去呢?又怎么接收回来呢?HTTP的请求——GETPOST

package util;

import java.io.*;
import java.net.*;

public class HttpJSONUtil {

    /**
     * GET请求
     * @param GET_URL 请求的url
     * @return json串
     * @throws IOException
     */
    public static String readContentFromGet(String GET_URL) throws IOException{
        // URL请求,建立连接
        URL getUrl = new URL(GET_URL);
        HttpURLConnection connection = (HttpURLConnection) getUrl.openConnection();
        connection.connect();
        // 将返回内容读入内存中(设置编码,否则中文乱码)
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));
        StringBuffer lines=new StringBuffer();
        String line=null;
        while ((line = reader.readLine()) != null){
            lines.append(line);
        }
        // 释放资源,断开连接
        reader.close();
        connection.disconnect();
        return lines.toString();
    }

    /**
     * POST请求
     * @param POST_URL POST请求的url
     * @param params json串形式的POST表单
     * @return json
     * @throws IOException
     */
    public static String readContentFromPost(String POST_URL, String params) throws IOException{
        // URL请求,建立连接
        URL postUrl = new URL(POST_URL);
        HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setRequestMethod("POST");
        // Post 请求不能使用缓存
        connection.setUseCaches(false);
        connection.setInstanceFollowRedirects(true);
        // 注意:一定要设置json格式
        connection.setRequestProperty("Content-Type","application/json");
        // 连接,从postUrl.openConnection()至此的配置必须要在connect之前完成,
        // 要注意的是connection.getOutputStream会隐含的进行connect。
        connection.connect();
        DataOutputStream out = new DataOutputStream(connection.getOutputStream());
        // DataOutputStream.writeBytes将字符串中的16位的unicode字符以8位的字符形式写道流里面
        out.writeBytes(params);
        out.flush();
        out.close();
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));//设置编码,否则中文乱码
        String line=null;
        StringBuffer lines=new StringBuffer();
        while ((line = reader.readLine()) != null){
            lines.append(line);
        }
        // 释放资源,断开连接
        reader.close();
        connection.disconnect();
        return lines.toString();
    }

}

代码的话,主要就是IO的读写操作,HTTP的连接啥的。

需要注意的地方有:(坑点)

  • 编码格式!!!UTF-8!!!
  • POST提交json串表单,要把Content-Type设置为application/json,我就被这个坑了!
  • 连接资源要记得释放!!!

代码组织与内部实现设计

  • entity包的(主要是json对象)

entity

  • game包的(游戏模拟)

game

  • util包的(各种网络、图像识别的工具类)

util


算法的关键与关键实现部分流程图

在看完这篇论文后,我决定使用BFS来写八数码

  • 游戏规则是:先步数,后时间
  • BFS能保证步数是最优解

所以我选择了BFS,虽然效率差些,我可以用别的地方弥补,比如:

  • 通过静态类加载,空间换时间,减少IO耗时
  • 通过逆序数判断有无解,而不是让它去跑到没解为止

游戏的模拟部分说白了,就是BFS+逆序数+一个Node内部类记录状态,因为代码太长了,我这边也不方便贴,可以直接上我GitHub看;这并不是难点所在我认为,坑的地方就是要用一个集合去记录状态,避免重复兜圈子

难点是在于把图片给矩阵化,因为确实还蛮绕的

首先找到两组图片互相对应的位置(0-8)

6 7 0
1 5 -1
3 4 2

上面这个矩阵是board,-1代表空白的那张(用0怕和0重复)

我们在设定好未调整的target矩阵

0 1 2
3 4 5
6 7 8

接下来,我们要对两个矩阵进行一些处理

/**
     * 矩阵调整:空白-1转0,其他小于全白的上调1
     * @param board
     * @param target
     * @param whitePos
     */
private static void matrixAdjust(int[][] board,int[][] target,int whitePos){
    // 0+1+2+..+8 = 36 36-1=35
    int sum = 0;
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            sum += board[i][j];
        }
    }
    board[whitePos/3][whitePos%3] = 35 - sum;
    int white = board[whitePos/3][whitePos%3];
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if(board[i][j] < white){
                board[i][j]++;
            }
            else if(board[i][j] == white){
                board[i][j] = 0;
            }
            if(target[i][j] < white){
                target[i][j]++;
            }
            else if(target[i][j] == white){
                target[i][j] = 0;
            }
        }
    }
}

先找出缺的元素,再把board的-1转为缺的元素,为避免重复,其他数组元素凡是比缺了那块小的都加上1,而等于缺的元素的置0,target也是

比如上面的board矩阵我们缺的是8,那我就把-1置0,小于8的加1

7 8 1
2 6 0
4 5 3

target类似:

1 2 3
4 5 6
7 8 0

调整好矩阵,就可以开始玩耍了

  • 总体流程是这样的:

全流程图


Valuable Code Piece

这个我下文会细说,我这边先大概贴下代码说几句

public class ImgCompetition {

    public static List<byte[][]> picBytes = new ArrayList<>();
    private static byte[][] srcByteArray = new byte[9][16];

    private static PickThread[] pickThreads = new PickThread[36];
    private static AtomicBoolean hasFound = new AtomicBoolean(false);
    private static String globalTarget="";
    private static String globalFinalFile="";

    static{
        for(int i=0;i<36;i++){
            pickThreads[i] = new PickThread();
            pickThreads[i].id = i;
        }
    }

    /**
     * 初始化:将一张图片转化为均值哈希的byte数组
     * @param target "D:/testImg/targetX"
     * @throws IOException
     */
    private static void initOnePic(String target) throws IOException {
        String[] targetPieces = new File(target).list();
        FingerPrint fp = null;
        byte[][] picByteArray = new byte[9][16];
        int cnt=0;
        for (String targetPiece : targetPieces) {
            fp = new FingerPrint(ImageIO.read(new File(target+"/"+targetPiece)));
            picByteArray[cnt++] = fp.binaryzationMatrix;
        }
        picBytes.add(picByteArray);
    }

    /**
     * 全部初始化
     * @throws IOException
     */
    public static void init() throws IOException {
        for(int i=1;i<=36;i++){
            initOnePic(PathUtil.TARGET_PIECES+i);
        }
    }

    /**
     * 初始化json图片
     * @param src
     * @throws IOException
     */
    public static void initSrc(String src) throws IOException {
        String[] srcPieces = new File(src).list();
        FingerPrint fp = null;
        int cnt=0;
        for (String srcPiece : srcPieces) {
            fp = new FingerPrint(ImageIO.read(new File(src+"/"+srcPiece)));
            srcByteArray[cnt++] = fp.binaryzationMatrix;
        }
    }

    /**
     * 两组图片的比较(在初始化的情况下,通过byte[]比较)
     * @param index picBytes的索引
     */
    public static boolean compare1v1OnInit(int index) throws IOException {

        byte[][] picBytesArray = picBytes.get(index);

        for (byte[] srcByte : srcByteArray){
            float max = 0;
            for(byte[] pic : picBytesArray){
                float res = FingerPrint.compare(srcByte,pic);
                if(max < res){
                    max = res;
                }
            }
            if(max < 1){
                return false;
            }
        }
        return true;
    }

    /**
     * 图片匹配,查找与第几张相同(在初始化的情况下,通过byte[]比较)
     * @param target "D:/testImg/target"
     */
    public static String pickTheOneOnInit(String target) throws IOException {
        String finalFile="";
        for(int i=1;i<=36;i++){
            if(compare1v1OnInit(i-1)){
                System.out.println("匹配上了,第"+i+"张");
                finalFile = target+i;
                break;
            }
        }
        return finalFile;
    }

    public static String pickTheOneOnThread(String target){
        globalTarget = target;
        for (int i = 0; i < 36; i++) {
            if(pickThreads[i].isAlive()){
                pickThreads[i].interrupt();
            }
            pickThreads[i].start();
        }
        try {
            synchronized (hasFound) {
                hasFound.wait();//主线程等待
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return globalFinalFile;
    }

    static class PickThread extends Thread{

        // 0-35
        int id;

        @Override
        public void run() {
            try {
                if(compare1v1OnInit(id)){
                    globalFinalFile = globalTarget+(id+1);
                    synchronized (hasFound) {//获取对象锁
                        hasFound.notify();//子线程唤醒
                    }
                    System.out.println("匹配上了,第"+(id+1)+"张");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

这个类只要是负责找出是json传过来的是哪张图,与图库中的比较并匹配

我认为它好的原因是:

  • 实现真正意义的空间换时间(主要)
  • 多线程并发,开36条一起找(次要)

通过静态代码块和静态成员变量,在类加载之初就将图片通过IO读入内存转化为字节数组。我们都知道IO是非常耗时的,如果在开始就能处理完这些之后,再去做JSON请求,是不是会好很多呢?

我觉得Valuable Code Piece,并不是说你写的多优雅,用了多少设计模式,什么几行写完八皇后问题,关键就在于Valuable,它得是有价值的!!!我的代码很丑,没有任何复杂的语法、设计模式blabla的,但我认为他是有价值的,它完成了很大的优化,我从最开没优化的30s,到现在只要1s左右,我认为静态这个东西是有价值的。

然后是多线程吧,我这边用了一个原子的Boolean做标志,找到了就唤醒主线程,36条一起找,但是可能因为线程切换也需要一定资源吧,所以我的自做聪明似乎并没起到效果,速度实差不多的(我在想用python搞个并行矩阵,或者go的协程是不是会更好一点)


性能分析与改进

这边我分“图片识别与匹配”和“游戏模拟”两块说

图片识别与匹配

我继续上面的那个话题说吧~

开始呢,我想写个图片比较,具体说就是把两组图片(每组已切为9份儿)进行比较,看看是否相同,于是我这么写

public static float compare1v1(String src,String target) throws IOException {

    String[] srcPieces = new File(src).list();
    String[] targetPieces = new File(target).list();
    FingerPrint fp1 = null;
    FingerPrint fp2 = null;
    float[] ans = new float[9];
    int cnt=0;

    for (String srcPiece : srcPieces){
        float max = 0;
        for(String targetPiece : targetPieces){
            fp1 = new FingerPrint(ImageIO.read(new File(src+"/"+srcPiece)));
            fp2 = new FingerPrint(ImageIO.read(new File(target+"/"+targetPiece)));
            float res = fp1.compare(fp2);
            if(max < res){
                max = res;
            }
        }
        ans[cnt++] = max;
    }

    float sum = 0;
    for(int i=0;i<9;i++){
        sum += ans[i];
    }

    return sum/9;

}

这太脑残了!要做9 x 9 x 2 = 162次IO,这还只是比较一次两组,我要比36次!也就是我总共要搞5832次IO!假设我一次IO要5ms,比完已经花了29s左右了啊!!!这太糟糕了!!!

等等,是不是有什么问题,ans数组的意义在哪里?我只要找到最大值就可以啦,为何要找9个,然后再除以9呢?只要最大那个是1就行啦!(虽然这不是本质问题,但也算是优化了一点点)

public static boolean compare1v1(String src,String target) throws IOException {

    String[] srcPieces = new File(src).list();
    String[] targetPieces = new File(target).list();
    FingerPrint fp1 = null;
    FingerPrint fp2 = null;

    for (String srcPiece : srcPieces){
        float max = 0;
        for(String targetPiece : targetPieces){
            fp1 = new FingerPrint(ImageIO.read(new File(src+"/"+srcPiece)));
            fp2 = new FingerPrint(ImageIO.read(new File(target+"/"+targetPiece)));
            float res = fp1.compare(fp2);
            if(max < res){
                max = res;
            }
        }
        if(max < 1){
            return false;
        }
    }

    return true;

}

似乎好了一点,现在20s左右了hhh~

但还是没改到点上,但是当局者迷,旁观者清,当时的我以为已经改不动了,这时我的队友提醒我可以使用配置类或者xml,直接把字节数组存在文件里,到时候就不用转换了,我觉得这还是要IO,只是少了转换,但确实给了我很大的灵感!

我突然想起来静态成员这个东西,它在类加载时候就可以完成,我等全部准备好了,再去做json请求,(服务器从你请求开始计时的),所以整了这么一出“空间换时间”!

public static List<byte[][]> picBytes = new ArrayList<>();;

private static void initOnePic(String target) throws IOException {
    String[] targetPieces = new File(target).list();
    FingerPrint fp = null;
    byte[][] picByteArray = new byte[9][16];
    int cnt=0;
    for (String targetPiece : targetPieces) {
        fp = new FingerPrint(ImageIO.read(new File(target+"/"+targetPiece)));
        picByteArray[cnt++] = fp.binaryzationMatrix;
    }
    picBytes.add(picByteArray);
}

public static void init() throws IOException {
    for(int i=1;i<=36;i++){
        initOnePic(PathUtil.TARGET_PIECES+i);
    }
}

public static boolean compare1v1OnInit(int index) throws IOException {

    byte[][] picBytesArray = picBytes.get(index);

    for (byte[] srcByte : srcByteArray){
        float max = 0;
        for(byte[] pic : picBytesArray){
            float res = FingerPrint.compare(srcByte,pic);
            if(max < res){
                max = res;
            }
        }
        if(max < 1){
            return false;
        }
    }
    return true;
}

不仅是空间换时间的优化,而且还有一个地方,就是我发现没有与第一小份匹配的图片,我就直接出来,不比了!

这下时间直接飞跃到了2、3s!!!!!!!!!!!!!!

因为后面还有一个地方(我就不贴了),要把白色的那块找出来,我也用同样的思路,它更快了!


游戏模拟

这个也是我搭档想的,它翻阅资料,发现了逆序数这个玩意儿,可以用来判断是否有解

public boolean isSolvable(int[][] board, int[][] target){
    int targetSum = 0,boardSum = 0;
    for (int i = 8; i >= 0; i--) {
        if(target[i/3][i%3] == 0){
            continue;
        }
        for (int j = i-1; j >= 0; j--) {
            if(target[j/3][j%3] != 0 && target[i/3][i%3] > target[j/3][j%3]){
                targetSum++;
            }
        }
    }
    for (int i = 8; i >= 0; i--) {
        if(board[i/3][i%3] == 0){
            continue;
        }
        for (int j = i-1; j >= 0; j--) {
            if(board[j/3][j%3] != 0 && board[i/3][i%3] > board[j/3][j%3]){
                boardSum++;
            }
        }
    }

    if((boardSum&1) == (targetSum&1)){
        return true;
    }
    return false;
}

(我代码写的真的很臭...)

于是乎它又快了一些!


最后

把用到IO的地方,全都提前到静态类加载(请求来的就没办法了,不过也可以先转化为内存,也会快一点),再加入亿点细节之后,跑30个试试~(不是多线程的)

匹配上了,第22张
wwdssawwdssawdwaassdwwasddwaasddwaassdd,共39步
挑战成功!用时:1.13s
匹配上了,第4张
sawwdssawwdsaasddwaawddsaawdssawdsd,共35步
挑战成功!用时:0.94s
匹配上了,第22张
sdwwassdwwassdwwasawdssawddsawas,共32步
挑战成功!用时:0.33s
匹配上了,第22张
wwdssdwwassdwwassawwawddssawwdsaasdwwasdsa,共42步
挑战成功!用时:1.07s
匹配上了,第10张
wwdssdwwassdwwassawdwd,共22步
挑战成功!用时:1.20s
匹配上了,第27张
wwdssaawdwdssawasdwwassdd,共25步
挑战成功!用时:0.50s
匹配上了,第28张
wwassdwaasdwdsawwdsaawdsasdwwa,共30步
挑战成功!用时:1.17s
匹配上了,第31张
wwdssdwwassdwwasawddsawassdwwasdwdssaw,共38步
挑战成功!用时:0.65s
匹配上了,第11张
wwassdwwassdwwaasswdsdwawdsaawdsasddwaawdssd,共44步
挑战成功!用时:0.86s
匹配上了,第28张
sawwdssawwdssawdwaasdsdwwasdsawdwaassdd,共39步
挑战成功!用时:0.72s
匹配上了,第2张
swdsaawwdsdsaawdd,共17步
挑战成功!用时:1.11s
匹配上了,第23张
wwdssawwsaasddwaawdssawwdsd,共27步
挑战成功!用时:0.67s
匹配上了,第2张
wwdssdwwassdwwaswwdsaasddwwassdwwaasdwa,共39步
挑战成功!用时:1.14s
匹配上了,第6张
wwdssdwawassdwwasddwaassddwasa,共30步
挑战成功!用时:0.77s
匹配上了,第25张
wwdssdwwadsaasdwawdsasdwawd,共27步
挑战成功!用时:0.34s
匹配上了,第6张
wwdssawwsdsawwdssawasddwawdsaasddw,共34步
挑战成功!用时:1.08s
匹配上了,第28张
sdwaassddwwasasddwasa,共21步
挑战成功!用时:0.43s
匹配上了,第19张
wwdssawwdssaawdwdsawassddww,共27步
挑战成功!用时:0.98s
匹配上了,第31张
ssaawwddsasdwaawdds,共19步
挑战成功!用时:1.02s
匹配上了,第22张
wwassdwwassdwwaassdsaawwdsawddssawdsaawwdsaw,共44步
挑战成功!用时:1.23s
匹配上了,第35张
ssawwdssawwdssawwdsasawwddsasdwaasddwaaw,共40步
挑战成功!用时:0.85s
匹配上了,第21张
wwdssdwwassdwwaswasddwassdwwassawdsdwwa,共39步
挑战成功!用时:0.75s
匹配上了,第35张
swwdsasdwdsawawdsdwaassd,共24步
挑战成功!用时:0.64s
匹配上了,第26张
ssdwwdssaassdwwasdwaassdwwasddwasd,共34步
挑战成功!用时:0.82s
匹配上了,第3张
sswdsawddsawwdssawwasddsa,共25步
挑战成功!用时:0.58s
匹配上了,第2张
ssdwwdssawwdssawawdsdwaassdwdsawwassdw,共38步
挑战成功!用时:0.73s
匹配上了,第28张
wwdswassawwdsdsawasddww,共23步
挑战成功!用时:0.68s
匹配上了,第28张
sdwwdssawwdswasdsawdwaassddwwaas,共32步
挑战成功!用时:0.84s
匹配上了,第33张
wdsdwassawwdssdwwassawdsa,共25步
挑战成功!用时:1.11s
匹配上了,第4张
sdwwdssawwdssawwassdwawdsdwassdwaasdwawdsa,共42步
挑战成功!用时:0.76s
成功率:1.0

Process finished with exit code 0

芜湖,起飞!

讲点不足

但其实我这个算法并不好,好的算法应该是在交换前就做出准备,利用好这个交换以减少步数,而不是等交换了再继续跑交换,这样一来,交换从工具变成了累赘,这是我能力不足的地方。我之前一直傻傻地不断提高算法地性能,却忘了应该提升算法的本身,这是非常错误的,也算吸取教训吧,下次拿题一定看清规则,理解透了再下笔。


性能分析

  • 大致情况

性能1

  • 内存使用情况

性能2

  • 方法消耗

性能3

性能4


项目部分单元测试代码

测试?我都是这样的。

美图

hhh不开玩笑了

    /**
     * 有无解测试
     */
    @Test
    public void testForIsSolvable(){
        int[][] board1 = new int[][]{{1,3,8}, {2,4,6},{7,5,0}};
        int[][] board2 = new int[][]{{3,1,8}, {2,4,6},{7,5,0}};
        int[][] target = new int[][]{{1,2,3}, {4,5,6},{7,8,0}};
        Game game = new Game();
        Assert.assertTrue(game.isSolvable(board1, target));
        Assert.assertFalse(game.isSolvable(board2, target));
    }

    /**
     * 请求JSON图片 + 切割 + 图片识别 + 模拟游戏
     */
    @Test
    public void playWithPic() throws Exception {
        // 初始化,把图片转成内存中的byte[]
        ImgCompetition.init();
        Assert.assertTrue(Play.play(false));
    }

    /**
     * 100次:请求JSON图片 + 切割 + 图片识别 + 模拟游戏
     */
    @Test
    public void playWithPicBunch() throws Exception {
        int cnt=0;
        int round = 100;
        ImgCompetition.init();
        for (int i = 0; i < round; i++) {
            if(Play.play(false)){
                cnt++;
            }
        }
        Assert.assertTrue(cnt == round);
        System.out.println("成功率:"+(float)cnt/round);
    }
  • 第一个是测试逆序数判断有无解的

  • 第二个是判断跑1次

  • 第三个是判断跑100次


Github的代码签入记录

Github的代码签入记录


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

问题1:通过POST请求,喜提500

如题所示,搞了半天都是500;后来发现是content-type没设置好哎

500图

在几经询问下:

感谢1

感谢2

得知了原因并解决了,实名感谢


问题2:测试中测着测着碰到429

429图片

429是啥玩意儿???查了才知道是Too Many Requests ,我请求过多了

我满脸问号???

问了才知道原来之前竟然有人去dos服务器,然后棚飞就把qps限制在1了😄

哈哈,如果确实真是班上有人,无聊到去攻击服务器,一个软工作业的服务器,真的,我实名diss,很无趣hhh

(这是Tricker和Breaker的行为,不是Hacker)


问题3:步数理解存在偏差

步数

如图,最后解决了


评价俺的队友

  • 值得学习的地方
    • 冲的一手好浪,查阅相关资料能力强
    • 思路清晰,脑回路在线,经常将“当局者迷”的我拉回
    • 想优化方案快
  • 需要改进的地方
    • 或许要更积极一点hhh(也不是他不积极,是因为我自己性子比较急hhhh)

PSP表格

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

学习进度条

第N周 新增代码(行) 累计代码(行) 本周学习耗时(h) 累计学习耗时(h) 重要成长
1 856 856 8 8 fastjson的使用;通过均值哈希做图像识别;BFS
2 46 902 4 12 BFS的进一步深入理解;Postman工具的使用
3 478 1380 6 18 逆序数
4 156 1536 2 20 Java IO的进一步深入理解

最后的最后

我发誓我再也不玩华容道了,嗯😢

最后

posted @ 2020-10-19 00:13  王帅真  阅读(418)  评论(1编辑  收藏  举报