结对编程作业

1.作业信息

1.1博客链接

我的链接:点击这里
队友链接:点击这里

1.2 Github地址

我的地址:点击这里(内含原型设计工程文件,游戏实现代码)
队友地址:点击这里(内含AI大比拼包括算法在内的代码)

1.3 具体分工

任务 队友
原型设计
AI算法设计
游戏代码
接口与测试

2.1原型设计

2.1.1设计说明

既然要设计一个游戏,最基本的肯定要有开始,退出和得分(步数),为了方便还原,将图片的原来样子也展示出来

玩家可以通过查看栏菜单查看游戏规则和排行榜

游戏里添加了强制交换功能,但是因为键盘不方便实现自定义调换,可能会出现无解的情况,于是就注释掉了

因为我们的游戏界面设置的很小,所以如果要添加这么多按钮,就会显得拥挤,所以menu组件就派上了用场

2.1.2原型开发工具:Axure Rp 8

2.1.3结对过程:舍友两两组合

2.1.4困难方法

  • 困难描述
  • 之前从来没有接触过原型设计,对概念对操作都是一头雾水
  • 解决尝试
  • 询问往届的学长学姐,在B站上看教学视频
  • 收获与遗憾
  • 虽然刚开始都不懂,但是根据视频教学很快就能上手,基本的操作及交互动作试两下就能熟悉,而且也激发了我们对原型设计的兴趣,以后找机会会自己练一练的。然后有点尴尬的是这次作业的原型设计还是十分简陋,因为原型设计是临时学的怕在代码中实现不了。。。

    2.2 AI与原型设计实现

    2.2.1代码实现思路:

    网络接口的使用

    用requests和BeautifulSoup处理,获取某题的Uuid,返回图片的src链接,challengeidd,强制交换的步数和强制交换的操作

    getProblem.py

    import json
    import requests
    import base64
    url ="http://47.102.118.1:8089/api/challenge/start/e9d5727c-57fa-4182-a1fd-24b43fd392ce"
    data = {
        "teamid": 53,
        "token": "99a01c39-f3ee-4967-8b5e-35cfbcfb9f7a"
    }
    r = requests.post(url,json=data)
    r.raise_for_status()
    r.encoding = r.apparent_encoding
    dic=json.loads(r.text)
    img = base64.b64decode(dic["data"]["img"])
    Step=dic["data"]["step"]
    Swap=dic["data"]["swap"]
    Uuid=dic['uuid']
    with open("./photo.jpg", "wb") as fp:
        fp.write(img)  # 900*900

    将获取返回的Uuid和算法解出来的operations和swaplist上传

    post.py

    import json
    import requests
    import getproblem as gp
    import qipan as Qi
    url ="http://47.102.118.1:8089/api/challenge/submit"
    data = {
        "uuid": gp.Uuid,
        "teamid": 53,
        "token": "99a01c39-f3ee-4967-8b5e-35cfbcfb9f7a",
        "answer": {
            "operations": Qi.operations,
            "swap": Qi.swaplist
        }
    }
    r = requests.post(url,json=data)
    print(r.text)

    代码组织与内部实现设计

    本部分有getProblem.py,cutImage.py,find_image.py,post.py为工具类函数,用于获取题目,题目图片预处理等,还有generator.py和cutImage.py来生成用于预测代价和题目切图保存到本地文件夹.

    Prediction.py,qipan.py两个用于AI求解的函数,Prediction.py通过generator.py生成的quary表中字典代价去预测当前状态下代价最小的下一步走向,qipan.py中实现循环和强制交换,与find_image.py中其他功能性函数链接,包括判断是否无解,无解后自行交换的方法实现

    算法关键

    识别出挖空的数字,导入对应的q_tab表,每次通过棋盘当前的状态,生成0123的随机序列,0123代表上左下右,对于四个方向的操作,计算出四个方向的代价,之后返回代价列表中最小的代价对应的方向操作

    find_image.py 其中的将图片对应关系变成二维数组

    def make_qipan(m):
        sign=[1,2,3,4,5,6,7,8,9]#存储被挖空的图的位置
        blanknumber=[1,2,3,4,5,6,7,8,9]#存储被挖空的图是第几张图
        temp=[]#存储棋盘的顺序
        qipan=np.arange(1,10).reshape(3,3)#创建2维数组
        for pic2 in os.listdir('./QuestionCut'):
            for pic1 in os.listdir('./picture/' +m):
                with open ('./picture/'+m+'/'+pic1,'rb') as f1:
                    base64_f1=base64.b64encode(f1.read())
                with open ('./QuestionCut/'+pic2,'rb') as f2:
                    base64_f2=base64.b64encode(f2.read())
                if base64_f1==base64_f2:#找到切图和数据库中的对应关系
                    number=int(pic2.split('.')[0])
                    col=(number-1)%3#记录每一个问题切图的所在列
                    row=int((number-1)/3)#记录每一个问题切图的所在行
                    qipan[row][col]=pic1.split('.')[0]#对应棋盘的位置为这个图所对应数据库的图的数字
                    temp.append(int(pic1.split('.')[0]))#按顺序存到temp
                    sign.remove(number)#在存储被挖空的图的位置列表去除对应
                    blanknumber.remove(int(pic1.split('.')[0]))#在存储被挖空的图的数字列表去除对应
        qipan[int((sign[0]-1)/3)][(sign[0]-1)%3]=blanknumber[0]#令二维数组被挖空的图的位置值为0
        return temp,qipan,sign[0],blanknumber[0]#返回二维数组的一维形式temp,二维数组qipan,sign[0]为被挖空的数字的题目位置序号,blanknumber[0]为被挖空的数字

    判断逆序对的方法judgment,逆序数奇偶性的判断,需要除了空格以外的八个数的有序数列计算

    def judgment(temp):
        signal=0
        for i in range(len(temp)):
            for j in range(i+1,len(temp)):
                #计算逆序对
                if temp[i]>temp[j]:
                    signal+=1
                    #保存第一个有逆序对的数字的位置
                    if signal==1:
                        position=i
        if signal%2!=0:#逆序对为奇数则无解
            print('no way')
            return signal,position

    将二维数组转换为有序的一维数组,和judgment对接

    def turnToarray(qipan,bk):
        t=[]
        for i in range(3):
            for j in range(3):
                if qipan[i][j]!=bk:
                    t.append(qipan[i][j])
        return t
    

    性能分析

    由于强制交换的存在,代价预估预测都对当前状态进行代价分析,不会考虑利用强制交换获取捷径,所以这强制交换之前的操作可能使得交换后更加复杂或者更加简单。

    性能分析图

    项目展示测试单元

    测试接口获取题目gettest.py

    import base64
    import json
    import requests
    from PIL import Image
    def gethtml(url):
        try:
            resp = requests.request('get', url)
            resp.raise_for_status()
            resp.encoding = resp.apparent_encoding
            return resp.text
        except:
            print('err')
    def getProblem():
        url = "http://47.102.118.1:8089/api/problem?stuid=031804140"
        # 每次请求的结果都不一样,动态变化
        text = json.loads(gethtml(url))
        img_base64 = text["img"]
        step = text["step"]
        swap = text["swap"]
        uuid = text["uuid"]
        img = base64.b64decode(img_base64)
        # 获取接口的图片并写入本地
        with open("question.jpg", "wb") as fp:
            fp.write(img)  # 900*900
        return step,swap
    Step,Swap=getProblem()
    print(Swap)
    print(Step)

    测试接口提交答案posttest.py

    import json
    import requests
    import getproblem as gp
    import qipan as Qi
    url ="http://47.102.118.1:8089/api/answer"
    data = {
        "uuid": gp.Uuid,
        "answer": {
            "operations": Qi.operations,
            "swap": Qi.swaplist
        }
    }
    r = requests.post(url,json=data)
    print(r.text)

    2.2.2 GitHub签入记录

    2.2.3 代码模块异常

    问题描述

    强制交换后如果无解,在自行交换过后会出现两个0,即两个数字为挖空的数字,从而导致永远无解;也有交换过后没有改变逆序数的情况也是永远无解

    尝试解决

    通过对出现问题的题目二维数组进行人工复现代码,找出自行交换的下标处理需要考虑当前空格和交换格子的相对位置

    是否解决

    def swap(qipan,p,bkp):
        if bkp<=p: #空格在第一个逆序数的左边
            t = qipan[(p+1) // 3][(p+1) % 3]
            qipan[(p+1) // 3][(p+1) % 3] = qipan[(p + 2) // 3][(p + 2) % 3]
            qipan[(p + 2) // 3][(p + 2) % 3] = t
            print('swap[%d , %d]' % (p+1, p + 2))
            return p+1, p + 2
        else:
            if bkp==p+1:
                t=qipan[p//3][p%3]
                qipan[p//3][p%3]=qipan[(p+2)//3][(p+2)%3]
                qipan[(p + 2) // 3][(p + 2) % 3]=t
                print('swap[%d , %d]' % (p, p + 2))
                return p, p + 2
            else:
                t=qipan[p//3][p%3]
                qipan[p // 3][p % 3] = qipan[(p + 1) // 3][(p + 1) % 3]
                qipan[(p + 1) // 3][(p + 1) % 3] = t
                print('swap[%d , %d]' % (p, p + 1))
                return p, p + 1

    有何收获

    能解出答案了www,当时写的太过草率,重新理解以后感觉蛮好的

    2.2.4 评价队友

  • 值得学习的地方:
  • 烜哥的能力确实强,对于新知识的掌握很快,能很快明白代码需求与问题所在,从而进行改进,效率很高
  • 需要改进的地方:
  • 真没啥需要改进,全程烜哥带飞

    2.2.5 PSP表格/学习进度条

    PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
    Planning 计划 60 100
    Estimate 估计这个任务需要多少时间 60 100
    Development 开发 1120 1940
    Analysis 需求分析 (包括学习新技术) 400 720
    Design Spec 生成设计文档 50 80
    Design Review 设计复审 20 30
    Coding Standard 代码规范 (为目前的开发制定合适的规范) 50 50
    Design 具体设计 80 100
    Coding 具体编码 360 600
    Code Review 代码复审 120 120
    Test 测试(自我测试,修改代码,提交修改) 150 240
    Reporting 报告 110 110
    Test Repor 测试报告 60 40
    Size Measurement 计算工作量 30 40
    Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 20 30
    合计 1290 2240
    第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
    1 110 110 6 6 学会Axure的基本操作,从测试接口获取答案和上传答案
    2 340 450 16 22 掌握tkinter的基本架构,掌握广搜和循环迭代的原理
    3 350 800 10 32 游戏功能实现,内嵌AI算法,数据结构剖析,实现判断交换数据格式转换等
    posted @ 2020-10-19 22:01  K小虾米  阅读(116)  评论(0编辑  收藏  举报