字元版本貪吃蛇遊戲設計及演算法
阿新 • • 發佈:2019-01-27
貪吃蛇,是一款經典的益智遊戲。我們可以通過演算法捕捉和傳遞智慧,設計出能自動跑著吃食物的智慧蛇。
有不少人對此研究,設計出了很棒的演算法,如圖:
歸納一些比較高階的演算法為三類:
1.寬度優先搜尋最短路徑
2.走哈密頓迴路
3.特殊決策
這裡我主要介紹最簡單的貪婪演算法及整個遊戲的設計。
首先是自動尋路函式whereGoNext(void)。決策思想是走曼哈頓距離fabs(snakeX[0]-foody)+fabs(snakeY[0]-foodx)最小且不會死掉的方向。當然,這很不智慧,容易死掉,但容易實現。
char whereGoNext() {
char movable[4 ]= {'D','A','S','W'};
int min=9999,minDir;
for(int i=0; i<4; i++) {
snakeX[0]+=direx[i];
snakeY[0]+=direy[i];
if(!gameover()) {
int cost=fabs(snakeX[0]-foody)+fabs(snakeY[0]-foodx);
if(min>cost) {
min=cost;
minDir=i;
}
}
snakeX[0 ]-=direx[i];
snakeY[0]-=direy[i];
}
if(min<9999)
return movable[minDir];
else return 0;
}
接著是如何製作平滑的輸出介面。我的實現方法是:
每走一步
掩蓋蛇尾
輸出蛇頭
輸出蛇頸
完整的程式碼如下,供大家參考。程式碼不長,但實現了:
1.貪吃蛇每100ms行走一步;
2.平滑的輸出介面;
3.地圖中有一些設定的障礙物(牆),並且蛇會繞開障礙物;
4.貪婪演算法。
實驗環境:Ubuntu。
// snake_smart.cpp
// Kuncheng Xie 2017-12-26
// All rights reserved.
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<time.h>
#include<unistd.h>
#define SNAKE_MAX_LENGTH 150
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define SNAKE_FOOD '$'
#define WALL_CELL '*'
#define MAP_LENGTH 12
#define DELAY_TIME 100000 //delay 100ms
//move snake
void snakeMove(int dir);
//output cells of the grid
void show(void);
//output snake
void output(void);
//put a food randomized on a blank cell,1 for put food successfully,0 for fail
int putFood(void);
//judge whether gameover;0 for not over,1 for game over
int gameover(void);
//decide the smart snake's direction
char whereGoNext(void);
char map[MAP_LENGTH][MAP_LENGTH+1]= {//initial status
"************",
"*XXXXH *",
"* * *",
"* * *",
"* ** *",
"* * *",
"* *",
"* *",
"* *",
"* *",
"* *",
"************"
};
const int direx[4]= {0,0,1,-1};
const int direy[4]= {1,-1,0,0};
int snakeX[SNAKE_MAX_LENGTH]= {5,4,3,2,1};
int snakeY[SNAKE_MAX_LENGTH]= {1,1,1,1,1};
int snakeLength=5;
int foodx,foody,eatFood=1;
int main(void) {
char s;
srand(time(NULL));
printf("\033[2J");//clear the screen
printf("\033[1;1H");
show();
do {
s=whereGoNext();
if(s==0)break;
switch(s) {
case 'D':
snakeMove(0);
break;
case 'A':
snakeMove(1);
break;
case 'S':
snakeMove(2);
break;
case 'W':
snakeMove(3);
break;
}
output();
usleep(DELAY_TIME);
} while(!gameover());
printf("\033[13;1HGame over!!!\n");
return 0;
}
void snakeMove(int dir) {
//first clear the tail
printf("\033[%d;%dH%c\n",snakeY[snakeLength-1]+1,snakeX[snakeLength-1]+1,BLANK_CELL);
map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=BLANK_CELL;
if(snakeX[0]+direx[dir]==foody&&snakeY[0]+direy[dir]==foodx) {//eat food
eatFood=1;
snakeLength++;//increase length
}
for(int i=snakeLength-1; i>0; i--) {//body move
snakeX[i]=snakeX[i-1];
snakeY[i]=snakeY[i-1];
}
snakeX[0]+=direx[dir];//head move
snakeY[0]+=direy[dir];
}
void show() {
for(int i=0; i<MAP_LENGTH; i++) {//output map
for(int j=0; j<MAP_LENGTH; j++) {
printf("%c",map[i][j]);
}
printf("\n");
}
}
void output(void) {
printf("\033[%d;%dH%c\n",snakeY[0]+1,snakeX[0]+1,SNAKE_HEAD);//print snake
printf("\033[%d;%dH%c\n",snakeY[1]+1,snakeX[1]+1,SNAKE_BODY);
printf("\033[%d;%dH%c\n",snakeY[snakeLength-1]+1,snakeX[snakeLength-1]+1,SNAKE_BODY);
//must empty buffers: printf+'\n' or fflush(stdout);
map[snakeY[0]][snakeX[0]]=SNAKE_HEAD;
map[snakeY[snakeLength-1]][snakeX[snakeLength-1]]=SNAKE_BODY;
if(eatFood) {
eatFood=0;
if(putFood()) { //put food
printf("\033[%d;%dH%c\n",foodx+1,foody+1,SNAKE_FOOD);
}
} else {
printf("\033[%d;%dH%c\n",foodx+1,foody+1,SNAKE_FOOD);
}
printf("\033[13;1H\n");
}
int gameover(void) {
if(snakeLength>=(MAP_LENGTH-2)*(MAP_LENGTH-2))return 1;//the map is full
if(map[snakeY[0]][snakeX[0]]==WALL_CELL)return 1;//not touch wall
for(int j=1; j<snakeLength; j++) {//doesn't eat itself
if(snakeX[0]==snakeX[j]&&snakeY[0]==snakeY[j])return 1;
}
return 0;
}
int putFood(void) {
int foodMap[MAP_LENGTH*MAP_LENGTH][2],len,ran;
len=0;
for(int i=1; i<MAP_LENGTH-1; i++) {//put empty place into foodMap
for(int j=1; j<MAP_LENGTH-1; j++) {
if(map[i][j]==BLANK_CELL) {
foodMap[len][0]=i;
foodMap[len][1]=j;
len++;
}
}
}
if(len>1)ran=rand()%len;//place food randomly
else if(len==0)return 0;
else ran=0;
foodx=foodMap[ran][0];
foody=foodMap[ran][1];
return 1;
}
char whereGoNext() {
char movable[4]= {'D','A','S','W'};
int min=9999,minDir;
for(int i=0; i<4; i++) {
snakeX[0]+=direx[i];
snakeY[0]+=direy[i];
if(!gameover()) {
int cost=fabs(snakeX[0]-foody)+fabs(snakeY[0]-foodx);
if(min>cost) {
min=cost;
minDir=i;
}
}
snakeX[0]-=direx[i];
snakeY[0]-=direy[i];
}
if(min<9999)
return movable[minDir];
else return 0;
}
最終介面如圖:
這裡大力推薦一個同學的部落格:設計簡單的貪吃蛇AI 。身為同學,不免自慚形穢。