结对编程作业

我的github 队友的博客

一、原型设计

分工 ai算法 原型设计 AI 与原型设计实现 博客编写
高逸超 70% 20% 80% 30%
吴仕涛 30% 80% 20% 70%

二、AI与原型设计实现

原型设计概述:

  • 本次设计为一款简单可玩的数字华容道web小程序。设计目的:可为闲暇的用户打发打发时间,自动生成可解三阶数字华容道,同时可获得本次所需的分解打乱的字母进行复原。有可以查看历史得分,记录步数以及时间的功能。
  1. 页面设计说明:

    • 开始页面和分数记录

  • 游戏界面

  1. 原型工具: Axure,很简单的工具,原型设计基本功,很多人都是新学的,但是还挺得心应手的。原型设计出来但是实现怎么样只能看天命~~
  2. 结对照片

  1. 遇到的问题和解决方法

    遇到的主要问题没有很大,主要还是沟通上的,想要的不一定能实现,沟通很久才有现在的效果

AI与原型设计实现

AI部分

流程图

算法思路

一开始拿到这个题就想着识图分割然后编号三阶行列式bfs

但是思路很美好,完成的过程非常坎坷,只能说实力不行

核心算法介绍

A*

• 在全局择优搜索中,每当需要扩展节点时,总是从 Open 表的所有节点中选择一个估价函数值最小的节点进行扩展。其搜索过程可能描述如下:

• ( 1 )把初始节点 S0 放入 Open 表中, f(S0)=g(S0)+h(S0) ;

• ( 2 )如果 Open 表为空,则问题无解,失败退出;

• ( 3 )把 Open 表的第一个节点取出放入 Closed 表,并记该节点为 n ;

• ( 4 )考察节点 n 是否为目标节点。若是,则找到了问题的解,成功退出;

• ( 5 )若节点 n 不可扩展,则转到第 (2) 步;

• ( 6 )扩展节点 n ,生成子节点 ni ( i =1,2, …… ) ,计算每一个子节点的估价值 f( ni ) ( i =1,2, …… ) ,并为每一个子节点设置指向父节点的指针,然后将这些子节点放入 Open 表中;

• ( 7 )根据各节点的估价函数值,对 Open 表中的全部节点按从小到大的顺序重新进行排序;

• ( 8 )转第 (2) 步。

参考代码如下

#include<cstdio>
#include<queue>
#include<map>
using namespace std;
char arr[10],brr[10]="123804765";
struct node{
	int num,step,cost,zeroPos;
	bool operator<(const node &a)const{
		return cost>a.cost;
	}
	node(int n,int s,int p){
		num=n,step=s,zeroPos=p;
		setCost();
	}
	void setCost(){
		char a[10];
		int c=0;
		sprintf(a,"%09d",num);
		for(int i=0;i<9;i++)
			if(a[i]!=brr[i])
				c++;
		cost=c+step;
	}
};
int des=123804765;
int changeId[9][4]={{-1,-1,3,1},{-1,0,4,2},{-1,1,5,-1},
					{0,-1,6,4},{1,3,7,5},{2,4,8,-1},
					{3,-1,-1,7},{4,6,-1,8},{5,7,-1,-1}};
map<int,bool>mymap;
priority_queue<node> que;//优先级队列 
void swap(char* ch,int a,int b){char c=ch[a];ch[a]=ch[b];ch[b]=c;}
int bfsHash(int start,int zeroPos){
	char temp[10];
	node tempN(start,0,zeroPos);//创建一个节点 
	que.push(tempN);//压入优先级队列 
	mymap[start]=1;//标记开始节点被访问过 
	while(!que.empty()){
		tempN=que.top();
		que.pop();//弹出一个节点 
		sprintf(temp,"%09d",tempN.num);
		int pos=tempN.zeroPos,k;
		for(int i=0;i<4;i++){
			if(changeId[pos][i]!=-1){
				swap(temp,pos,changeId[pos][i]);
				sscanf(temp,"%d",&k);
				if(k==des)return tempN.step+1;
				if(mymap.count(k)==0){
					node tempM(k,tempN.step+1,changeId[pos][i]);
					que.push(tempM);//创建一个新节点并压入队列 
					mymap[k]=1;
				}
				swap(temp,pos,changeId[pos][i]);
			}
		}
	}
}
int main(){
	int n,k,b;
	scanf("%s",arr);
	for(k=0;k<9;k++)
		if(arr[k]=='0')break;
	sscanf(arr,"%d",&n);
	b=bfsHash(n,k);
	printf("%d",b);	
	return 0;
}

我们自己的核心代码和上述很像

如下

import time as tm
g_dict_layouts = {}
g_dict_layouts_deep = {}
g_dict_layouts_fn = {}

g_dict_shifts = {0:[1, 3], 1:[0, 2, 4], 2:[1, 5],
                 3:[0,4,6], 4:[1,3,5,7], 5:[2,4,8],
                 6:[3,7],  7:[4,6,8], 8:[5,7]}

def dislocation(srcLayout,destLayout):
    sum=0
    a= srcLayout.index("0")
    for i in range(0,9):
        if i!=a:
            sum=sum+abs(i-destLayout.index(srcLayout[i]))
    return sum
def swap(a, i, j, deep, destLayout):
    if i > j:
        i, j = j, i
    b = a[:i] + a[j] + a[i+1:j] + a[i] + a[j+1:]
    #存储fn,A*算法
    fn = dislocation(b, destLayout)+deep
    return b, fn

def solveP(srcLayout, destLayout):

    src=0;dest=0
    for i in range(1,9):
        fist=0
        for j in range(0,i):
          if srcLayout[j]>srcLayout[i] and srcLayout[i]!='0':
              fist=fist+1
        src=src+fist
    for i in range(1,9):
        fist=0
        for j in range(0,i):
          if destLayout[j]>destLayout[i] and destLayout[i]!='0':
              fist=fist+1
        dest=dest+fist
    if (src%2)!=(dest%2):
        return -1, None
    g_dict_layouts[srcLayout] = -1
    g_dict_layouts_deep[srcLayout]= 1
    g_dict_layouts_fn[srcLayout] = 1 + dislocation(srcLayout, destLayout)
    stack_layouts = []
    gn=0
    stack_layouts.append(srcLayout)
    while len(stack_layouts) > 0:
        curLayout = min(g_dict_layouts_fn, key=g_dict_layouts_fn.get)
        del g_dict_layouts_fn[curLayout]
        stack_layouts.remove(curLayout)

        if curLayout == destLayout:#判断当前状态是否为目标状态
            break

        ind_slide = curLayout.index("0")
        lst_shifts = g_dict_shifts[ind_slide]#当前可进行交换的位置集合
        for nShift in lst_shifts:
            newLayout, fn = swap(curLayout, nShift, ind_slide, g_dict_layouts_deep[curLayout] + 1, destLayout)
            if g_dict_layouts.get(newLayout) == None:#判断交换后的状态是否已经查询过
                g_dict_layouts_deep[newLayout] = g_dict_layouts_deep[curLayout] + 1
                g_dict_layouts_fn[newLayout] = fn#存入fn
                g_dict_layouts[newLayout] = curLayout
                stack_layouts.append(newLayout)#存入集合
    lst_steps = []
    lst_steps.append(curLayout)
    while g_dict_layouts[curLayout] != -1:#存入路径
        curLayout = g_dict_layouts[curLayout]
        lst_steps.append(curLayout)
    lst_steps.reverse()
    return 0, lst_steps
def run():
    srcLayout  = "538210467"
    destLayout = "123456780"

    retCode, lst_steps = solveP(srcLayout, destLayout)
    if retCode != 0:
        print("不可行")
    else:
        num=len(lst_steps)
        for nIndex in range(num):
            print("step #" + str(nIndex))
            print(lst_steps[nIndex][:3])
            print(lst_steps[nIndex][3:6])
            print(lst_steps[nIndex][6:])


def main():
    run()


if __name__ == "__main__":
    main()

识图算法

我们的识图和图片分割算法可以说是残疾人算法,时灵时不灵 参考

# -*- coding: utf-8 -*-
"""
Created on Tue May 28 19:23:19 2019
将图片按照表格框线交叉点分割成子图片(传入图片路径)
@author: hx
"""
 
import cv2
import numpy as np
import pytesseract
 
image = cv2.imread('C:/Users/Administrator/Desktop/7.jpg', 1)
#灰度图片
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
#二值化
binary = cv2.adaptiveThreshold(~gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 35, -5)
#ret,binary = cv2.threshold(~gray, 127, 255, cv2.THRESH_BINARY)
cv2.imshow("二值化图片:", binary) #展示图片
cv2.waitKey(0)
 
rows,cols=binary.shape
scale = 40
#识别横线
kernel  = cv2.getStructuringElement(cv2.MORPH_RECT,(cols//scale,1))
eroded = cv2.erode(binary,kernel,iterations = 1)
#cv2.imshow("Eroded Image",eroded)
dilatedcol = cv2.dilate(eroded,kernel,iterations = 1)
cv2.imshow("表格横线展示:",dilatedcol)
cv2.waitKey(0)
 
#识别竖线
scale = 20
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(1,rows//scale))
eroded = cv2.erode(binary,kernel,iterations = 1)
dilatedrow = cv2.dilate(eroded,kernel,iterations = 1)
cv2.imshow("表格竖线展示:",dilatedrow)
cv2.waitKey(0)
 
#标识交点
bitwiseAnd = cv2.bitwise_and(dilatedcol,dilatedrow)
cv2.imshow("表格交点展示:",bitwiseAnd)
cv2.waitKey(0)
# cv2.imwrite("my.png",bitwiseAnd) #将二值像素点生成图片保存
 
#标识表格
merge = cv2.add(dilatedcol,dilatedrow)
cv2.imshow("表格整体展示:",merge)
cv2.waitKey(0)
 
 
#两张图片进行减法运算,去掉表格框线
merge2 = cv2.subtract(binary,merge)
cv2.imshow("图片去掉表格框线展示:",merge2)
cv2.waitKey(0)
 
#识别黑白图中的白色交叉点,将横纵坐标取出
ys,xs = np.where(bitwiseAnd>0)
 
mylisty=[] #纵坐标
mylistx=[] #横坐标
 
#通过排序,获取跳变的x和y的值,说明是交点,否则交点会有好多像素值值相近,我只取相近值的最后一点
#这个10的跳变不是固定的,根据不同的图片会有微调,基本上为单元格表格的高度(y坐标跳变)和长度(x坐标跳变)
i = 0
myxs=np.sort(xs)
for i in range(len(myxs)-1):
    if(myxs[i+1]-myxs[i]>10):
        mylistx.append(myxs[i])
    i=i+1
mylistx.append(myxs[i]) #要将最后一个点加入
 
 
i = 0
myys=np.sort(ys)
#print(np.sort(ys))
for i in range(len(myys)-1):
    if(myys[i+1]-myys[i]>10):
        mylisty.append(myys[i])
    i=i+1
mylisty.append(myys[i]) #要将最后一个点加入
 
print('mylisty',mylisty)
print('mylistx',mylistx)
 
 
#循环y坐标,x坐标分割表格
for i in range(len(mylisty)-1):
    for j in range(len(mylistx)-1):
        #在分割时,第一个参数为y坐标,第二个参数为x坐标
        ROI = image[mylisty[i]+3:mylisty[i+1]-3,mylistx[j]:mylistx[j+1]-3] #减去3的原因是由于我缩小ROI范围
        cv2.imshow("分割后子图片展示:",ROI)
        cv2.waitKey(0)
 
        #special_char_list = '`~!@#$%^&*()-_=+[]{}|\\;:‘’,。《》/?ˇ'
        pytesseract.pytesseract.tesseract_cmd = 'E:/Tesseract-OCR/tesseract.exe'
        text1 = pytesseract.image_to_string(ROI)  #读取文字,此为默认英文
        #text2 = ''.join([char for char in text2 if char not in special_char_list])
        print('识别分割子图片信息为:'+text1)
        j=j+1
    i=i+1
        

网络接口使用

因为使用 python 来 编写 ai,所以网络接口的实现就十分简单

使用了业界最流行的 requests 库

from typing import List
import requests


def get_not_answer_list() -> List:
    url = 'http://47.102.118.1:8089/api/team/problem/xx'
    r = requests.get(url)
    return r.json()
访问text
import json
from typing import List, Dict
from ai.util import get_swaps


def test(serial_number: List[int], swap_step: int, swap: List[int], operations: str, my_swap: List[int]):
    pass


def main():
    fail_list: List[Dict] = []
    with open('test.txt', 'r', encoding='utf-8') as f:
        while s:=f.readline():
            print(s)
            fail_list.append(json.loads(s))
    # for fail in fail_list:
    #     ai = get_swaps.Ai(fail['serial_number'], fail['swap_step'], fail['swap'], fail['uuid'])
    #     ai.get_steps()
    #     print(ai.operations, ai.is_solution, ai.my_swap)
    fail = fail_list[0]
    ai = get_swaps.Ai(fail['serial_number'], fail['swap_step'], fail['swap'], fail['uuid'])
    ai.get_steps()
    print(ai.operations, ai.my_swap, fail)


if __name__ == '__main__':
    main()
内存占用

三、最后的部分

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

遇到的代码模块异常或结对困难及解决方法。****

  • 问题描述

    ai识图功能很难完成,有时候能识别有时候完成不了

  • 解决尝试

    通过算法调试解决部分问题

  • 是否解决

    只解决了一部分,还有很多是没做到的

  • 有何收获

    说明ai算法只是面向网络抄是没有好下场的

评价你的队友。

吴仕涛评价高逸超

  • 值得学习的地方

    做事很认真,算法做很好

  • 需要改进的地方

    有点心急,可以戒骄戒躁

高逸超评价吴仕涛

  • 值得学习的地方

    做原型很快,实力还行

  • 需要改进的地方

我们都需要提高编程能力,需要认真一点

.2.5]提供此次结对作业的PSP和学习进度条

PSP表格

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

学习进度条

第N周 新增代码(行) 累计代码(行) 累计学习耗时(小时) 重要成长
第1周 180 180 10 postman使用
第2周 330 510 14 bfs,A、A*算法
第3230 200 710 20 图像处理方式
第4周 200 900 20 ai大比拼加强很多实力