一、一些小要求
- 如下
我们的github项目地址如下:雨文丶丶队伍的GitHub
具体分工:
二、原型设计
2.1我们使用的原型设计工具:
Axure Rp 9
2.2设计说明:首先需要完成基础原型设计的要求:
(1)开始界面:
- 在窗体中设计一个独特的开始按钮,并且使用经典的数字华容道的图片作为背景。
(2)游戏界面:
-
在窗体左侧为基本信息以及一些附加功能,比如当前步数,目前为止最好成绩,还有内嵌的AI提示功能。
-
窗体右侧就是游戏的主界面,会有一块被空白块剔除,需要识别出原先的字母,并且使用wasd控制白色方块完成复原。
-
点击左下角的提示按钮就是AI内嵌的功能,按下时将会弹出接下来能够复原的步骤序列,指导你完成游戏
之后需要完成一些提高游戏体验的功能:
(3)额外功能:
- 在完成一局游戏之后弹出是否继续的按钮,这样就可以选择继续冲击更少的步数或者退出游戏
- 以防按错×按键退出游戏,在点击之后追加提问是否退出游戏
2.3结对照片:提供非摆拍的两人在讨论、细化和使用专用原型模型工具时的结对照片:
2.4 遇到的困难以及解决方法:
-
困难描述:对产品经理这一类的原型设计一点都不了解,一度以为只是画出类似流程图之类的东西。
-
解决尝试:后来在知道原型设计的实质以及其重要性之后,安装了Axure以及汉化,在B站上自学两天掌握了基础的原型设计能力,并且设计出了项目的原型图,成功解决了问题
-
有何收获:对原型设计有了初步的认识,扭转了之前直接上手代码的习惯,在认真设计出原型之后再通过代码实现会让工作效率更高。
三、AI与原型设计实现
3.1 代码实现思路:
- 网络接口的使用
def gethtml(url):
r = requests.get(url)
r.encoding = r.apparent_encoding
html = r.text
return html
def getProblemlist():
###获取赛题列表
url = "http://47.102.118.1:8089/api/challenge/list"
text = json.loads(gethtml(url))
for t in text:
print(t)
def createProblem():
###创建赛题
url = "http://47.102.118.1:8089/api/challenge/create"
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'
}
data_json = json.dumps({
"teamid": 35,
"data": {
"letter": "a",
"exclude": 5,
"challenge": [
[0, 8, 4],
[1, 9, 7],
[2, 6, 3]
],
"step": 19,
"swap": [1,2]
},
"token":"56f315e5-051d-493c-ab96-dc7bb87ea443"
})
r = requests.post(url, headers=headers, data=data_json)
print(r.text)
def getproblem():
###挑战赛题
url = "http://47.102.118.1:8089/api/challenge/start/cbc7a355-f385-4a4c-801c-da845f785336"
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'
}
data_json = json.dumps({
"teamid": 35,
"token": "56f315e5-051d-493c-ab96-dc7bb87ea443"
})
r = requests.post(url, headers=headers, data=data_json)
text = json.loads(r.text)
img_base64 = text["data"]["img"]
step = text["data"]["step"]
swap = text["data"]["swap"]
uuid = text["uuid"]
chance = text["chanceleft"]
print("chance = "+str(chance))
img = base64.b64decode(img_base64)
def postresult(uuid,totalpath,swap):
###提交赛题答案
url = "http://47.102.118.1:8089/api/challenge/submit"
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'
}
data_json = json.dumps({
"uuid": uuid,
"teamid": 35,
"token": "56f315e5-051d-493c-ab96-dc7bb87ea443",
"answer": {
"operations": totalpath,
"swap": swap
}
})
r = requests.post(url, headers=headers, data=data_json)
print(r.text)
-
代码组织与内部实现设计
-
说明算法的关键与关键实现部分流程图
-
贴出你认为重要的/有价值的代码片段,并解释
使用堆来存储结点,每次将启发函数最小的结点取出,获取其子结点,加入堆中,直到最终从堆中取出的点为目的结点
def AStar(start, end, distance_fn, generate_child_fn, swap, step, ischange):
'''
A*算法
start: 起始状态
end: 终止状态
distance_fn: 距离函数
generate_child_fn: 产生孩子节点的函数
'''
root = State(0, 0, start, hash(str(BLOCK)), None) # 根节点
end_state = State(0, 0, end, hash(str(GOAL)), None) # 终止节点
if root == end_state:
return 0,[],''
openlist.append(root)
heapq.heapify(openlist) #将列表转换为堆
node_hash_set = set() # 存储节点的哈希值
node_hash_set.add(root.hash_value)
while len(openlist) != 0:
# 删除并返回最小值结点
top = heapq.heappop(openlist)
# 如果获取节点为最后的节点,表示算法结束,获取输出路径
# 同时将强制交换的步数传入print_path函数,用来获取自选交换的序列
if top == end_state:
return print_path(top, step)
# 产生孩子节点,孩子节点加入openlist
# 当进行到一定步数时进行强制交换,如果之前已经强制交换过,就不进行操作
if top.gn == step and ischange == 0:
top.state = exchange(swap, top.state)
# 强制交换结束之后,判断当前是否有解,如果无解就进行自选交换
status = getStatus(top.state)
if status % 2 != 0:
top.state, selfswap = selfchange(top.state, end)
#将自选交换序列加入到当前结点的属性中
top.setchange(selfswap)
#生成当前结点的子结点并加入堆中
generate_child_fn(cur=top, end=end_state, hash_set=node_hash_set,
openlist=openlist, distance=distance_fn)
def generate_child(cur, end, hash_set, openlist, distance):
'''
生成子结点函数
cur: 当前节点
end: 最终状态节点
hash_set: 哈希表,用于判重
distance: 距离函数
'''
if cur == end:
# 往堆中加入一个新的值
heapq.heappush(openlist, end)
return
num = len(cur.state)
for i in range(0, num):
for j in range(0, num):
if cur.state[i][j] != 0:
continue
for d in direction: # 四个偏移方向
if d[0] == 0 and d[1] == 1:
dir = 'd'
if d[0] == 0 and d[1] == -1:
dir = 'a'
if d[0] == 1 and d[1] == 0:
dir = 's'
if d[0] == -1 and d[1] == 0:
dir = 'w'
x = i + d[0]
y = j + d[1]
if x < 0 or x >= num or y < 0 or y >= num: # 越界了
continue
state = copy.deepcopy(cur.state) # 复制父节点的状态
state[i][j], state[x][y] = state[x][y], state[i][j] # 交换位置
h = hash(str(state)) # 哈希时要先转换成字符串
if h in hash_set: # 结点重复了
continue
hash_set.add(h) # 加入哈希表
gn = cur.gn + 1 # 已经走的距离
hn = distance(cur.state, end.state) # 启发的距离函数
node = State(gn, hn, state, h, cur, dir) # 新建节点
cur.child.append(node) # 加入到孩子队列
heapq.heappush(openlist, node) # 加入到堆中
-
性能分析与改进
刚开始的时候当初始结点无解的时,选择在强制交换前进行随机交换,但是对于最后的结果影响很大,随机性很大。后面改为采用广域搜索,获得所有可能的结果,然后根据启发式函数选择最优的一种情况,再进行后面的A*算法的计算。 -
描述你改进的思路
取消随机交换造成的不确定性,使用耗时高但是结果精度高的广搜去解决初始结点无解的情况。同时将自由交换也改成启发式函数最优的情况作为最终结果。 -
展示性能分析图和程序中消耗最大的函数
-
展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路
使用测试接口对代码进行测试
def getproblem():
#接口测试获取题目
url = "http://47.102.118.1:8089/api/challenge/start/4f198e1d-a7aa-4851-bfac-644597afb8fa"
text = json.loads(gethtml(url))
# print(text.keys())#dict_keys(['img', 'step', 'swap', 'uuid'])
# text["img"] = "none" #{'img': 'none', 'step': 0, 'swap': [7, 7], 'uuid': '3bc827e5008d460b893e5cb28769e6bf'}
img_base64 = text["img"]
step = text["step"]
swap = text["swap"]
uuid = text["uuid"]
img = base64.b64decode(img_base64)
# 获取接口的图片并写入本地
with open("E:/python/软工实践/结对编程/photo.jpg", "wb") as fp:
fp.write(img) # 900*900
return step,swap,uuid
def postAnswer(uuid, opertions, swap):
#接口测试提交
url = "http://47.102.118.1:8089/api/answer"
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'
}
data_json = json.dumps({
"uuid": uuid,
"answer": {
"operations": opertions,
"swap": swap}
})
r = requests.post(url, headers=headers, data=data_json)
print(r.text)
3.2 贴出Github的代码签入记录,合理记录commit信息。
3.3 遇到的代码模块异常或结对困难及解决方法。
- 问题描述
对初始结点无解的情况,不能很好的运行代码。 - 解决尝试
使用随机变换到强制交换为止,再等强制交换之后进行自由交换。但是效果很差,后面改为使用广搜并选择最优情况。 - 是否解决
解决了初始无解的情况。 - 有何收获
对代码整体的结构有了进一步的理解,同时对整体的逻辑思路更加清晰。
3.4 评价你的队友。
- 值得学习的地方
学习能力很强,分工之后合作很迅速 - 需要改进的地方
太过优秀而不需要改进
3.5提供此次结对作业的PSP和学习进度条。
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
第一周 | 100 | 100 | 6 | 6 | 学习了Pyqt5的基本使用,学习了Axure的基本使用,掌握了游戏制作的基本框架 |
第二周 | 150 | 250 | 8 | 16 | 学习了Pyqt5的综合运用,对照画出的原型设计模板开始设计游戏 |
第三周 | 150 | 400 | 10 | 26 | 解决了开始界面的跳转问题,解决了强制交换之后多出一个空格的问题 |
第四周 | 100 | 500 | 4 | 28 | 完成了最终的产品结构,加上了亿点点的小细节 |
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 600 | 600 |
· Estimate | · 估计这个任务需要多少时间 | 600 | 600 |
Development | 开发 | 1130 | 1170 |
· Analysis | · 需求分析 (包括学习新技术) | 540 | 540 |
· Design Spec | · 生成设计文档 | 30 | 30 |
· Design Review | · 设计复审 | 30 | 30 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
· Design | · 具体设计 | 60 | 60 |
· Coding | · 具体编码 | 300 | 300 |
· Code Review | · 代码复审 | 30 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 120 | 120 |
· Test Repor | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 30 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 30 | 30 |
· 合计 | 1850 | 1890 |
四、Github使用相关演示:
再次贴出github的链接:雨文丶丶队伍的GitHub
4.1、README
对自己这个项目的简单介绍
4.2、.gitignore
配置不被允许push的文件后缀,在创建新文件之后可以git会帮助你根据语言和项目选择配置,我选择的是python
4.5、开源协议
4.6、持续集成
需要使用额外的插件Travis CI
4.7、Issues模板
设置了一个BUG模板
五、总结
这次的结对编程是一种不一样的编程体验,只需要我完成划分好的任务,剩下的一半放心的交给我的队友,最后都能够完整的实现,比起一个人的编程,结对的意义在于让这次看起来很复杂的任务能够顺利完成。