我的博客链接:https://www.cnblogs.com/axx4136/p/13841086.html
我的Github链接:https://github.com/axx4136/NumberHuaRongDao.git
队友的博客链接:https://www.cnblogs.com/kkk-home/p/13838618.html
队友的Github链接:https://github.com/031804119/jigsaw
具体分工
任务 | 队友 | 我 |
---|---|---|
原型设计 | √ | |
AI算法设计 | √ | |
游戏代码 | √ | |
接口与测试 | √ |
原型设计
设计说明
既然要设计一个游戏,最基本的肯定要有开始,退出和得分(步数),为了方便还原,将图片的原来样子也展示出来玩家可以通过查看栏菜单查看游戏规则和排行榜
游戏里添加了强制交换功能,但是因为键盘不方便实现自定义调换,可能会出现无解的情况,于是就注释掉了
因为我们的游戏界面设置的很小,所以如果要添加这么多按钮,就会显得拥挤,所以menu组件就派上了用场
原型开发工具
Axure Rp 8
结对过程
舍友两两组合
困难方法
AI与原型设计实现
代码实现思路
网络接口的使用
用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)
Github代码签入记录
模块异常
问题描述
强制交换后如果无解,在自行交换过后会出现两个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,当时写的太过草率,重新理解以后感觉蛮好的
.评价我的队友
值得学习的地方:学习能力强,效率高
需要改进的地方:有点懒癌:)
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算法,数据结构剖析,实现判断交换数据格式转换等 |