• 结对编程作业


    github项目地址

    公共的github仓库地址

    博客链接

    我的博客链接
    队友的博客链接
    1.1.2分工合作

    AI代码 算法代码 UI设计 原型设计 博客编写
    王梓鹏 80% 70% 15% 10% 35%
    周天 20% 30% 85% 90% 65%

    本次结对编程作业显然还是有点难度的,感觉也是为后面的大作业做一个提前的准备
    同学之间交流和学习再完成一些除了最基本代码部分之外的内容

    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 += '
    '
            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评价队友

    • 值得学习的地方
      会ui,很有想法,每天都会找我学习,每天最期待的时间就是我天哥到点了敲我宿舍门,是学习的味道。
    • 需要改进的地方
      代码及算法的能力需要提高一下。

    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代码部分

    PSP表格

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

    学习进度条

    第N周 新增代码(行) 累计代码(行) 累计学习耗时(小时) 重要成长
    第1周 102 102 15 切割图片以及图像相似度计算
    第2周 64 166 17 requests、接口的使用
    第3周 283 449 20.5 通过图片转化为文本以及A*算法
    第4周 30 479 17.5 封装各个模块以及改进性能
  • 相关阅读:
    大二下-个人课堂总结
    第十六周总结
    第十五周总结
    计算最长英语单词链
    第十四周总结
    大道至简阅读笔记03
    信息反馈—冲刺20
    sudo安装某一文件报错:E: 无法获得锁 /var/lib/dpkg/lock
    linux脚本文件执行的方法之间的区别
    opencv的安装及填坑
  • 原文地址:https://www.cnblogs.com/xiaobingganya/p/13842732.html
Copyright © 2020-2023  润新知