结对编程作业
具体分工:
个人 | 队友 |
---|---|
前端 原型图 编写文档 | 后端 AI算法 接口 打榜 |
原型设计
1.设计说明
原型设计包括开始页面,开始按钮,重置按钮,暂停按钮。其中还包含了计时函数和计算步数的函数,后面更是添加了保存最佳成绩的具体实现函数,能够清晰的看出自己的操作的过程中实现拼图的最短步数。
开始页面的代码:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" //防止乱码
content="text/html; charset=utf-8"/>
<title>华容道开始界面</title>
</head>
<body>
<p>请点击图片链接进入游戏界面
<a href="华容道2.html"> //开始页面的图片地址,运行后要自己改图片的位置
<img border="0" src="./A.jpg" width="200" height="200">
</a></p>
</body>
</html>
游戏代码:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>九宫格拼图</title>
<style>
*{
padding: 0;
margin: 0;
border: 0;
}
/* *是通配符,给所有的元素去掉默认样式,因为有的浏览器会默认加上一些样式,这可能会给布局带来问题 */
body{
width: 100%;
height: 100%;
}
/* 给body设置100%的高度和宽度,这样就会根据浏览器屏幕大小自动适配 */
#container{
position: relative;
width: 620px;
height: 450px;
margin: 0 auto;
margin-top: 100px;
border-radius: 1px;
}
/* 这是包裹所有元素的DIV,给他设置620px的宽和450px的高,这个大小可以设置为更大,但是不能小,至少要能包含里面所有的元素 */
#game{
position: absolute;
width: 450px;
height: 450px;
border-radius: 5px;
display: inline-block;
background-color: white;
box-shadow: 0 0 10px #ffe171;
}
/* 这是游戏区的DIV,这个大小是计算出来的,取决于你的小方块的大小。这里我们设置小方块的大小为150px 150px,所以这个大小是150px*3,为450px */
/* 这里的background-color 设置为blue */
#game img{
position: absolute;
width: 149px;
height: 149px;
box-shadow: 1px 1px 2px #777;
background-color: green;
color: white;
text-align: center;
font-size: 150px;
line-height: 150px;
cursor: pointer;
-webkit-transition: 0.3s;/*浏览器前缀,兼容其他浏览器 chrome*/
-moz-transition: 0.3s;/*firefox*/
-ms-transition: 0.3s;/*ie*/
-o-transition: 0.3s;/*opera*/
transition: 0.3s;
}
/* 这就是小方块的大小了,定位为绝对定位,这样改变位置不会影响其他元素的位置。宽高都是149px。注意了,我们还设置了box-shadow:1px 1px 2px #777 ;
它还有边框阴影,所以149px 加上边框1px,它的总宽度是150px 下面的transition:0.3s是设置过渡时间,这是css3的属性,它会让属性改变呈现过渡动画,所以
当我们改变方块的位置时,它会有一个动画,我们不必自己编写动画函数,这回让你疯狂*/
#game img:hover{
color: red;
}
/*给方块设置鼠标悬停动画,当鼠标悬停在元素上面时,会用这里的属性替换上面的属性,移开后又会变为原来的,这里我们是把字体颜色改变*/
#control{
width: 150px;
height: 450px;
display: inline-block;
float: right;
}
/*控制区,display:inline-block会让元素呈现块状元素的特性,使得可以改变大小,同时也会具有行内元素的特性,使得不会占据一行空间,float:right让元素浮动到右边*/
#control rowspan{
height: 25px;
font-size: 20px;
color: #222;
margin-top: 10px;
font-family: KaiTi;
}
/*设置控制区按钮的共同样式*/
#start{
display: inline-block;
font-size: 28px;
width: 100px;
height: 28px;
background-color: #20a6fa;
color: #ffe171;
text-shadow: 1px 1px 2px #ffe171;
border-radius: 5px;
box-shadow: 2px 2px 5px #4c98f5;
text-align: center;
cursor: pointer;
}
/*给start按钮设置属性。cursor:pointer属性让鼠标移到元素上面时会显示不同的鼠标形状,pointer是手型*/
#reset{
display: inline-block;
font-size: 28px;
width: 100px;
height: 28px;
background-color: #20a6fa;
color: #ffe171;
text-shadow: 1px 1px 2px #ffe171;/*字体阴影*/
border-radius: 5px;/*圆角属性*/
box-shadow: 2px 2px 5px #4c98f5;/*盒子阴影*/
text-align: center;/*文字居中*/
cursor: pointer;
}
/*给Reset按钮设置属性*/
#d1{
left: 0px;
}
#d2{
left: 150px;
}
#d3{
left: 300px;
}
#d4{
top: 150px;
}
#d5{
top: 150px;
left: 150px;
}
#d6{
top: 150px;
left: 300px;
}
#d7{
top: 300px;
}
#d8{
left: 150px;
top: 300px;
}
/*这是预先给每个小方块按照顺序排好位置*/
#result{
font-family:"Times New Roman";
font-size: 30px;
text-align:center;
color: red;
}
</style>
</head>
<body>
<h1 style="text-align: center;">华容道游戏</h1>
<h2 id="result">
</h2>
<div id="container">
<!--最外面的DIV,用来包含里面的结构-->
<!--游戏区,大DIV方块-->
<div id="game">
<img src="1.png" id="d1" width="150" height="150" onclick="move(1)"></img>
<img src="2.png" id="d2" width="150" height="150" onclick="move(2)"></img>
<img src="3.png" id="d3" width="150" height="150" onclick="move(3)"></img>
<img src="4.png" id="d4" width="150" height="150" onclick="move(4)"></img>
<img src="5.png" id="d5" width="150" height="150" onclick="move(5)"></img>
<img src="6.png" id="d6" width="150" height="150" onclick="move(6)"></img>
<img src="7.png" id="d7" width="150" height="150" onclick="move(7)"></img>
<img src="8.png" id="d8" width="150" height="150" onclick="move(8)"></img>
<!-- <img src="D:\py\image\X_9.jpg" id="d9" width="150" height="150"> -->
</div>
<div id="control">
<!--游戏控制区-->
<p>
<img src="A.jpg" width="100" height="100"><br>
<rowspan id="timeText">总用时</rowspan>
<rowspan id="timer"></rowspan><br>
<rowspan id="walk"><b> 步数</b></rowspan>
<rowspan id="count"></rowspan><br>
<rowspan id="walk_min"><b>最短步数</b></rowspan>
<rowspan id="count_min"></rowspan>
</p>
<!--显示游戏时间区域-->
<p>
<rowspan id="start" onclick="start()">开始</rowspan>
<rowspan id="reset" onclick="reset()">重来</rowspan>
</p>
<!--显示控制按钮区域-->
</div>
</div>
<script>
var count_min=99999;
var conts=0; //有没有成功过
if(count_min==99999){
document.getElementById("count_min").innerHTML="无记录";
}
var count=0;//保存走过的步数
var time=0; //保存定时时间
var pause=true; //设置是否暂停标志,true表示暂停
var set_timer; //设置定时函数
var d=new Array(10); //保存大DIV当前装的小DIV的编号
var d_direct=new Array(
[0],//为了逻辑更简单,第一个元素我们不用,我们从下标1开始使用
[2,4],//大DIV编号为1的DIV可以去的位置,比如第一块可以去2,4号位置
[1,3,5],
[2,6],
[1,5,7],
[2,4,6,8],
[3,5,9],
[4,8],
[5,7,9],
[6,8]
); //保存大DIV编号的可移动位置编号
var d_posXY=new Array(
[0],//同样,我们不使用第一个元素
[0,0],//第一个表示left,第二个表示top,比如第一块的位置为let:0px,top:0px
[150,0],
[300,0],
[0,150],
[150,150],
[300,150],
[0,300],
[150,300],
[300,300]
); //大DIV编号的位置
d[1]=1;d[2]=2;d[3]=3;d[4]=4;d[5]=5;d[6]=6;d[7]=7;d[8]=8;d[9]=0; //默认按照顺序排好,大DIV第九块没有,所以为0,我们用0表示空白块
function move(id){
//移动函数,前面我们已将讲了
var i=1;
for(i=1; i<10; ++i){
if( d[i] == id )
break;
}
//这个for循环是找出小DIV在大DIV中的位置
var target_d=0;
//保存小DIV可以去的编号,0表示不能移动
target_d=whereCanTo(i);
//用来找出小DIV可以去的位置,如果返回0,表示不能移动,如果可以移动,则返回可以去的位置编号
if( target_d != 0){
d[i]=0;
//把当前的大DIV编号设置为0,因为当前小DIV已经移走了,所以当前大DIV就没有装小DIV了
d[target_d]=id;
//把目标大DIV设置为被点击的小DIV的编号
document.getElementById("d"+id).style.left=d_posXY[target_d][0]+"px";
document.getElementById("d"+id).style.top=d_posXY[target_d][1]+"px";
//最后设置被点击的小DIV的位置,把它移到目标大DIV的位置
count+=1
document.getElementById("count").innerHTML=count;
}
//如果target_d不为0,则表示可以移动,且target_d就是小DIV要去的大DIV的位置编号
var finish_flag=true;
//设置游戏是否完成标志,true表示完成
for(var k=1; k<9; ++k){
if( d[k] != k){
finish_flag=false;
break;
//如果大DIV保存的编号和它本身的编号不同,则表示还不是全部按照顺序排的,那么设置为false,跳出循环,后面不用再判断了,因为只要一个不符,就没完成游戏
}
}
//从1开始,把每个大DIV保存的编号遍历一下,判断是否完成
if(finish_flag==true){
if(!pause)
start();
if(count_min>count){
document.getElementById("count_min").innerHTML=count;
count_min=count;
}
if(count>count_min){
document.getElementById("count_min").innerHTML=count_min;
}
document.getElementById("result").innerHTML="(*//ω\*)Congratulation!!!!!!!!";
<!-- alert("congratulation");-->
}
//如果为true,则表示游戏完成,如果当前没有暂停,则调用暂停韩式,并且弹出提示框,完成游戏。
//start()这个函数是开始,暂停一起的函数,如果暂停,调用后会开始,如果开始,则调用后会暂停
}
function whereCanTo(cur_div){
//判断是否可移动函数,参数是大DIV的编号,不是小DIV的编号,因为小DIV编号跟可以去哪没关系,小DIV是会动的
var j=0;
var move_flag=false;
for(j=0; j<d_direct[cur_div].length; ++j){
//把所有可能去的位置循环遍历一下
if( d[ d_direct[cur_div][j] ] == 0 ){
move_flag=true;
break;
}
//如果目标的值为0,说明目标位置没有装小DIV,则可以移动,跳出循环
}
if(move_flag == true){
return d_direct[cur_div][j];
}else{
return 0;
}
//可以移动,则返回目标位置的编号,否则返回0,表示不可移动
}
//定时函数,每一秒执行一次
function timer(){
time+=1;//一秒钟加一,单位是秒
var min=parseInt(time/60);//把秒转换为分钟,一分钟60秒,取商就是分钟
var sec=time%60;//取余就是秒
document.getElementById("timer").innerHTML=min+"分"+sec+"秒";//然后把时间更新显示出来
}
//开始暂停函数
function start(){
if(pause){
document.getElementById("start").innerHTML="暂停";//把按钮文字设置为暂停
pause=false;//暂停表示设置为false
set_timer=setInterval(timer,1000);//启动定时
//如果当前是暂停,则开始
}else{
document.getElementById("start").innerHTML="开始";
pause=true;
clearInterval(set_timer);
}
}
//重置函数
function reset(){
count=0;
time=0;//把时间设置为0
random_d();//把方块随机打乱函数
if(pause)//如果暂停,则开始计时
start();
document.getElementById("result").innerHTML="";
document.getElementById("count").innerHTML=0;
if(count_min=99999)
if(finish_flag==true)//这样点重来就不会没有上次的记录
document.getElementById("count_min").innerHTML="无记录";
if(count_min!=99999)
count_min=count_min;
<!-- else-->
<!-- document.getElementById("count_min").innerHTML=count_min;-->
}
//随机打乱方块函数,我们的思路是从第九块开始,随机生成一个数,然后他们两块对调一下
function random_d(){
for(var i=9; i>1; --i){
var to=parseInt(Math.random()*(i-1)+1);//产生随机数,范围为1到i,不能超出范围,因为没这个id的DIV
if(d[i]!=0){
document.getElementById("d"+d[i]).style.left=d_posXY[to][0]+"px";
document.getElementById("d"+d[i]).style.top=d_posXY[to][1]+"px";
}
//把当前的DIV位置设置为随机产生的DIV的位置
if(d[to]!=0){
document.getElementById("d"+d[to]).style.left=d_posXY[i][0]+"px";
document.getElementById("d"+d[to]).style.top=d_posXY[i][1]+"px";
}
//把随机产生的DIV的位置设置为当前的DIV的位置
var tem=d[to];
d[to]=d[i];
d[i]=tem;
//然后把它们两个的DIV保存的编号对调一下
}
}
//初始化函数,页面加载的时候调用重置函数,重新开始
window.onload=function(){
reset();
}
</script>
</body>
</html>
2.原型开发工具
采用的是Axure来设计模型,尽量把拼图中所有涉及到的功能全都实现了一遍。
3.结对过程
在最开始的分工时,我们抱着一起做所有事情的心态去学习所需要用到的东西,包括pygame,html,css,js等等,后来发现时间赶不上才开始定下个人的小目标。虽然在此之前,起霖用pygame实现了拼图过程,但是后面了解到pygame实现不了网页的接口,于是我们又再次写了一份html的拼图游戏。最后在起霖的不断努力下,最终完成了AI大比拼中的算法实现。
4.困难与解决方法
我在最开始的html和js相结合的地方有许多疑惑的地方,后来请教了楚哥,霖哥等等大佬后,最终才完成了这一份html的代码块。中途在实现最佳成绩的函数时也遇到了想不到的bug,也是在其他人的帮助下得以解决。
5.两人讨论细化的照片
6.动态展示
AI与原型设计实现
1.代码实现思路
2.相关算法以及代码
最核心的A*算法原理:
1.把起点加入 open表。
2.重复如下过程:
a. 遍历 open表,查找 F 值最小的节点,把它作为当前要处理的节点。
b. 把这个节点移到 close表。
c. 对当前方格的 4个方向相邻方格进行操作:
◆ 如果它是不可抵达的或者它在 close表中,忽略它。
◆ 如果它不在 open表中,把它加入 open表,并且把当前方格设置为它的父亲,记录该方格的 F,G 和H值。
◆ 如果它已经在 open表中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 H值作参考。更小的H值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 、H和 F 值。
d. 判断是否出现以下情况,是就停止跳出循环:
◆ 把终点加入到了 open表中,此时路径已经找到了,或者
◆ 查找终点失败,并且 open表是空的,此时没有路径。
3.保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。
代码如下:
def run_Axing():
global swap
global move
#before:保存父节点的列表
global before
global cost
global openList
global closeList
before = [0]
cost = [0]
openList = [[]]
closeList = []
#从image_compare获取被扣去的格子
blank = image_compare.bk
li_goal = list(range(1, 9 + 1))
# entry_num=int(entry_num)
#目标九宫格
li_goal[blank - 1] = 0
#print(li_goal)
#起始状态
li0 = image_compare.array
#print('li0', li0)
swap = []
move = [-3, 3, -1, 1]
maxcount=10000+81
# 1. 把起始格添加到开启列表。
openList[0]=li0[:]
#print('openlist[0]', openList[0])
cost[0]=H_cost(li_goal,openList[0])
#print('cost[0]',cost[0])
before[0]=0
Gn = 0
# add_open(st[0])
# 2.重复如下的工作:
while len(closeList)<10000:
# a) 寻找开启列表中H值最低的格子。我们称它为当前格。
mc = cost.index(min(cost))
if cmp(openList[mc][:],li_goal[:])==0:
return mc
x = openList[mc].index(0) + 1
# b) 把它切换到关闭列表。
closeList.append(openList[mc][:])
cost[mc]=maxcount
# del openList[mc]
# del cost[mc]
# c) 对相邻的4格中的每一个进行以下操作
for i in range(0, 4):
if bool_rules(x, x + move[i]):
temp = turn(x, x + move[i], openList[mc][:])[:]
if closeList.count([temp]) == 0: # 如果它不可通过或者已经在关闭列表中,略过它。
if openList.count([temp]) == 0:
openList.append(temp) # 如果它不在开启列表中,把它添加进去。把当前格作为这一格的父节点。记录这一格的H值。
c = H_cost(li_goal, openList[-1])+Gn
cost.append(c)
before.append(mc)
# 如果它已经在开启列表中,用H值为参考检查新的路径是否更好。
# 更低的H值意味着更好的路径。如果是这样,就把这一格的父节点改成当前格,
# 并且重新计算这一格的H值。
else:
t = openList.index(temp)
c = H_cost(li_goal, openList[t])+Gn
if c < cost[t]:
cost[t]=c
before[t] = mc
Gn += 1
#print(before)
return -1
结果传回run(),如果寻路有解,则我用step存储每一步变化的结果,从openList中获得。
def run():
global zero_location
global way
global swap
swap = []
way = str()
mc = run_Axing()
#step存储每一步变化完的九宫格
global step
step = []
if mc > -1:
zero_location = []
num = 0
step = [openList[mc]]
new_list = []
while mc > 0:
mc = before[mc]
step.append(openList[mc])
num += 1
#print('次数:', num)
for i in range(0, num + 1):
#print('step', i)
b = 0
list1 = []
for j in range(0, 3):
list1 = list1 + (step[num - i][j * 3:j * 3 + 3])
#print(step[num - i][j * 3:j * 3 + 3])
#print("list1", list1)
b = get_zero_location(list1)
zero_location.append(b)
way = get_zero_path(zero_location)
else:
print('ERROR!')
#print(way)
#print('finsh')
return way,swap
对于空白格的移动,我写了get_zero_location和get_zero_path两个函数来分别获取每步空白格的位置以及空白格移动的方向。最后路径存储在way中返回给Post_Answer作为答案上传
def get_zero_path(list):
global zero_path
zero_path = str()
index = 0
while index < len(list)-1:
if list[index] - list[index+1] == 3:
zero_path+=str('w')
elif list[index] - list[index+1] == 1:
zero_path+=str('a')
elif list[index] - list[index + 1] == -3:
zero_path+= str('s')
elif list[index] - list[index + 1] == -1:
zero_path+=str('d')
index += 1
return zero_path
def get_zero_location(list):
a = 0
for i in list:
if i == 0:
a = list.index(i)+1
print(a)
return a
3.性能分析与改进
只能说好不容易写出个算法勉强能用已经是我的极限了,毕竟也是借鉴网上的代码,在性能改进上自己还是做不了啥的。
4.性能分析图
AI_Test中性能消耗最大的函数就是run()和run Aixing.
5.对队友的评价
起霖在AI算法的实现上想了很多也实现了很多,特别在是后来的接口和打榜,都是起霖一人扛起来这份大旗,虽然最后的结果不是很好,但是对我们来说,已经是很不错的成绩了。
6.PSP和学习进度条
何文龙
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(h) | 累计学习耗时(h) | 重要成长 |
---|---|---|---|---|---|
1 | 15 | 15 | 10 | 10 | 了解前端和后端的基本特性 |
2 | 64 | 79 | 24 | 学习并练习html,js,css语句 | |
3 | 251 | 320 | 16 | 40 | 继续学习html,js,css的写法,练习Axure的使用 |
4 | 49 | 369 | 20 | 60 | 将js和css嵌入html当中(实现Axure) |
5 | 15 | 384 | 20 | 80 | 最终实现游戏的页面 |
吴起霖
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(h) | 累计学习耗时(h) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 2 | 2 | 了解题目需求 |
2 | 250 | 250 | 20 | 22 | 用pygame实现了一个九宫格拼图游戏 |
3 | 150 | 400 | 22 | 44 | 学习有关接口调用的相关知识,发现与自己上的爬虫课差不多,尝试了解AI测试 |
4 | 200 | 600 | 20 | 60 | 了解AI大比拼的相关需求,寻找可用算法,学习A*算法 |
5 | 300 | 900 | 20 | 80 | 实现A*算法,学会对接口得到的图片进行分割以及对比 |
PSP(二人合计) | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 40 | 30 |
Estimate | 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 1500 | 1400 |
Analysis | 需求分析 (包括学习新技术) | 600 | 450 |
Design Spec | 生成设计文档 | 30 | 30 |
Design Review | 设计复审 | 30 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 60 | 30 |
Design | 具体设计 | 30 | 30 |
Coding | 具体编码 | 700 | 500 |
Code Review | 代码复审 | 90 | 80 |
Test | 测试(自我测试,修改代码,提交修改) | 150 | 150 |
Reporting | 报告 | 170 | 150 |
Test Repor | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 20 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 30 |
Total | 合计 | 3530 | 285 |