结对编程作业

1.链接与分工

1.1链接与博客

1.2分工合作

AI代码 算法代码 UI设计 原型设计 博客与Github
王梓鹏 80% 70% 15% 10% 35%
周天 20% 30% 85% 90% 65%
分工如表所示,文件等均上传至周天的GitHub

2.原型与代码

2.1-原型设计

2.1.1设计说明:
①进入后展示初始界面,有“排行榜”、“开始游戏”、“打赏”按钮;点击“开始游戏”后进入下一界面——游戏界面;
②游戏界面中有左上角“返回”键——返回初始界面,“计时器”以及“步数”标识,还包括“排行榜”、“助力一步”——AI自动走一步、“重新来过”——重开一局的大按钮,以及“Tips”和“打赏”小按钮。
③查看“排行榜”、“打赏”均会跳转到相应界面,点击周围区域或者“返回键”则返回上一级界面。
④打开“Tips”会跳转到第三方链接。
⑤至于为什么开始界面图用的是“三国华容道”,个人是觉得华容道的思想还是某种意义上的解题,况且对于中国来说三国华容道大家都不陌生,恰好设计想到的是古时的一些用法,比如牌匾的配色等,于是开始界面就是下图所示了。

下图顺序为 : 初始界面 ——> 游戏界面 ——> 排行榜 ——> 打赏 (图片由XD导出)

2.1.2原型工具
本次首先使用的Adobe XD,但是因为导出html文件时无法达到多界面交互,于是后来又转向Axure Rp进行修改。

2.1.3结对过程
主要还是网上交流以及线下跑宿舍,离鹏少宿舍近,这波就已经赚了。

2.1.4困难及解决方法

  • 困难主要是想不到ui应该如何设计

  • 解决方法就是:数字华容道 ——> 华容道 ——> 三国华容道 ——> 古代;同时想到界面整体类似游戏机,就也搜了一下游戏机的界面

  • 问题解决了

  • 收获:不会就多查多看

  • 一开始考虑到这次原型图的部分,并没有很多的内容,于是我没有想到墨刀和axure,直接使用的XD进行画图,毕竟XD里面也有设计原型操作的效果而且操作方便。但是后面要求导出html故又转向了使用html。

  • 原型的部分基本上已经完成,但是预计是没法达到完全实现的目的(毕竟我太垮了)

2.2-AI与原型设计实现

2.2.1代码实现思路

  • 网络接口使用
    查找相关资料并且请教了同学后使用了pyhon的requests库
'''
请求题目
'''
import requests
import base64
trackId='031802342'
url = "http://47.102.118.1:8089/api/challenge/start/xxxxxxxxxxxxx"
body = {
    "teamid":xx,
    "token":"xxxxx"
}
headers = {'content-type':"application/json"}
response = requests.request("post",url,headers=headers,json=body)
use_dic=response.json()
data=use_dic['data']
img=data['img']
stepre=data['step']
uuid=use_dic['uuid']
swap=data['swap']
image_data = base64.b64decode(img)  # 解码图片
'''
提交答案
'''
headers = {'content-type': "application/json"}
    anserurl = "http://47.102.118.1:8089/api/challenge/submit"
    answer = {

        "uuid": uuid,
        "teamid": xx,
        "token": "xxxxx",
        "answer": {
            "operations": arr,
            "swap": swap
        }
    }
    response = requests.request("post", anserurl, headers=headers, json=answer)

  • 代码组织与内部实现设计,接下来是所使用到的关键算法
'''
将一张图片切为9张图
'''
from PIL import Image
import sys


def Cut_image(image):
    width, height = image.size
    item_width = int(width / 3)
    box_list = []
    for i in range(0, 3):
        for j in range(0, 3):
            box = (j * item_width, i * item_width, (j + 1) * item_width,
                   (i + 1) * item_width)  # Image.crop(left, up, right, below)
            box_list.append(box)

    image_list = [image.crop(box) for box in box_list]
    return image_list


# 保存
def save_images(folder, image_list):
    index = 1
    for image in image_list:
        image.save(folder + str(index) + '.jpg')      #folder为存放的文件夹
        index += 1

为了复原请求来的打乱的题目,首先要找到对应的原图,一开始的思路是直接对比大图的相似度,考虑到后面AI复原时需要以列表形式存放图片九宫格的对应位置,就将请求来的打乱的题目以及原图进行分割保存。


接着就是对比图片的相似度,鉴于所给的图片只有黑白,所以采用了灰度直方图算法

import os
import cv2
def calculate(image1, image2):
    # 灰度直方图算法
    # 计算单通道的直方图的相似值
    hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
    hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
    # 计算直方图的重合度
    degree = 0
    for i in range(len(hist1)):
        if hist1[i] != hist2[i]:
            degree = degree + \
                     (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
        else:
            degree = degree + 1
    degree = degree / len(hist1)
    return degree


def runAllImageSimilaryFun(para1, para2):
    # 通过imread方法直接读取物理路径
    img1 = cv2.imread(para1)
    img2 = cv2.imread(para2)

    sim = calculate(img1, img2)
    return sim

然后先匹配大图找出相似度最高的,在利用小图去一一匹配对应,并标上相应的位置

from similarity import *


def Judge(arr, record,position,target):
    for i in range(0, 10):
        record.append(0)
    for i in range(1, 10):
        image1 = 'cutmodify/' + str(i) + '.jpg'
        flag = 0  # 是否匹配到图(找白色图片)
        for j in range(1, 10):
            image2 = 'cutorigin/' + str(j) + '.jpg'
            calc_sim = runAllImageSimilaryFun(image1, image2)  # 计算相似度
            if calc_sim == 1:
                flag = 1
                arr.append(j)
                record[j-1] = 1
                break
        if flag == 0:
            arr.append(0)
    # 目标状态
    target = []
    for i in range(9):
        if (record[i] != 0):
            target.append(i + 1)
        else:
            target.append(0)
    return arr, record, position, target

AI还原部分主要用到了A*启发式搜索

class EightPuzzle:

    def __init__(self):
        # 搜索值
        self._hval = 0
        # 当前实例的搜索深度
        self._depth = 0
        # 搜索路径中的父节点
        self._parent = None
        self.adj_matrix = []
        for i in range(3):
            self.adj_matrix.append(_goal_state[i][:])

    def __eq__(self, other):
        if self.__class__ != other.__class__:
            return False
        else:
            return self.adj_matrix == other.adj_matrix

    def __str__(self):
        res = ''
        for row in range(3):
            res += ' '.join(map(str, self.adj_matrix[row]))
            res += '\r\n'
        return res

    def _clone(self):
        p = EightPuzzle()
        for i in range(3):
            p.adj_matrix[i] = self.adj_matrix[i][:]
        return p

    def _get_legal_moves(self):  # 返回可以交换可用空间的元组列表
        # 获取空块的行和列
        row, col = self.find(0)
        free = []

        # 找出哪些位置可以移动到哪里
        if row > 0:
            free.append((row - 1, col))
        if col > 0:
            free.append((row, col - 1))
        if row < 2:
            free.append((row + 1, col))
        if col < 2:
            free.append((row, col + 1))
        return free
    def _generate_moves(self):
        free = self._get_legal_moves()
        zero = self.find(0)
        def swap_and_clone(a, b):
            p = self._clone()
            p.swap(a, b)
            p._depth = self._depth + 1
            p._parent = self
            return p

        return map(lambda pair: swap_and_clone(zero, pair), free)

    def _generate_solution_path(self, path):
        if self._parent == None:
            return path
        else:
            path.append(self)
            return self._parent._generate_solution_path(path)

    def solve(self, h):  # 执行A*搜索目标状态
        def is_solved(puzzle):
            return puzzle.adj_matrix == _goal_state

        openl = [self]
        closedl = []
        move_count = 0
        while len(openl) > 0:
            x = openl.pop(0)
            move_count += 1
            if (is_solved(x)):
                if len(closedl) > 0:
                    return x._generate_solution_path([]), move_count
                else:
                    return [x]

            succ = x._generate_moves()
            idx_open = idx_closed = -1
            for move in succ:
                # 是否遍历过该节点
                idx_open = index(move, openl)
                idx_closed = index(move, closedl)
                hval = h(move)
                fval = hval + move._depth

                if idx_closed == -1 and idx_open == -1:
                    move._hval = hval
                    openl.append(move)
                elif idx_open > -1:
                    copy = openl[idx_open]
                    if fval < copy._hval + copy._depth:
                        # 将move的值复制到现有的
                        copy._hval = hval
                        copy._parent = move._parent
                        copy._depth = move._depth
                elif idx_closed > -1:
                    copy = closedl[idx_closed]
                    if fval < copy._hval + copy._depth:
                        move._hval = hval
                        closedl.remove(copy)
                        openl.append(move)

            closedl.append(x)
            openl = sorted(openl, key=lambda p: p._hval + p._depth)
        # 如果未找到完成状态,则返回失败
        return [], 0

    
def heur(puzzle, item_total_calc, total_calc):
    t = 0
    for row in range(3):
        for col in range(3):
            val = puzzle.peek(row, col) - 1
            target_col = val % 3
            target_row = val / 3

            # account for 0 as blank
            if target_row < 0:
                target_row = 2

            t += item_total_calc(row, target_row, col, target_col)

    return total_calc(t)


# 用来低估的启发式方法
def h_manhattan(puzzle):
    return heur(puzzle,
                lambda r, tr, c, tc: abs(tr - r) + abs(tc - c),
                lambda t: t)

以下是性能的部分

可以看到多个函数都是类似的耗时时间,并没有一个非常突出的

接下来是judge函数,把已经切割过的两张图,9张一一对比,找到相似度为1的块,并标上对应的位置。


2.2.2贴出Github的代码签入记录,合理记录commit信息

2.2.3遇到的问题以及解决
此处遇到了的问题是由于采用的是灰度直方图计算相似度,所以黑色白色的图片相似度显示为1.0。
但是通过增加计算结果的小数位,可以判断出是否为对应图

2.2.4评价队友

  • 值得学习的地方
    很能肝,很认真,很乐意去学
  • 需要改进的地方
    调整一下作息,多记录、多用用草稿纸(毕竟一心不能多用)

3.算法和接口

3.1A*算法解释

A*启发式搜索:把起点加入 open list,重复如下过程:a.遍历openlist,查找 F 值最小的节点,把它作为当前要处理的节点。b.把这个节点移到 close list 。c.对当前方格的 8 个相邻方格的每一个方格?

  • 如果它是不可抵达的或者它在 close list 中,忽略它。否则,做如下操作。
  • 如果它不在 open list 中,把它加入openlist,并且把当前方格设置为它的父亲,记录该方格的 F,G和H值。
  • 如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和F值。如果 openlist是按 F值排序的话,改变后可能需要重新排序。
    d.停止,当把终点加入到了 open list 中,此时路径已经找到了,或者查找终点失败,并且 openlist 是空的,此时没有路径。
    最后保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。

3.2Json接口部分

  • 我们是在postman官网下载的postman,然后按照输入url等等进行GETPOST
  • 这里是postman下载链接,接下来是postman对应的使用获取排行榜截图
  • 其他的像POST上传题目/答案则类似于下图,改好POST/GETURL后,就要在body——>raw——>json里面加入代码,这里是具体json代码部分

4.PSP表格和学习进度条

4.1学习进度条

第N周 新增代码(行) 累计代码(行) 累计学习耗时(小时) 重要成长
第1周 200 200 2
第2周 0 200 4 初步看了看a*算法和华容道解法
第3周 300 500 5 了解了a*算法
第4周 300 800 16 学到了鹏少教的技术

4.2PSP表格

PSP2.1 Personal Software Process Stages 预估耗时 实际耗时
Plannning 计划
· Estimate · 估计这个任务需要多少时间 30 50
· Development 开发
· Analysis · 需求分析 (包括学习新技术) 200 180
· Design Spec · 生成设计文档 40 100
· Design Review · 设计复审 60 50
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 60 150
· Design · 具体设计 180 200
· Coding · 具体编码 360 400
· Code Review · 代码复审 120 120
· Test · 测试(自我测试,修改代码,提交修改) 120 120
Reporting 报告
· Test Repor · 测试报告 60 60
· Size Measurement · 计算工作量 30 30
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 20 50
合计 1250 1460
posted @ 2020-10-19 21:40  skyjay  阅读(99)  评论(0编辑  收藏  举报