zoukankan      html  css  js  c++  java
  • 结对编程作业


    写在前面
    1.我的博客地址
    2.队友的博客地址
    3.github项目地址
    4.具体分工

    姓名分工 AI算法 原型设计 游戏实现 博客
        白霖    √       √
        熊崟     √   √
    一.原型设计
    1.设计说明
    通过HTML5,JavaScript,CSS的编写创建本地网页,通过网页的形式展现游戏内容和相关功能:
    1)可以查看原图,或者不看(记忆大佬)。
    2)在游戏过程中可以重新开始。
    3)记录时间和步数。
    4)根据时间和步数分别查看排名。
    5)自动演示走法。

    2.原型模型设计(Axure Rp)

    原型模型的构建应用的软件为Axure Rp 8
    主页面(实际游戏页面)

    排行榜:按照步数或者时间进行编排成功游戏者和对应的步数和时间
    开始游戏:获取分割打乱后的图片,同时启动时间计时器(游戏进行时为“重新开始”,成功时为“再来一次”,都可以开始新一轮的游戏)
    提示:获取并演示解过程
    显示图片:可在左边显示原图,以供游戏参考(再点一次则隐藏)

    3.结对交流过程

    4.困难与解决办法
    在原型实现的过程中,运用动态面板进行操作和记录困难,多次出错。通过多次的查询和实例理解最终实现。

    二、AI与原型设计实现
    1.AI的原型实现
    1)代码实现思路
    网络接口的使用:
    使用python的requests库来进行get和post请求

    def getData():
        url = "http://47.102.118.1:8089/api/problem?stuid=031804101"
        r = requests.get(url)
        data = json.loads(r.text)
        imgdata = base64.b64decode(data['img'])
        step = data['step']
        swap = data['swap']
        uuid = data['uuid']
        # 将图片写到本地
        path = "C:\software\char.jpg"
        file = open(path,'wb')
        file.write(imgdata)
        file.close() 
        return step,swap,uuid
    
    def postData(uuid,ope,swap):
        url = "http://47.102.118.1:8089/api/answer"
        data = {
            "uuid":uuid,
            "answer":{
                "operations": ope,
                "swap": swap
            }
        }
        r = requests.post(url,json=data)
        return r.text
    

    代码组织与内部实现设计(类图)
    算法流程图


    我认为算法的关键是图片识别,如果图片都识别不了,后面就不用做了
    采用KNN算法实现

    # 训练模型,我练我自己?
    import os
    import numpy as np
    from PIL import Image
    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.externals import joblib
    
    # subimg中每个文件夹都包含9张切割完的子图
    path = 'C:\software\subimg\'
    dirs = os.listdir(path)
    length = len(dirs)
    
    X_train=[]
    y_train=[]
    for j in range(length):
        dirpath = path + dirs[j] + "\"
        names = os.listdir(dirpath)
        for i in range(len(names)):
            f = Image.open(dirpath+names[i],"r")
            # 判断是不是纯黑,纯黑就跳过
            extrema = f.convert("L").getextrema()
            if extrema == (0, 0):
                continue
            
            # 图片有三个通道,通常是转为灰度图,在这我取其中一个通道
            image = np.array(f)
            image = image[:,:,0]
            # 转换为2维的numpy数组
            image = image.reshape(1,-1)
            # 有监督学习,还需要y标签
            X_train.append(image)
            y_train.append(j*10+i)
    
    X_train = np.array(X_train)
    y_train = np.array(y_train)
    # 还需要再将X_train转为2维
    X_train = X_train.reshape(X_train.shape[0],-1)
    
    # 预测的时候找和它最像的就行了
    clf = KNeighborsClassifier(1)
    clf.fit(X_train,y_train)
    
    #保存模型,需要时直接加载使用
    joblib.dump(clf, 'C:\software\clf.pkl')
    
    # 识别图片,返回相应的序列及白块位置
    # 将获取的图片转为np数组,并添加到列表
    def imgToArr():
        arrlist = []
        f = Image.open('C:\software\char.jpg',"r")
        array = np.array(f)
        for i in range(3):
            for j in range(3):
                subarray = array[i*300:(i+1)*300,j*300:(j+1)*300]
                arrlist.append(subarray)
        return arrlist
    
    def getNum():
        arrs = imgToArr()
        # load model
        clf = joblib.load('C:\software\clf.pkl')
        ans = []
        dirindex = 0
    
        # 对arrs中的每个np数组进行预测
        for arr in arrs:
            # s1==0 白  s2==0 黑
            s1 = np.count_nonzero(arr==0)
            s2 = np.count_nonzero(arr==255)
            if s1==0:
                ans.append("白")
            elif s2==0:
                ans.append("黑")
            else:
                arr = arr[:,:,0]
                arr = arr.reshape(1,-1)
                pre = int(clf.predict(arr))
                dirindex=pre//10
                ans.append(pre)
    
        path = 'C:\software\subimg\'
        dirs = os.listdir(path)
        # 白块的位置
        white = ans.index("白")
    
        # matchNums是已经确定的子图,noMatchNums还没确定的子图,有一白(白块恰好就是黑色的)和一黑一白两种情况
        matchNums = [i%10 for i in ans if isinstance(i,int)]
        noMatchNums = [i for i in range(9) if i not in matchNums]
    
        # 对还未确定的子图进行判断
        if "黑" not in ans:
            ans[ans.index("白")] = noMatchNums[0]
        else:
            img1path = path + dirs[dirindex] + "\"
            files = os.listdir(img1path)
            img1path += files[noMatchNums[0]]
            arr = np.array(Image.open(img1path,"r"))
            if np.count_nonzero(arr==255) == 0:
                ans[ans.index("黑")] = noMatchNums[0]
                ans[ans.index("白")] = noMatchNums[1]
            else:
                ans[ans.index("黑")] = noMatchNums[1]
                ans[ans.index("白")] = noMatchNums[0]
        # ans是获取的图片,对应原图的序列(0-8)
        ans = [i%10 for i in ans]
        s = ""
        for l in ans:
            s+=str(l)
        return s,str(ans[white])
    

    由序列,白块位置,step,swap得到结果,最开始使用遍历,遇到step=15+的20分钟还出不来
    于是就对原来的方法稍微优化一下,不过只能得到大部分最优解
    改进思路

    # 交换字符串两个位置
    def change(s,i,j):
        if i==j:
            return s
        a = min(i,j)
        b = max(i,j)
        return s[:a]+s[b]+s[a+1:b]+s[a]+s[b+1:]
    
    def getMethod(nums,white,step,swap):
        '''
        1.将部分数据写在文件里
        white是指白块对应的数字
        flag中的white.txt是判断当前序列在不在文件中
        在的话有解,获取索引,去ans中的white.txt获得序列;否则无解
        '''
        ww = open("C:\software\flag\"+white+".txt")
        qq = ww.read()
        pflag = qq.split("
    ")
        ww.close()
    
        ww = open("C:\software\ans\"+white+".txt")
        qq = ww.read()
        pans = qq.split("
    ")
        ww.close()
    
        # 记录要交换的序列,交换的位置,以及最少步数
        post_swap = None
        post_ope = None
        minsteps = 100
    
        queue = []
        flag = {}
        # 2.对强制交换后的序列也进行判重
        flag2 = {}
        flag3 = {}
        lis = [[i,j] for i in range(9) for j in range(9) if i<j]
        flag[nums]=1
        queue.append(nums+"0 ")
        while queue:
            p = queue[0]
            queue.remove(p)
            space = p.index(" ")
            # 步数
            step2 = int(p[9:space])
            # 空格+序列
            me = p[space:]
            # 字符串
            p=p[:9]
            if p=="012345678":
                if step2<=step:
                    post_swap = []
                    post_ope = me[1:]
                break
            if step2 == step:
                s = change(p,swap[0]-1,swap[1]-1)
                if flag2.__contains__(s):
                    continue
                else:
                    flag2[s] = 1
                if s in pflag:
                    ins = pflag.index(s)
                    method = pans[ins]
                    if len(me[1:]+method)<minsteps:
                        post_swap = []
                        post_ope = me[1:]+method
                        minsteps = len(post_ope)
                        # print("[]",post_ope,str(minsteps))
                else:
                    for li in lis:
                        s2 = change(s,li[0],li[1])
                        if flag3.__contains__(s2):
                            continue
                        else:
                            flag3[s2] = 1
                        if s2 in pflag:
                            ins = pflag.index(s2)
                            method = pans[ins]
                            if len(me[1:]+method)<minsteps:
                                post_swap = [li[0]+1,li[1]+1]
                                post_ope = me[1:]+method
                                minsteps = len(post_ope)
                                # print(post_swap,post_ope,str(minsteps))  
                # 3.当前最短步数已经等于其它大佬的最短步数
                #if minsteps == 20:
                #    break
            if step2<step:
                pos = p.index(white)
                if(pos>=3):# 上
                    s = change(p,pos-3,pos)
                    if(not flag.__contains__(s)):
                        flag[s]=1
                        queue.append(s+str(step2+1)+me+"w")
                if(pos<=5):# 下
                    s = change(p,pos,pos+3)
                    if(not flag.__contains__(s)):
                        flag[s]=1
                        queue.append(s+str(step2+1)+me+"s")
                if(pos%3!=0):# 左
                    s = change(p,pos-1,pos)
                    if(not flag.__contains__(s)):
                        flag[s]=1
                        queue.append(s+str(step2+1)+me+"a")
                if(pos%3!=2):# 右
                    s = change(p,pos,pos+1)
                    if(not flag.__contains__(s)):
                        flag[s]=1
                        queue.append(s+str(step2+1)+me+"d")
        return post_ope,post_swap
    

    性能分析与改进

    因为涉及到图片的读写操作,以及文件的读取,所以io操作占了较大的比例

    单元测试

    from AI import getMethod
    import unittest
    from BeautifulReport import BeautifulReport as br
    
    class Test(unittest.TestCase):
        # 测试3组:强制交换完正好还原,一开始就是还原后的序列,正常情况
        def test1(self):
            nums = "017345628"
            white = "2"
            step = 2
            swap = [3,8]
            print(getMethod(nums,white,step,swap))
        
        def test2(self):
            nums = "012345678"
            white = "2"
            step = 2
            swap = [3,8]
            print(getMethod(nums,white,step,swap))
        
        def test3(self):
            nums = "876543210"
            white = "2"
            step = 5
            swap = [3,4]
            print(getMethod(nums,white,step,swap))
    
    if __name__ == "__main__":
        ts = unittest.TestSuite()
        test = [Test('test1'),Test('test2'),Test('test3'),]
        ts.addTests(test)
        br(suite).report('result.html','report','.')
    

    2.游戏实现
    游戏分为前端和后端
    前端采用了html、js、bootstrap、jquery、ajax等技术,后端用python flask库搭建了个本地服务器
    1)生成几百种有解的序列,写入到文件
    2)游戏页面点击开始游戏/重新开始,会通过ajax的get方法向后端获取数据,包括原图(随机),由原图生成的子图,白块,位置排列,白块位置,
    前端根据获得的数据进行排列数据
    3)交换相邻的块,js实现.点击白块周边的块,则两个img标签的src、value属性互换,标记白块位置的变量pos变为点击的块的id

    function turn(btn) {
    	white_btn = document.getElementById(pos);
    	btn_id = btn.id;
    	if (pos == btn_id) return;
    	if ((btn_id == pos - 3) || (btn_id == parseInt(pos) + parseInt(3)) || (btn_id == pos - 1 && pos != 3 && pos != 6) ||
    		(btn_id == parseInt(pos) + parseInt(1) && pos != 2 && pos != 5)) {
                    //两块交换,就交换他们的图片路径和img标签的value值,
    		src = white_btn.src;
    		white_btn.src = btn.src;
    		btn.src = src;
    
    		var tmp = $("#" + pos).attr("value");
    		$("#" + pos).attr("value", $("#" + btn_id).attr("value"));
    		$("#" + btn_id).attr("value", tmp);
                    //改变步数
    		pos = btn_id;
    		k = document.getElementById("cnt").innerHTML;
    		document.getElementById("cnt").innerHTML = parseInt(k) + 1;
    
    		// 判断成功
    		var flag = 1;
    		for (i = 0; i < 9; i++) {
    			if ($("#" + i).attr("value") != i) {
    				flag = 0;
    				break;
    			}
    		}
    		if (flag) {
    			$("#succ").attr("class", "alert alert-success");
    			$("#succ").attr("role", "alert");
    			$("#succ").text("成功解出!");
    			//time stop
    			clearTimeout(timer);
    			//can't move
    			$("img").attr("onClick", "");
    			document.getElementById("start").innerHTML = "再来一局";
    			// 有帮助 不记录
    			if(!isHelp) record();
    		}
    	}
    }
    

    4)自动演示还原.通过jquery获得每个img标签的value,得到一串序列,加上白块的位置,通过ajax的post方法提交给后端,后端调用解法函数返回走的序列,for循环+定时器,白块逐一移到序列的每一个位置

    function help() {
    	// 如果已经还原了
    	var arr = new Array(9);
    	flag = 1;
    	for (i = 0; i < 9; i++){
    		arr[i] = $("#" + i).attr("value");
    		if(arr[i]!=i) flag=0;
    	}
    	if(flag) document.getElementById("succ").innerHTML = '您已经成功啦';
    	else {
    	isHelp = true;
    	var jsonString = JSON.stringify(arr);
    	$.ajax({
    		type: "POST",
    		url: "http://127.0.0.1:5000/h2",
    		data: {
    			"pos": jsonString,
    			"white": arr[pos]
    		},
    		dataType: "json",
    		contentType: "application/x-www-form-urlencoded;charset=UTF-8",
    		async: false,
    		success: function(data) {
                            // 从后端返回的data是一串序列,第n步移动到哪一个位置,然后设个定时器不断调用移动函数即可
    			arr = data;
    			var j = 0;
    			function fn(){ turn(document.getElementById(arr[j])); j++; }
    			for(var i = 0; i < arr.length; i++ ){
    		    	setTimeout(fn,i*700)//还原速度
    		    }
    		},
    		error: function(XMLHttpRequest, textStatus, errorThrown) {
    			alert("请先开始游戏");
    		}
    	});
    	}
    }
    

    5)排行榜。分为时间榜和步数榜,使用bootstrap的按钮组件实现。
    2.Github代码签入记录

    3.遇到的困难及解决方法。

    • 问题描述
      对新知识不了解,经常出错,而且错误了没提示,只是该功能失效,不好检查bug
      例如:jquery改不了标签的内容,只能用dom获取标签进行修改
      本机作为服务器还得处理跨域问题...
    • 解决尝试
      网上查阅相关资料
    • 是否解决
    • 有何收获
      以前有看过相关视频,但只是纸上谈兵,自己从0到1实现一个小项目还是蛮有成就感。

    4.评价你的队友。

    • 值得学习的地方
      实力很强,提出的idea很好,画图很漂亮
    • 需要改进的地方
      得早点开始准备

    5.学习进度条

    第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
    1 200 200 14 14 解决图像识别问题
    2 150 350 10 24 学习html,css,js,做出游戏雏形,AI算法初步
    3 200 550 16 40 学习jquery,游戏功能添加
    学习ajax,实现前后端交互
    4 350 900 20 60 使用bootstrap美化页面,
    添加排行榜功能,改进AI算法
    PSP表格
    PSP2.1 Personal Software
    Process Stages
    预估耗时
    (分钟)
    实际耗时
    (分钟)
    Planning 计划
    · Estimate · 估计这个任务需要多少
    时间
    180 200
    Development 开发
    · Analysis · 需求分析 (包括学习新
    技术)
    450 480
    · Design Spec · 生成设计文档 200 180
    · Design Review · 设计复审 60 80
    · Coding Standard · 代码规范 (为目前的开
    发制定合适的规范)
    60 60
    · Design · 具体设计 100 120
    · Coding · 具体编码 1500 1600
    · Code Review · 代码复审 90 90
    · Test · 测试(自我测试,修改
    代码,提交修改)
    50 60
    Reporting 报告
    · Test Repor · 测试报告 60 70
    · Size Measurement · 计算工作量 50 75
    · Postmortem & Process
    Improvement Plan
    · 事后总结, 并提出过程
    改进计划
    100 90
    · 合计 2900 3105
  • 相关阅读:
    Swap Nodes in Pairs
    Permutations(copy)
    Sort Colors
    Merge Two Sorted Lists
    Implement Queue using Stacks
    Best Time to Buy and Sell Stock
    Happy Number
    Gray Code
    springMVC初次搭建,产生错误
    JSP常用指令
  • 原文地址:https://www.cnblogs.com/baiii/p/13843615.html
Copyright © 2011-2022 走看看