结对编程作业

我的github项目地址 博客链接

队友的github项目地址 博客链接

具体分工情况表格

任务 队友
原型设计
算法
界面
接口

一、原型设计

1.提供此次结对作业的设计说明,要求文字准确、样式清晰、图文并茂(多贴原型图)

此次作业设计了4个页面,分别是主界面,开始游戏界面,游戏规则界面,联网解题界面,历史得分界面(具体内容见下图),并实现了各个界面之间的跳转。
主界面: 是整个程序(游戏)的入口,用于链接和跳转各个界面。
开始游戏: 是单机游戏,随机生成一个白块,并有提示功能,可玩性还行。
游戏规则: 这个游戏的游戏规则还挺复杂,所以这个界面说明了游戏规则。
联网解题模式: 链接postman接口,获取随机的题目,并通过AI解法解题,提交答案。
历史得分: 针对开始游戏的那个单机游戏按步数算分,并把得分的前15个返回,简而言之,就是一个排行版。



  • 所有页面的跳转方式如下

2.原型设计工具

Axure Rp

3.结对图片描述结对的过程,提供非摆拍的两人在讨论、细化和使用专用原型模型工具时的结对照片。

题目出来的时候刚好在上课,两人对视一眼,一拍即合,默契满分,组队成功。

4.遇到的困难及解决方法

  • 困难描述

1.不懂原型设计是什么?
2.只能做简单的界面,不能实现页面的跳转,其实就是不会使用Axure Rp。

  • 解决尝试

一开始不知道原型设计的概念,后来通过百度,B站等途径了解了原型设计是产品经理的一门必修课,然后在B站两小时速成Axure Rp的使用,就可以做一个简单的还算可以的原型。

  • 是否解决

  • 有何收获

1.了解了原型设计的重要性,扩展了知识面
2.学会了Axure Rp的使用,可以独立做一些简单的、工程量小的原型设计

二、AI与原型设计实现

1.代码实现思路

  • 网络接口的使用

  •  请求接口

import base64
import json
import requests

def gethtml(url):
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3941.4 Safari/537.36'
        }
        r=requests.get(url,headers = headers)
        r.raise_for_status()
        r.encoding=r.apparent_encoding
        return r.text
    except:
        return ""
if __name__ == "__main__":
    url = "http://47.102.118.1:8089/api/problem?stuid=031802433"
    # 每次请求的结果都不一样,动态变化
    text = json.loads(gethtml(url))
    img_base64 = text["img"]
    step = text["step"]
    swap = text["swap"]
    uuid = text["uuid"]
    img = base64.b64decode(img_base64)

  • 提交接口
import json
import requests
def getAnswer(url):
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3941.4 Safari/537.36',
            'Content-Type': 'application/json'
        }
        r=requests.post(url,headers = headers,data = data_json)
        return r.text

if __name__ == "__main__":
    url = " http://47.102.118.1:8089/api/answer"
    data = {
    "uuid":"20c8d3e27d6e4d638a1fed4218737e41",#本道题目标识
    "answer":{
        "operations": "wsaaadasdadadaws",
        "swap": [1,2]
            }
            }
    data_json = json.dumps(data)#转为json提交
    ret = getAnswer(url)
  • 代码组织与内部实现设计(类图)

  • 说明算法的关键与关键实现部分流程图

  •   AStart算法

  •   确定序列算法

  • 贴出你认为重要的/有价值的代码片段,并解释

最有价值的代码片段肯定是A*算法进行自动求解,但是以因为这一部分不是我写的,所以就换成历史记录得分界面的编写,通过文件存储历史得分并取出前15名进行显示输出。

class ThirdUi(QWidget):
    def __init__(self):
        super(ThirdUi, self).__init__()
        self.init_ui()

    def init_ui(self):
        self.resize(950, 950)
        self.setWindowTitle('历史得分')
        #设置背景
        window_pale = QtGui.QPalette()
        window_pale.setBrush(self.backgroundRole(), QtGui.QBrush(QtGui.QPixmap("back.jpg")))
        self.setPalette(window_pale)

        self.label1 = QLabel(self)
        self.label1.setGeometry(350, 50, 255, 150)
        self.label1.setText("历史得分")
        self.label1.setStyleSheet("QLabel{color:rgb(0,0,0,255);font-size:60px;font-weight:bold;font-family:楷体;}")
        self.label = QLabel(self)
        self.label.setGeometry(400, 150, 600, 800)
        with open("score.txt","r",encoding="utf-8") as fp:
            scores = fp.readlines()
            ls = []#只保留前15个记录
            for score in scores:
                ls.append(int(score.replace('
', '')))
            ls = sorted(ls)
            finalstr = ''
            if len(ls)>=15:
                for i in range(15):
                    if i<9:
                        finalstr += (str(i + 1) + '.' +"  "+ str(ls[i]) + '
')
                    else:
                        finalstr += (str(i+1)+'.'+" " +str(ls[i])+'
')
            else:
                for i in range(len(ls)):
                    if i<9:
                        finalstr += (str(i + 1) + '.' +"  "+ str(ls[i]) + '
')
                    else:
                        finalstr += (str(i+1)+'.'+" " +str(ls[i])+'
')
            self.label.setText(finalstr)
            self.label.setStyleSheet("QLabel{color:rgb(0,0,0,255);font-size:40px;font-weight:bold;font-family:楷体;}")
        # self.label.setStyleSheet("QLabel{color:rgb(33,215,217,255)}")
        self.btn = QPushButton('返回主页面', self)
        self.btn.setGeometry(0,0, 150, 50)
        self.btn.setStyleSheet("QPushButton{color:black;font-size:20px}"
                                "QPushButton:hover{background-color:lightgreen}"
                                "QPushButton{background-color:lightblue}"
                                "QPushButton{border:2px}"
                                "QPushButton{border-radius:10px}"
                                "QPushButton{padding:2px 4px}"
                                )
        self.btn.clicked.connect(self.slot_btn_function)

    def slot_btn_function(self):
        self.hide()
        self.f = FirstUi()
        self.f.show()
  • 性能分析与改进

从图可以看出本程序的时间消耗最大是pyqt5内置方法,键盘等待时间,AStart算法,pyqt5内置方法无法优化,键盘等待时间受人为因素影响不确定,所以唯一可以优化的就是AStart算法。

  • 描述你改进的思路
    通过优化AStart算法来提高程序的性能。
  •   改进前
    def solve(self):
        # self.prt2(self.mylist)
        self.goal = []
        for row in range(3):
            self.goal.append([])
            for column in range(3):
                temp = self.numbers[row * 3 + column]
                self.goal[row].append(temp)
        # self.prt2(self.goal)
        self.mylist = self.blocks
        if not checkNoAns(self.mylist):
            print("No Ans!")
            exit(1)
        startNum = StruNum(self.mylist, [], 0, self.goal)
        g_ListArr.append(startNum)
        cichu = 0
        while g_ListArr:

            nowNum = g_ListArr[0]
            g_ListArr.remove(nowNum)  # 从队列头移除
            g_readArr.append(nowNum)  # 添加到已读队列
            cichu +=1;
            if nowNum.myList == self.goal:
                print("Find The Answer!")
                self.ans = ""
                while nowNum.myPreList:
                    x1, y1 = find0(nowNum.myList)
                    nowNum = nowNum.GetItsPre()
                    x2, y2 = find0(nowNum.myList)
                    if x2 > x1:
                        self.ans += "w"
                    elif x2 < x1:
                        self.ans += "s"
                    elif x2 == x1:
                        if y2 > y1:
                            self.ans += "a"
                        elif y2 < y1:
                            self.ans += "d"
                listans = list(self.ans)
                listans.reverse()
                self.ans = "".join(listans)
                print(self.ans)
                print(cichu)
                break
            else:
                [idx, jdx] = nowNum.GetZeroIndex()
                if idx - 1 >= 0:
                    upList = copy.deepcopy(nowNum.myList)
                    upList[idx][jdx] = upList[idx - 1][jdx]
                    upList[idx - 1][jdx] = 0
                    upNum = StruNum(upList, nowNum.myList, nowNum.myCost + 1, self.goal)
                    # A*算法:代价 = 到达此状态代价 + 期望到达目标节点代价
                    upNum.myCost += upNum.GetGoalCost()
                    # 如果新节点没有被走过
                    if upNum.GetListIndex(g_readArr) == -1:
                        tmpIndex = upNum.GetListIndex(g_ListArr)
                        if tmpIndex != -1:
                            # 当新节点已经出现在未读队列中,如果新节点的代价更小,则更新,否则不更新
                            if upNum.myCost < g_ListArr[tmpIndex].myCost:
                                g_ListArr.remove(g_ListArr[tmpIndex])
                                g_ListArr.append(upNum)
                        else:
                            g_ListArr.append(upNum)

                if idx + 1 < 3:
                    downList = copy.deepcopy(nowNum.myList)
                    downList[idx][jdx] = downList[idx + 1][jdx]
                    downList[idx + 1][jdx] = 0
                    downNum = StruNum(downList, nowNum.myList, nowNum.myCost + 1, self.goal)
                    downNum.myCost += downNum.GetGoalCost()
                    # 如果新节点没有被走过
                    if downNum.GetListIndex(g_readArr) == -1:
                        tmpIndex = downNum.GetListIndex(g_ListArr)
                        if tmpIndex != -1:
                            # 当新节点已经出现在未读队列中,如果新节点的代价更小,则更新,否则不更新
                            if downNum.myCost < g_ListArr[tmpIndex].myCost:
                                g_ListArr.remove(g_ListArr[tmpIndex])
                                g_ListArr.append(downNum)
                        else:
                            g_ListArr.append(downNum)

                if jdx - 1 >= 0:
                    leftList = copy.deepcopy(nowNum.myList)
                    leftList[idx][jdx] = leftList[idx][jdx - 1]
                    leftList[idx][jdx - 1] = 0
                    leftNum = StruNum(leftList, nowNum.myList, nowNum.myCost + 1, self.goal)
                    leftNum.myCost += leftNum.GetGoalCost()
                    # 如果新节点没有被走过
                    if leftNum.GetListIndex(g_readArr) == -1:
                        tmpIndex = leftNum.GetListIndex(g_ListArr)
                        if tmpIndex != -1:
                            # 当新节点已经出现在未读队列中,如果新节点的代价更小,则更新,否则不更新
                            if leftNum.myCost < g_ListArr[tmpIndex].myCost:
                                g_ListArr.remove(g_ListArr[tmpIndex])
                                g_ListArr.append(leftNum)
                        else:
                            g_ListArr.append(leftNum)

                if jdx + 1 < 3:
                    rightList = copy.deepcopy(nowNum.myList)
                    rightList[idx][jdx] = rightList[idx][jdx + 1]
                    rightList[idx][jdx + 1] = 0
                    rightNum = StruNum(rightList, nowNum.myList, nowNum.myCost + 1, self.goal)
                    rightNum.myCost += rightNum.GetGoalCost()
                    # 如果新节点没有被走过
                    if rightNum.GetListIndex(g_readArr) == -1:
                        tmpIndex = rightNum.GetListIndex(g_ListArr)
                        if tmpIndex != -1:
                            # 当新节点已经出现在未读队列中,如果新节点的代价更小,则更新,否则不更新
                            if rightNum.myCost < g_ListArr[tmpIndex].myCost:
                                g_ListArr.remove(g_ListArr[tmpIndex])
                                g_ListArr.append(rightNum)
                        else:
                            g_ListArr.append(rightNum)

                # 按照COST排序
                g_ListArr.sort(key=takeThr)


  •   改进后
def Astar(startStat):
    # open和closed存的是grid对象
    openlist = []
    closed = []
    # 初始化状态
    g = grid(startStat)
    # 检查是否有解
    if not judge(startStat, target):
        print("无解")
        exit(1)

    openlist.append(g)
    # time变量用于记录遍历次数
    time = 0
    # 当open表非空时进行遍历
    while openlist:
        # 根据启发函数值对open进行排序,默认升序
        openlist.sort(key=lambda G: G.F)
        # 找出启发函数值最小的进行扩展
        minFStat = openlist[0]
        # 检查是否找到解,如果找到则从头输出移动步骤
        if minFStat.H == 0:
            print("found and times:", time, "moves:", minFStat.G)
            minFStat.seeAns()
            break

        # 走到这里证明还没有找到解,对启发函数值最小的进行扩展
        openlist.pop(0)
        closed.append(minFStat)
        expandStats = minFStat.expand()
        # 遍历扩展出来的状态
        for stat in expandStats:
            # 将扩展出来的状态(二维列表)实例化为grid对象
            tmpG = grid(stat)
            # 指针指向父节点
            tmpG.pre = minFStat
            # 初始化时没有pre,所以G初始化时都是0
            # 在设置pre之后应该更新G和F
            tmpG.update()
            # 查看扩展出的状态是否已经存在与open或closed中
            findstat = isin(tmpG, openlist)
            findstat2 = isin(tmpG, closed)
            # 在closed中,判断是否更新
            if findstat2[0] == True and tmpG.F < closed[findstat2[1]].F:
                closed[findstat2[1]] = tmpG
                openlist.append(tmpG)
                time += 1
            # 在open中,判断是否更新
            if findstat[0] == True and tmpG.F < openlist[findstat[1]].F:
                openlist[findstat[1]] = tmpG
                time += 1
            # tmpG状态不在open中,也不在closed中
            if findstat[0] == False and findstat2[0] == False:
                openlist.append(tmpG)
                time += 1


从这几张性能分析图的对比可以看出,改进后的算法性能提升了将近三倍。

  • 展示性能分析图和程序中消耗最大的函数

  •   性能分析图

      程序中消耗最大的函数:pyqt5内置方法(大约占了程序的74.6%

  • 展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路

单元测试采用AI大比拼的接口,通过返回的"success"值与True比较,如果相同则说明本题求解成功,通过单元测试,反之则未通过。具体实现代码如下:

#coding:utf-8
import unittest
from AI大比拼 import *
class MyTestCase(unittest.TestCase):
    def test1(self):
        uuid = "7aa3a255-a8a9-4314-91d8-57772d068087"
        global  answer
        answer = False
        step, swap, uuid, zuhao, listproblem, dis = jiekou.challenge(uuid)
        set(step, swap, uuid, zuhao, listproblem, dis)
        NumberHuaRong()
        jiekou.submit(uuid, operations, myswap)
        self.assertEqual(answer, True)
    def test2(self):
        uuid = "ec0dd026-3a78-4b18-971f-2b651aaa7b5f"
        global  answer
        answer = False
        step, swap, uuid, zuhao, listproblem, dis = jiekou.challenge(uuid)
        set(step, swap, uuid, zuhao, listproblem, dis)
        NumberHuaRong()
        jiekou.submit(uuid, operations, myswap)
        self.assertEqual(answer, True)
    def test3(self):
        uuid = "cac66a4d-956b-4abe-9baf-f45087a4290a"
        global  answer
        answer = False
        step, swap, uuid, zuhao, listproblem, dis = jiekou.challenge(uuid)
        set(step, swap, uuid, zuhao, listproblem, dis)
        NumberHuaRong()
        jiekou.submit(uuid, operations, myswap)
        self.assertEqual(answer, True)
if __name__ == '__main__':
    unittest.main()

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






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

  • 问题描述

在编写UI界面的过程中,华容道的界面和主界面无法进行切换,开始是想通过一个按钮来实现界面的切换,但是添加了按钮后整个游戏的九宫格布局就被毁了,而且还有可能闪退.

  • 解决尝试

队友灵机一动,想出了通过键盘的按钮来触发界面的切换,既不会破坏布局,也可以实现切换功能,而且效果还更好。问题完美解决~~~nice

  • 是否解决

  • 有何收获

通过这次华容道的学习,学习到了非常多的知识。比如pyqt5的以及qtDesigner的使用,在这之前,我只会用tkinter进行一些简单的GUI界面编程,这次通过pyqt5的学习,可以用之开发一些较为复杂的界面,qt不愧是GUI编程的神器。还有接口的使用,学习了使用Python提交url请求。

4.评价你的队友。(2分)

  • 值得学习的地方

十分感谢我无敌的队友,我生病了一段时间回来就把大部分东西都搞完了,超强的学习和实践能力是十分值得我学习的。

  • 需要改进的地方

太优秀了,没有缺点,不用改进。

5.提供此次结对作业的PSP和学习进度条(每周追加)

  • 学习进度条
第N周 新增代码(行) 累计代码(行) 本周学习耗时(小时) 累计学习耗时(小时) 重要成长
第一周 300 300 10 10 学会设计原型和Axure的使用,初步了解qt
第二周 500 800 5 15 学习了一些八数码的算法
第三周 300 1100 12 22 学习了pyqt5编写GUI界面
第四周 300 1400 10 32 学习了接口的编写
  • PSP表格

    PSP2.1

    Personal Software Process Stages

    预估耗时(分钟)

    实际耗时(分钟)

    Planning

    计划

    70

    70

    · Estimate

    · 估计这个任务需要多少时间

    70

    70

    Development

    开发

    1030

    1750

    · Analysis

    · 需求分析 (包括学习新技术)

    300

    870

    · Design Spec

    · 生成设计文档

    200

    210

    · Design Review

    · 设计复审

    50

    40

    · Coding Standard

    · 代码规范 (为目前的开发制定合适的规范)

    20

    30

    · Design

    · 具体设计

    30

    30

    · Coding

    · 具体编码

    300

    430

    · Code Review

    · 代码复审

    30

    20

    · Test

    · 测试(自我测试,修改代码,提交修改)

    100

    120

    Reporting

    报告

    400

    440

    · Test Repor

    · 测试报告

    300

    320

    · Size Measurement

    · 计算工作量

    50

    50

    · Postmortem & Process Improvement Plan

    · 事后总结, 并提出过程改进计划

    50

    70

    · 合计

    1500

    2260

原文地址:https://www.cnblogs.com/koun/p/13811227.html