1. 程式人生 > >字元版本貪吃蛇遊戲設計及演算法

字元版本貪吃蛇遊戲設計及演算法

貪吃蛇,是一款經典的益智遊戲。我們可以通過演算法捕捉和傳遞智慧,設計出能自動跑著吃食物的智慧蛇。

有不少人對此研究,設計出了很棒的演算法,如圖:

貪吃蛇演算法

歸納一些比較高階的演算法為三類:
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 。身為同學,不免自慚形穢。