在有了上一篇博客中的普通贪吃蛇的基础后,我们来思考一下如何制作一个智能蛇。也就是说写出一个程序让这个贪吃蛇自己前进,自己寻找食物,但同时又要让它不要撞到墙壁或者自己的身体,并且不要让它太过于目光短浅,为了吃到当前食物而将自己困死。
首先我们来考虑一下最简单的情况:知道头和食物的坐标,怎样用最短路线走到食物。 这种情况确实特别简单,因为我们知道两点的坐标,通过比较横坐标大小就可以知道是向左走还是右走,比较纵坐标就可以知道是向上走还是下走。 而且适用这种算法的情况也很明确:只要头和食物坐标之间的区域什么都没有,我们就可以选择这么走。 代码如下:
int explorer(int x1,int y1,int x2,int y2)
{
int i,j,mid;
if(x2<x1) {
mid=x1;
x1=x2;
x2=mid;
}
if(y2<y1) {
mid=y1;
y1=y2;
y2=mid;
}
for(i=x1;i<=x2;i++) {
for(j=y1;j<=y2;j++) {
if(map[j][i]=='*')return 1;
}
}
return 0;
}
char AI(int x1,int y1,int x2,int y2,char direction)
{
int wide,length,noa=0,nos=0,nod=0,now=0,M;
char think;
if(explorer(x1,y1,x2,y2)==1)goto run;
wide=x1-x2;
if(wide<0)wide*=-1;
length=y1-y2;
if(length<0)length*=-1;
if(x1<x2)think='a';
if(x1>x2)think='d';
if(y1<y2)think='w';
if(y1>y2)think='s';
return think;
}
如果不是这种情况呢,其实大多数时候都不是这样的情况。这些时候,我们就要让这条蛇自己找路。但是找路不是漫无目的的乱走,即使在找路,也要尽可能的向食物的方向靠近,所以我引入了评分机制,也就是说对每个方向评分,用分数来评判那条路最好。
评分标准:
1.如果下一步走某个方向就会撞上障碍物,当然这条路是绝对不能走的,所以没有任何加分,也就是零分。
2.当然,很多时候有多个方向可走,那么给这些下一步可走的方向两分初始分。
3.有些时候走出下一步,结果所有方向都会被堵死,这样的方向是绝对不能走的,对与这些方向,即使有再多加分也直接清零。相当于这是对下一步的一种预判。
4.当蛇变长之后就会发现,如果蛇能够贴着自己身体走,比起满屏乱跑存活率要高的多。所以下一步如果能够贴着身体的话就再加一分。
5.当然在保证能够生存的前提下,我们应该尽可能的向食物靠近。比如食物在相对于蛇头在左上方,那么W、A两个方向的评分就会加一分。
6.以上五条评分方法后运行贪吃蛇发现,贪吃蛇的死法都是因为自己的身体围成了一个口袋形,然后这条傻蛇一头栽进去,一去不复返。观察发现,口袋形致死的很大程度原因因为蛇在进入口袋的时候把口袋入口给封死了。所以我在蛇的长度超过20之后加入一条评分,当该前进方向上第二格有障碍物时减一分。这样可以让蛇进入口袋时很大概率为自己留一条出路。
评分代码如下:
if(map[y2][x2-1]==' ') {
noa+=2;
if(x1<x2)noa++;
if((map[y2+1][x2-1]=='*')||(map[y2-1][x2-1]=='*'))noa++;
if((map[y2+1][x2-1]=='*')&&(map[y2-1][x2-1]=='*'))noa--;
if((map[y2][x2-2]=='*')&&(lenght>=20))noa--;
if((map[y2+1][x2-1]=='*')&&(map[y2-1][x2-1]=='*')&&(map[y2][x2-2]=='*'))noa=0;
}
if(map[y2][x2+1]==' ') {
nod+=2;
if(x2<x1)nod++;
if((map[y2+1][x2+1]=='*')||(map[y2-1][x2+1]=='*'))nod++;
if((map[y2+1][x2+1]=='*')&&(map[y2-1][x2+1]=='*'))nod--;
if((map[y2][x2+2]=='*')&&(lenght>=20))nod--;
if((map[y2+1][x2+1]=='*')&&(map[y2-1][x2+1]=='*')&&(map[y2][x2+2]=='*'))nod=0;
}
if(map[y2-1][x2]==' ') {
now+=2;
if(y1<y2)now++;
if((map[y2-1][x2+1]=='*')||(map[y2-1][x2-1]=='*'))now++;
if((map[y2-1][x2+1]=='*')&&(map[y2-1][x2-1]=='*'))now--;
if((map[y2-2][x2]=='*')&&(lenght>=20))now--;
if((map[y2-1][x2+1]=='*')&&(map[y2-1][x2-1]=='*')&&(map[y2-2][x2]=='*'))now=0;
}
if(map[y2+1][x2]==' ') {
nos+=2;
if(y2<y1)nos++;
if((map[y2+1][x2+1]=='*')||(map[y2+1][x2-1]=='*'))nos++;
if((map[y2+1][x2+1]=='*')&&(map[y2+1][x2-1]=='*'))nos--;
if((map[y2+2][x2]=='*')&&(lenght>=20))nos--;
if((map[y2+1][x2+1]=='*')&&(map[y2+1][x2-1]=='*')&&(map[y2+2][x2]=='*'))nos=0;
}
M=max(noa,nos,nod,now);
if(M==1)return 'a';
if(M==2)return 's';
if(M==3)return 'd';
if(M==4)return 'w';
在这种评分机制下的最高得分是64分,这样的评分机制并不能解决蛇的长度增加后将自己围死的情况。
因为做这个贪吃蛇只用了一晚上时间,这个评分方法肯定存在很多漏洞。比如每种情况究竟该分配多少分,以及还应该考虑多少情况。最大的问题就是如何让蛇“更有远见”。 我会继续思考这个问题,并改进贪吃蛇,也欢迎大家提出意见。