开头
-
GitHub地址
具体分工
AI算法 小程序后台 原型设计 UI 博客编写 github 何坤杰 ✔ ✔ ✔ ✔ 肖少东 ✔ ✔ ✔ ✔
原型设计
1.设计说明
-
功能设计:本次原型设计预期的基本功能有图块移动、计时计步数、更换图形查看目标图形等交互模块、排行榜以及个人成绩等。
-
原型实现:第一次进入图片资源加载可能会很慢
-
原型设计图主要页面:
- 设计流程图:
- 样式设计理念:考虑到原型的主要内容是华容道,是具有丰富历史内涵与文化底蕴的主题,所以本次原型设计的风格搭配采用的是以古风为主的简约设计。原型中的主要交互文字使用的是篆书,再联系到给的图片素材是以黑色为主要基调的图块,所以背景图打算用灰白搭配的山水网图,让原型整体戴上缕缕墨色。
2.原型工具
- 本次项目使用的原型设计工具是Axure Rp。
- AxureRP 是一个快速原型制作软件,由美国Axure Software Solutions, Inc.公司开发。Axure RP 能让操作它的人快速准确的创建基于Web的网站流程图、原型页面、交互体验设计、标注详细开发说明,并导出Html原型或规格的Word开发文档。
- 本次原型设计使用版本是8企业版。使用的交互素材是Axure素材库里的移动端开发素材库。
3.结对的过程
- 结对过程描述:出作业时刚好坐在一起,一拍即合。虽然两个都是后端开发,但考虑到对方都是有着开发经验的“老咸鱼”,所以结对的也挺顺利的。
- 结对照片:
4.遇到的困难及解决方法
困难描述 | 解决尝试 | 是否解决 |
---|---|---|
1.图块移动问题。起初的图块移动过于敏感,常常随便一移就导致图块乱窜,严重的可能导致多相同图块的bug | 改变了图块变化的思路,有原先的拖动移动变化变成点击白块周围的图块进而交换 | √ |
2.小程序登录认证问题。登录时长丢失后端给的token,导致提交记录失败。 | 先是后端解决了证书的问题(无证书无法正常请求接口),后来小程序端也对登录模块进行了修改 | √ |
3.样式显示缺失问题。初涉前端,很多样式会莫名其妙不显示。 | 查阅了很多css、js以及小程序相关博客 | √ |
4.上线问题。因为本次原型实现采用的是小程序,小程序的上线存在着诸多问题。 | 查官方文档,查博客,问客服 | √ |
5.审美问题。老直男了,界面设计一直感觉不是很满意,-_-|| | 看其他优秀设计 | × |
AI与原型设计实现
代码实现思路
网络接口的使用
- 网络接口用的都是python的requests库
代码组织
- 算法框架(算法详细在后面)
- 微信小程序后台
后台使用的是django框架。整个类的使用都是依据django而来的,唯二值得提的是
1.首先是token的构造,由于没找到类似的库,我是参考JWT构造的token,
def generate_token(no, key="key", expire=1000000):
ts_str = str(time.time() + expire)
ts_byte = ts_str.encode("utf-8")
sha1_result = hmac.new((key + str(no)).encode("utf-8"), ts_byte, 'sha1').hexdigest()
token = str(no) + ':' + ts_str + ':' + sha1_result
b64_token = base64.b64encode(token.encode("utf-8"))
return b64_token.decode("utf-8")
def certify_token(token, key="key"):
if token is None:
raise TokenError("没有token")
try:
token_str = base64.b64decode(token)
token_list = token_str.decode('utf-8').split(':')
except Exception:
raise TokenError("token解析错误")
if len(token_list) != 3:
raise TokenError("token格式错误")
ts_str = token_list[1]
if float(ts_str) < time.time():
# token expired
raise TokenError("token过期")
known_sha1 = token_list[2]
sha1 = hmac.new((key + token_list[0]).encode("utf-8"), ts_str.encode('utf-8'), 'sha1')
sha_ans = sha1.hexdigest()
if sha_ans != known_sha1:
# token certification failed
raise TokenError("token错误")
# token certification success
return str(token_list[0])
然后在view中对需要请求进行token验证与创建
- 异常处理
一个正常的后台必须要对错误的请求返回正确的错误码。错误时,django似乎本身会返回html页面。而我在了解中间件(middleware)后,使用中间件构造了异常处理模块。
class ExceptionMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
super(ExceptionMiddleware, self).__init__(get_response)
@staticmethod
def process_exception(request, exception):
if isinstance(exception, TokenError):
return JsonResponse({"result": 401, "msg": exception.value})
if isinstance(exception, NoResultError):
return JsonResponse({"result": 404, "msg": exception.value})
print("========================================================================")
print('traceback.format_exc():
%s' % traceback.format_exc())
return JsonResponse({"result": 500, "msg": "error"})
数字华容道算法/算法的关键
基本思想是bfs,由于强制交换的存在,我们需要考虑两轮bfs,分别为
- 在强制交换前进行bfs
- 在强制交换前无解启用强制交换后进行bfs
然而还有存在两个问题:
- 在强制交换时华容道有哪几种情况?
首先由于bfs的特性(每一次都是此时最远的一步),强制交换时有的情况一定在之前的bfs中出现过。
然后在交换前偶数次都可以通过左右横跳(ad)来达到强制交换时存在。
而由于华容道的特点——他每一步都需要移动而且是上下左右选一。所以如果奇数步白块是在(1,3,5,7,9)中的一个。而偶数步白块是一定是在(2,4,6,8)中的一个。所以交换前奇数步是不可能在交换前出现。
所以我们只需要把所有交换前偶数次步数的华容道状态存起来。在强制交换时把这些珍藏的状态那来交换作为第二次bfs的初始状态。
- 如何确定强制交换后有解
这个判定方法是判断逆序对的数量(不把空白块计入)是否为偶数个。
在排序好的华容道上,逆序对是0;
而空白块的移动有四种情况,wasd,在ad——左右变化的时候,华容道的逆序对不会变
当进行w或s时
原来第一列 | 原来第二列 | 原来第三列 | 移动 | 移后第一列 | 移后第二列 | 移后第三列 |
---|---|---|---|---|---|---|
a | b | c | ----------------------------> | a | c | |
d | f | ----------------------------> | d | b | f | |
g | h | i | ----------------------------> | g | h | i |
原来的逆序对数为 m + (b,c)+ (b,d)
移后的逆序对数为 m + (c,d)+ (c,b)
其中m为前后无变化的逆序对信息
以此变化量为 delta(逆序对) = (b,c)+ (b,d) - (c,d)- (c,b)
因为(b,c)与(c,b)只有一个会同时是逆序对,(b,d)与(d,b)也只有一个会同时是逆序对,所以delta(逆序对)为 -2,0,2。因此我们只需要确定是偶数个逆序对有解。
性能分析与改进
我一开始是用python写算法,然而python展现的效率。。。
所以我对python算法进行了改进:
最早是__Hash__函数,因为已出现的状态存在于字典,而华容道的特点使我们可以构造一种__Hash__算法即
def __hash__(self):
t = 0
for i in range(3):
for j in range(3):
t = t*10 + self.square[i][j]
return t
但优化后时间仍在30s左右
接着取消复制类(deepcopy),改为创建新的对象
结果只能达到10s左右,因此,我更改语言,使用c++(果然写算法用C++才是最舒服),最后能达0.8s的水平。
接着优化在于我用来存储曾经出现的华容道状态的数组,(因为我开辟了一个1000000000的bool数组)在强制交换后马上需要将其清零。这里占了极大的时间。我先将数组改下十倍(华容道九宫格九个数字只要出现8个就能推出最后一个所以用更小的数组),然后又开辟一个新的数组用于记录华容道交换后的状态。这样一来时间能在单独算法的时间能0.4秒以下。
关于图像处理,我一开始使用的是PIL的ImageChops.difference,我尝试过将给的base64进行Hash,本想到时候可进行查表直接返回对应的华容道状态。然而我直接拼接的华容道图片终是与接口返回的有差别。用PIL的ImageChops.difference是可判别,但是在文件大小等方面却不一样,导致hash不出相同的值。结果优化一直失败。
单元测试
上面是我写的脚本,作用是用于自动对测试组提供的问题接口来请求,求解,返回,并将结果存取下来。
用他来判别算法是否正确
Github的代码签入记录
算法
小程序
困难及解决方法
-
算法在最开始我遗漏了,交换前有哪些状态这个问题,不过最后还是改回来了
-
django的orm是一个感觉奇怪的玩意。他一定义外键就会变成拉入一个类进来。导致在返回排行榜的时候,会自己返回一个用户。而解决方法,我是靠修改序列化的方法,将返回一个用户改为返回他的头像与名字
-
另外一个问题是微信小程序需要的https证书与域名,这个https证书依靠阿里云的免费ssl,域名5元一年,真正的问题在于网站备案,由于办事效率的不确定,我最后借用了别人的域名
评价你的队友
-
值得学习的地方
他提供了非常优秀前端界面,对我们的作业提供了许多优秀的建议
-
需要改进的地方
还需要加强审美
结对作业的PSP和学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 278 | 278 | 12 | 12 | 学习了python的PIL,能够对比头像,并实现了算法一部分 |
2 | 385-142 | 421 | 13 | 25 | 将算法改为c++,算法完全实现 ,学习了django的orm集 |
3 | 272 | 693 | 10 | 35 | 后台功能加强,实现token,一对一实现,算法修正,测试脚本实现 |
4 | 182 | 876 | 8 | 43 | 实现Ai大比拼脚本,进行算法优化 |
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 7 | 6 |
· Estimate | · 估计这个任务需要多少时间 | 7 | 6 |
Development | 开发 | 2000 | 2239 |
· Analysis | · 需求分析 (包括学习新技术) | 450 | 672 |
· Design Spec | · 生成设计文档 | 40 | 68 |
· Design Review | · 设计复审 | 40 | 62 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 0 | 0 |
· Design | · 具体设计 | 20 | 12 |
· Coding | · 具体编码 | 1000 | 815 |
· Code Review | · 代码复审 | 20 | 58 |
· Test | · 测试(自我测试,修改代码,提交修改) | 430 | 552 |
Reporting | 报告 | 190 | 317 |
· Test Repor | · 测试报告 | 90 | 235 |
· Size Measurement | · 计算工作量 | 40 | 48 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 34 |
合计 | 2197 | 2562 |