看似简单的AI贪吃蛇其实用的不是类似我们写的low比代码,它用了许多高级的算法来保证能够足够“智能”,比如A星算法,
下面内容版权属于百度:
A*(A-Star)算法是一种静态路网中求解最短路最有效的直接搜索方法,也是许多其他问题的常用启发式算法。注意——是最有效的直接搜索算法,之后涌现了很多预处理算法(如ALT,CH,HL等等),在线查询效率是A*算法的数千甚至上万倍。
公式表示为: f(n)=g(n)+h(n),
其中, f(n) 是从初始状态经由状态n到目标状态的代价估计,
g(n) 是在状态空间中从初始状态到状态n的实际代价,
h(n) 是从状态n到目标状态的最佳路径的估计代价。
(对于路径搜索问题,状态就是图中的节点,代价就是距离)
h(n)的选取
保证找到最短路径(最优解的)条件,关键在于估价函数f(n)的选取(或者说h(n)的选取)。
我们以d(n)表达状态n到目标状态的距离,那么h(n)的选取大致有如下三种情况:
如果h(n)< d(n)到目标状态的实际距离,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。
如果 h(n)>d(n),搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
但是代码写起来相当复杂,而如果用PML老师的代码会发生许多有趣的事
1.蛇会绕路:
因为“智能”蛇会沿着最短距离爬,然后它并不知道障碍物在那,或者它知道但是无动于衷,然后它走到“¥”面前发现有堵长墙,于是GG,重新沿着长墙慢慢爬
2.蛇会转圈:
因为PML老师的奇妙算法每次相同的输入都有相同的输出,然后就会发现蛇每次绕着圈圈转,于是GG
3.蛇会选择优秀走法(XJB走)
例如
强行撞墙
GG
其实设计的时候思路是大家都能想到的,但是在设计游戏这个整块的项目的时候这种感觉往往都不全面,有很多情况和边界如果用讨论就会很麻烦,所以感觉选正确的方法算是比较关键的。谈到这个项目我觉得最重要的还是让我们感受到编程是件很严谨的事,而且选对方法比debug能力更加可贵。
其实有一些问题是可以解决的,比如,绕圈这件事,我们只需要每次设一个随机数让随机走就好了,但是总感觉像在打补丁,无论怎么样,这个项目终于结束了。
最后……字数不够代码凑了(打代码也是写博客一部分吧):-)
//SNAKE.c(手动贪吃蛇)
#include
#include
#include
#define SNAKE_MAX_LENGTH 20
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define SNAKE_FOOD '$'
#define WALL_CELL '*'
//snake stepping: dy = -1(up),1(down);dx = -1(left),1(right),0(no move)
void snakeMove(int,int);
//put a food randomized on a blank cell
void put_money(void);
//out cells of the grid
void output(void);
//outs when gameover
void gameover(void);
char map[12][12]=
{"************",
"*XXXXH *",
"* *",
"* *",
"* *",
"* *",
"* *",
"* *",
"* *",
"* *",
"* *",
"************",};
//define vars for snake, notice name of vars in C
int snakeX[SNAKE_MAX_LENGTH]={1,2,3,4,5};
int snakeY[SNAKE_MAX_LENGTH]={1,1,1,1,1};
int snakeLength = 5;
int judRand(int random1,int random2){
int i;
for(i=0;i<=snakeLength-1;i++){
if ((random1 == snakeX[i] && random2 == snakeY[i])) return 1;
}return 0;
}
void gameover(void) {
printf("%s\n","Game Over!!!" );
}
void put_money(void){//放置新的SNAKE_FOOD
int randomX=rand()%10+1,randomY=rand()%10+1;
while (judRand(randomX,randomY)){
randomX=rand()%10+1;randomY=rand()%10+1;
}
map[randomX][randomY]=SNAKE_FOOD;
}
void output(void){ //输出矩阵
int i,j;
for(i=0;i<12;i++){
for(j=0;j<12;j++){
printf("%c ",map[i][j]);
} printf("\n");
}
}
void snakeMove(int x,int y){//描述snake的运动模式
int i;
if (map[snakeY[snakeLength-1]+y][snakeX[snakeLength-1]+x] != SNAKE_FOOD){
if ((snakeY[snakeLength-1]+y) != snakeY[snakeLength-2] || (snakeX[snakeLength-1]+x) != snakeX[snakeLength-2] ){
map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=BLANK_CELL;
for(i=0;i<=snakeLength-2;i++){
if (i == 0)
map[snakeY[i]][snakeX[i]]=BLANK_CELL;
snakeX[i]=snakeX[i+1];
snakeY[i]=snakeY[i+1];
map[snakeY[i]][snakeX[i]]=SNAKE_BODY;
}
snakeX[snakeLength-1] += x;
snakeY[snakeLength-1] += y;
map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=SNAKE_HEAD;
}
}else{
map[snakeY[snakeLength-1]+y][snakeX[snakeLength-1]+x] = SNAKE_HEAD;
snakeY[snakeLength]=snakeY[snakeLength-1]+y;
snakeX[snakeLength]=snakeX[snakeLength-1]+x;
map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=SNAKE_BODY;
snakeLength++;
put_money();
}
output();
}
int jud(){
//判断snake是否SNAKE_HEAD撞到SNAKE_BODY
int i;
for (i=0;i<=snakeLength-2;i++){
if (snakeX[i] == snakeX[snakeLength-1] && snakeY[i] == snakeY[snakeLength-1])
return 1;
}
return 0;
}
int main(){
int i,j;
output();
put_money();
while(snakeX[snakeLength-1]!=0&&snakeX[snakeLength-1]!=11&&snakeY[snakeLength-1]!=0&&snakeY[snakeLength-1]!=11 && !jud()){
char ch;
scanf("%c", &ch);
switch (ch) {
case 'A':
system("cls");
snakeMove(-1,0);
break;
case 'D':
system("cls");
snakeMove(1,0);
break;
case 'W':
system("cls");
snakeMove(0,-1);
break;
case 'S':
system("cls");
snakeMove(0,1);
break;
}
}
gameover();
return 0;
}