结对编程作业
作业相关
- 王祺👈这是俺
- 傅兴佳👈这是俺队友
- huarong-road👈这是俺的Github项目地址(俺负责的AI部分)
- Klotski👈这是俺队友Github项目地址(俺队友负责的小游戏部分)
- 具体分工(表格)
王祺 | 傅兴佳 |
---|---|
AI的设计 | 原型设计 |
AI的实现 | 原型设计的实现(微信小程序) |
其实还是分的挺开的,不过关于图像处理和游戏求解的算法部分的思路我们会一起讨论
原型设计
工具
本次使用Axure RP进行小程序的原型设计,成品请在微信小程序内搜索:Klotski 无广告
设计说明
原先考虑简约多彩的配色方案,然而马上发现自己的配色实在不堪入目,于是选择了简约黑白灰的配色方案。
小程序分为3个逻辑页面:首页、游戏页面、排行页面。首页为一级页面,游戏页面、排行页面为二级页面
首页
点击按钮——开始游戏,进入游戏界面
点击按钮——难度,选择难度系数
点击按钮——Top榜,进入排行页面
点击图像——声音,开关游戏音效
点击图像——分享,将小程序分享给微信好友
游戏页面
点击按钮——开始,开始游戏,打乱数字,页面自动更新时间、步数
点击按钮——重置,一切回到未开始状态
游戏结束,点击确定按钮,回到未开始状态
排行页面
记录每个系数难度所花最短时间
结对的过程
为啥会组在一起呢?我想想大概有以下几点:
- 老乡原则:都是物信大二转过来的难兄难弟
- 就近原则:他当时组队时候就在我旁边
- 聚类原则:技术栈相近,比较好交流
遇到的困难及解决方法
- 困难描述:第一次画原型,还准备自己动手画状态栏之类的,不好画。
- 解决尝试:之后上网查了才发现元件库这个好东西,直接复用,大大提高了效率
- 收获:动手前多吸取前人经验,事半功倍
原型设计实现
空白方块的移动算法
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>
使用教程👈戳这里
Java对象与json串之间的转换问题解决了,那json串怎么发出去呢?又怎么接收回来呢?HTTP的请求——GET
和POST
!
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对象)
- game包的(游戏模拟)
- 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
芜湖,起飞!
讲点不足
但其实我这个算法并不好,好的算法应该是在交换前就做出准备,利用好这个交换以减少步数,而不是等交换了再继续跑交换,这样一来,交换从工具变成了累赘,这是我能力不足的地方。我之前一直傻傻地不断提高算法地性能,却忘了应该提升算法的本身,这是非常错误的,也算吸取教训吧,下次拿题一定看清规则,理解透了再下笔。
性能分析
- 大致情况
- 内存使用情况
- 方法消耗
项目部分单元测试代码
测试?我都是这样的。
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的代码签入记录
遇到的代码模块异常 or 结对困难及解决方法
问题1:通过POST请求,喜提500
如题所示,搞了半天都是500;后来发现是content-type没设置好哎
在几经询问下:
得知了原因并解决了,实名感谢
问题2:测试中测着测着碰到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的进一步深入理解 |
最后的最后
我发誓我再也不玩华容道了,嗯😢