C語言實現貪吃蛇遊戲設計
阿新 • • 發佈:2020-07-17
C語言實現貪吃蛇,供大家參考,具體內容如下
實驗平臺:DEV C++
/******************************************************************************** *File name:SnakeGame3.0.c *Description:貪吃蛇遊戲原始碼(C語言),採用 *寬度優先演算法,計算蛇到食物的最短路徑(時間複雜度n^3空間複雜度n^2),這個演算法遇 * *到自身圍困情況將失效,無法計算出最短路徑 * *********************************************************************************/ #include<stdio.h> #include<stdlib.h> #include<Windows.h> #include<time.h> #include<math.h> #include<conio.h> #define SIZE 25 //定義地圖大小 #define MAX_LENGTH 19 * 19 //定義蛇的最大長度 typedef struct point //地圖上的點的節點 { int r; int c; } point; typedef struct queue //迭代搜尋最短路徑用到的佇列 { point * body[5 * SIZE]; //儲存蛇的身體的陣列(棧的深度最大為5 * SIZE) int num; //記錄佇列中節點數 int first_in_pos; //第一個進入佇列的元素的索引值 } queue; HANDLE stdOutput; //宣告windows標準輸出控制代碼 void init(int * length,point * foodAt,int * dir,point body[],char map[][SIZE]); //初始化 int getDir(int dir); //獲取蛇的行進方向 int getAIDir(int dir,int length,point foodAt); //獲取AI判斷得出的行進方向 int moveable(point moveTo,point body[]); int move(point foodAt,int dir,point body[]); //蛇的運動 void draw(int length,point foodAt,char map[][SIZE]); //畫圖 void food(point * foodAt,char map[][SIZE]); //生成食物 //棧相關的操作 point * pop(queue *queue); //從列隊取出最先進入的點,返回取出點的指標,取出失敗返回NULL void push(point *point,queue *queue); //推入列隊中 int main() { char map[SIZE][SIZE]; //定義地圖 point body[MAX_LENGTH],foodAt; //整個蛇身體和食物的所在點(body陣列的第一個值為蛇頭) int length; //蛇的實際長度 int dir; //行進方向 int rate = 1; //行進速率 int result; //儲存蛇運動的結果:【死亡】、【得分】、【無】 init(&length,&foodAt,&dir,body,map); //初始化 while (1) { Sleep(100 / rate); //dir = getDir(dir); //獲取蛇的行進方向 dir = getAIDir(dir,length,foodAt); //獲取AI判斷得出的行進方向 result = move(foodAt,dir,body); //蛇的運動 if (result == 1) //如果吃到食物 { length++; rate = length / 3; if (length == MAX_LENGTH) { printf("您已通關!"); break; } food(&foodAt,map); //生成食物 } draw(length,foodAt,map); //畫圖 if (result == -1) //如果死亡 { break; } } Sleep(500); printf(" 失敗,此次得分為%d ",(length - 3) * 100); system("pause"); } void init(int * length,char map[][SIZE]) //初始化 { memset(map,'*',SIZE * SIZE); //初始化地圖 body[0].r = 3,body[0].c = 2; //初始化蛇的身體 body[1].r = 2,body[1].c = 2; body[2].r = 1,body[2].c = 2; *length = 3; //初始長度為3 *dir = 2; //初始方向向下 food(foodAt,*length,map); //生成食物 draw(*length,*foodAt,map); //畫圖 printf(" 按下任意鍵開始,用ASDW控制方向,ESC暫停\n"); _getch(); srand((unsigned)time(NULL)); //生成隨機數種子,備用 stdOutput = GetStdHandle(STD_OUTPUT_HANDLE); //獲取標準輸出控制代碼 CONSOLE_CURSOR_INFO cci; cci.bVisible = 0; cci.dwSize = 1; SetConsoleCursorInfo(stdOutput,&cci); COORD coord = { 0,SIZE * 2 + 3 }; } int getDir(int dir) //獲取蛇的行進方向,規定返回值0代表向上,1代表向右,2代表向下,3代表向左 { char key; int newDir = dir; if (_kbhit()) { key = _getch(); switch (key) { case 'A': case 'a': newDir = 3; break; case 'S': case 's': newDir = 2; break; case 'D': case 'd': newDir = 1; break; case 'W': case 'w': newDir = 0; break; case 27: _getch(); break; } } if (newDir - dir == 2 || newDir - dir == -2) //蛇不能反向 { newDir = dir; } return newDir; } int getAIDir(int dir,point foodAt) //獲取AI判斷得出的行進方向 { static int *shortestPathDir,count = 0; //儲存最短路徑的方向(方向的先後順序為倒序,即排在後面的方向先走) if (count == 0) //如果最短路徑還沒生成,那麼重新生成 { int map_of_steps[SIZE][SIZE]; //儲存到達地圖上某一點的最小步數 queue queue = { 0,0 }; point *last_body = (point *)malloc(length * sizeof(point)); //儲存計算過程中上次蛇的身體位置 point *next_body; //儲存下一次蛇的身體 point next_point; int i,step = 0; point moveTo; memcpy(last_body,length * sizeof(point)); memset(map_of_steps,SIZE * SIZE * sizeof(int)); //向佇列中放入初始body push(last_body,&queue); push(NULL,&queue); //插入NULL來標識寬度優先搜尋的某一層的結束 step++; //用step來表示步數,也代表層數 while (queue.num != 0) { last_body = pop(&queue); if (last_body == NULL) //如果某一層結束 { if (queue.num != 0) //如果還有下一層的元素 { step++; push(NULL,&queue); //插入下一層的結束標誌 continue; } else { break; } } for (i = 0; i < 4; i++) //分別檢測四個方向能否移動 { switch (i) { case 0: moveTo.r = last_body[0].r - 1,moveTo.c = last_body[0].c; break; case 1: moveTo.r = last_body[0].r,moveTo.c = last_body[0].c + 1; break; case 2: moveTo.r = last_body[0].r + 1,moveTo.c = last_body[0].c; break; case 3: moveTo.r = last_body[0].r,moveTo.c = last_body[0].c - 1; break; } if (moveable(moveTo,last_body) && map_of_steps[moveTo.r][moveTo.c] == 0) //如果移向的點之前沒有移到過 //(即當前路徑是到該點的最短路徑),而且該點是moveable的 { map_of_steps[moveTo.r][moveTo.c] = step; if (moveTo.r == foodAt.r && moveTo.c == foodAt.c) //如果下一步就可以到達食物所在點 { //先free一些沒用的動態記憶體 free(last_body); while (queue.num != 0) { free(pop(&queue)); } goto outer; //跳出迴圈 } //生成next_body並將其推入佇列 next_body = (point *)malloc(length * sizeof(point)); for (i = length - 1; i > 0; i--) //移動蛇的位置 { next_body[i] = body[i - 1]; } next_body[0] = moveTo; //換一個頭 push(next_body,&queue);//推入佇列 } } //free一些沒用的動態記憶體 free(last_body); } outer:; if (map_of_steps[foodAt.r][foodAt.c] == 0) //如果無法到達食物所在點,那麼按原路走,直到死亡 { return dir; } //生成shortestPath shortestPathDir = (int *)malloc(step * sizeof(int)); count = step; next_point = foodAt; for (i = 0; i < step - 1; i++) //利用map_of_steps和下一個點推知到上一個點到下一個點的方向dir { if (next_point.r + 1 < SIZE && map_of_steps[next_point.r][next_point.c] == map_of_steps[next_point.r + 1][next_point.c] + 1) { shortestPathDir[i] = 0; next_point.r += 1; } else if (next_point.c - 1 >= 0 && map_of_steps[next_point.r][next_point.c] == map_of_steps[next_point.r][next_point.c - 1] + 1) { shortestPathDir[i] = 1; next_point.c -= 1; } else if (next_point.r - 1 >= 0 && map_of_steps[next_point.r][next_point.c] == map_of_steps[next_point.r - 1][next_point.c] + 1) { shortestPathDir[i] = 2; next_point.r -= 1; } else { shortestPathDir[i] = 3; next_point.c += 1; } } //第一步要單獨判斷(因為map_of_steps的值為0的點可能是蛇頭,也可能是蛇身,這樣會對蛇第一步方向判斷產生干擾) if (body[0].r > next_point.r) { shortestPathDir[step - 1] = 0; } else if (body[0].r < next_point.r) { shortestPathDir[step - 1] = 2; } else if (body[0].c > next_point.c) { shortestPathDir[step - 1] = 3; } else { shortestPathDir[step - 1] = 1; } /*printf("\n\n\n"); int j; for (i = 0; i < SIZE; i++) { for (j = 0; j < SIZE; j++) { printf("%3d",map_of_steps[i][j]); } printf("\n"); } printf("\n");*/ } //沿著最短路徑走 return shortestPathDir[--count]; } int moveable(point moveTo,point body[]) //判斷是否可以移動到moveTo點,能1,不能0 { int i; for (i = 0; i < length - 1; i++) { if (moveTo.r == body[i].r && moveTo.c == body[i].c) { return 0; } } if (moveTo.r < 0 || moveTo.r >= SIZE || moveTo.c < 0 || moveTo.c >= SIZE) //如果超出邊界 { return 0; } return 1; } int move(point foodAt,point body[]) //蛇的運動,規定返回值-1代表死亡,0代表沒有吃到食物,1代表吃到食物 { int i,flag = 0; point head = body[0]; switch (dir) { case 0: head.r -= 1; break; case 1: head.c += 1; break; case 2: head.r += 1; break; case 3: head.c -= 1; break; } if (head.r < 0 || head.r >= SIZE || head.c < 0 || head.c >= SIZE) //出界了死亡 { return -1; } for (i = 0; i < length - 1; i++) { if (head.r == body[i].r && head.c == body[i].c) //咬到了自己死亡 { return -1; } } if (head.r == foodAt.r && head.c == foodAt.c) //吃到了食物 { length++; flag = 1; //標記一下,便與等下返回值為1 } for (i = length - 1; i > 0; i--) //移動蛇的位置 { body[i] = body[i - 1]; } body[0] = head; //換一個頭 if (flag == 1) { return 1; } return 0; } void draw(int length,char map[][SIZE]) //畫圖 { static char bitmap[SIZE + 2][SIZE + 2]; //定義一個數組,用於把地圖背景、邊界、蛇、食物都畫上去 int i,j; for (i = 0; i < SIZE; i++) //背景 { for (j = 0; j < SIZE; j++) { bitmap[i + 1][j + 1] = map[i][j]; } } //邊框 bitmap[0][0] = '0',bitmap[0][SIZE + 1] = '1'; bitmap[SIZE + 1][0] = '2',bitmap[SIZE + 1][SIZE + 1] = '3'; for (i = 0; i < SIZE; i++) { bitmap[0][i + 1] = '4',bitmap[SIZE + 1][i + 1] = '4'; bitmap[i + 1][0] = '5',bitmap[i + 1][SIZE + 1] = '5'; } bitmap[foodAt.r + 1][foodAt.c + 1] = 'f'; //食物 bitmap[body[0].r + 1][body[0].c + 1] = 'h'; //蛇頭 for (i = 1; i < length; i++) //蛇身 { bitmap[body[i].r + 1][body[i].c + 1] = 'b'; } COORD coord = { 0,0 }; //座標0,0 SetConsoleCursorPosition(stdOutput,coord); //把游標設定到0,0位置 for (i = 0; i < SIZE + 2; i++) { for (j = 0; j < SIZE + 2; j++) { switch (bitmap[i][j]) { case 'f': printf("★"); break; case 'b': printf("●"); break; case 'h': printf("○"); break; case '0': printf("┏"); break; case '1': printf("━┓"); break; case '2': printf("┗"); break; case '3': printf("━┛"); break; case '4': printf(" ━"); break; case '5': printf("┃ "); break; default: printf(" "); } } printf("\n"); } } void food(point * foodAt,char map[][SIZE]) //生成食物 { int i; while (1) { foodAt->r = rand() % SIZE,foodAt->c = rand() % SIZE; //隨機生成食物位置 for (i = 0; i < length; i++) { if (foodAt->r == body[i].r && foodAt->c == body[i].c) //如果該位置在蛇的身體上 { goto retry; } } break; retry:; } } //佇列相關的函式 point * pop(queue *queue) //從佇列中取出,返回取出點的指標 { queue->num--; if (queue->first_in_pos == 5 * SIZE - 1) //返回第一進入佇列的點,並將變數fist_in_pos改變 { queue->first_in_pos = 0; return queue->body[5 * SIZE - 1]; } else { return queue->body[queue->first_in_pos++]; } } void push(point * body,queue *queue) { if (queue->num == 0) //如果佇列已空 { queue->num++; queue->first_in_pos = 0; //將第一進入佇列的位置設為0 queue->body[queue->first_in_pos] = body; } else //否則插入佇列 { if (queue->first_in_pos + queue->num > 5 * SIZE - 1) { queue->body[queue->first_in_pos + queue->num++ - 5 * SIZE] = body; } else { queue->body[queue->first_in_pos + queue->num++] = body; } } }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。