C語言單鏈表貪吃蛇小游戲
C語言實現單鏈表控制臺貪吃蛇小游戲,供大家參考。
編譯環(huán)境:vs2019
需求:
統(tǒng)計游戲開始后的時間,控制貪吃蛇;吃到食物蛇身加長,得分加一;碰墻或蛇頭碰到身體減一條生命;生命消耗完則結束游戲。
思路:
使用wasd鍵控制蛇的移動方向,蛇頭碰到食物得分加一,并在地圖上隨機產生一個食物,累加得分,碰墻或碰自己減一條生命,并初始化整條蛇,生命值為0時結束游戲。
做法:
使用單鏈表控制貪吃蛇移動的核心思想就是:鏈表存儲貪吃蛇所有坐標,每次循環(huán)貪吃蛇不斷向一個方向插入一個新的結點作為新的蛇頭,按下按鍵控制新蛇頭產生的位置,然后從新蛇頭處遍歷鏈表輸出蛇身到上一個蛇尾,清除上一個蛇尾的痕跡,并釋放相關結點。
每次向鏈表插入新節(jié)點后,判斷新節(jié)點的坐標是否和食物的坐標重合,如果重合本輪循環(huán)不釋放清除蛇尾結點,反之釋放清除上一個蛇尾的結點。
另外,在寫蛇生命相關代碼的時候,還需要注意一下哪些值應該初始化,哪些值不應該初始化。
只要明白了貪吃蛇運動的核心思想,整個程序其實就不難寫出來。
難點:
wsad控制貪吃蛇上下左右移動,并清除蛇尾。
說明:
使用單鏈表實現貪吃蛇的核心思想是我一開始沒有想到的,部分相關代碼我學習并借鑒了一些網絡上搜索到的代碼,如果有違反了相關版權協(xié)議,請告知我修改相關代碼。
注意:
由于編譯器原因程序中_kbhit()和_getch()函數可能在其他編譯器上編譯會出現錯誤,解決辦法是去掉函數前面的“_”。
運行效果:
代碼實現:
#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <time.h> #include <windows.h> void HideCursor(); //光標隱藏 void gotoxy(int x, int y); //光標定位 typedef struct snake { int x; int y; struct snake* next; }snake; #define WIDTH 100 //控制臺窗口寬度 #define HEIGHT 30 //控制臺窗口高度 #define SNAKEN 4 //貪吃蛇初始長度 #define LIFE 3 //初始生命次數 #define SPEED 200 //游戲速度、循環(huán)休眠時間 #define U 1 //使用宏代替需要數字代替的蛇的行動方向 #define D 2 //宏名含義是各方向英文單詞首字母 #define L 3 //蛇的狀態(tài),U:上 ;D:下;L:左 R:右 #define R 4 void dtxxcsh() //輸出地圖 { for (int i = 1; i < WIDTH-1; i++) //輸出上下面墻 { gotoxy(i, 26); printf("-"); gotoxy(i, 0); printf("-"); } for (int i = 0; i < HEIGHT-3; i++) //輸出左右兩面墻 { gotoxy(0, i); printf("|"); gotoxy(99, i); printf("|"); } gotoxy(24, 28); printf("得分: 0 生命: %d 時間: 0 ",LIFE); //xy 30,28可用得分數值 14個空格 } int foodx, foody; //食物位置坐標 void sjcsswhs() //隨機產生一個食物 { srand(time(NULL)); foodx = rand() % (WIDTH - 4) + 2; //使用宏運算隨機數最大值需要加括號 while (foodx % 2) //如果食物的x坐標不是偶數,再獲取一個x坐標 { foodx = rand() % (WIDTH - 4) + 2; //寬度 } foody = rand() % (HEIGHT - 7) + 3; //高度 gotoxy(foodx, foody); printf("★"); } snake* head; //蛇頭指針 void cshs() //初始化蛇的位置 { snake *tail; //蛇尾指針 int i; tail = (snake*)malloc(sizeof(snake)); tail->next = NULL; tail->x = HEIGHT-6; tail->y = 8; //貪吃蛇初始長度5 SNAKEN for (i = 1; i <= SNAKEN; i++) //在蛇尾處創(chuàng)建鏈表 { head = (snake*)malloc(sizeof(snake)); head->next = tail; head->x = 24 + i * 2; //head->x這個數必須為偶數,和食物坐標偶數對應 head->y = 8; tail = head; //此時蛇尾指針指向蛇頭 } while (tail) { gotoxy(tail->x, tail->y); printf("■"); tail = tail->next; } } int status = R; //蛇前進狀態(tài) snake* p = NULL; //工作指針 snake* nexthead; //下一個蛇頭 int score = 0; //得分 void snakemove() //蛇前進,上U,下D,左L,右R { nexthead = (snake*)malloc(sizeof(snake)); if (status == U) { nexthead->y = head->y - 1; //確定新蛇頭的下一個坐標 x,y nexthead->x = head->x; } if (status == D) //下 { nexthead->y = head->y + 1; nexthead->x = head->x; } if (status == L) //左 { nexthead->x = head->x - 2; nexthead->y = head->y; } if (status == R) //右 { nexthead->x = head->x + 2; nexthead->y = head->y; } nexthead->next = head; head = nexthead; p = head; if (p->x == foodx && p->y == foody) //判斷蛇頭的位置是否和食物的位置重合 { while (p) //輸出尾結點 { gotoxy(p->x, p->y); if (p == head) printf("●"); else printf("■"); p = p->next; //因為每次運動都是新創(chuàng)建一個頭結點再刪除一個尾結點, } //所以要增加一節(jié)身體,只需要這次循環(huán)不釋放尾結點,并輸出一次尾結點就好了 //sjcsswhs(); //碰到食物則再產生一個食物 score++; gotoxy(32, 28); printf("%d", score); } else { while (p->next->next) //不輸出尾結點 { gotoxy(p->x, p->y); if (p == head) printf("●"); else printf("■"); p = p->next; } gotoxy(p->next->x, p->next->y); printf(" "); free(p->next); p->next = NULL; } p = head; while (p) //如果食物的坐標刷新到了蛇身上則再產生一個食物 * { if (p->x == foodx && p->y == foody) sjcsswhs(); p = p->next; } } void czfxhs() //操作方向函數,接收從鍵盤輸入的按鍵,控制貪吃蛇行進方向 { char ch = _getch(); switch (ch) { case 'w': if(status != D) status = U; break; case 's': if (status != U) status = D; break; case 'a': if (status != R) status = L; break; case 'd': if (status != L) status = R; break; case ' ': _getch(); break; //空格暫停 } } int yxjstjjsmz_1() //生命掉落條件1咬自己 { snake* self = head->next; //self為蛇身上的一個結點 while (self) { if (self->x == head->x && self->y == head->y) //head和self的成員作比較,蛇頭一直存在,這里遍歷的是蛇身 { return 1; } self = self->next; } return 0; } int yxjstjjsmz_2() //生命掉落條件2碰墻 { if (head->x <= 1 || head->x >= 98 || head->y <= 0 || head->y >= 26) return 1; return 0; } int i = LIFE - 1; //變量存儲生命次數 void qcsytmslbhs() //清除并釋放上一條蛇留下來的痕跡,更新生命信息 { p = head; int _x_ = p->x; //用于保存死掉的蛇的蛇頭處的位置,用于輸出被蛇頭頂掉的墻壁 int _y_ = p->y; while (head) { gotoxy(head->x, head->y); printf(" "); head = head->next; free(p); p = head; } gotoxy(52, 28); //更新生命信息 printf("%d", i); if (_y_ == 0 || _y_ == HEIGHT - 4) //用于在蛇死掉后,蛇頭的位置輸出被清除蛇頭頂替掉的墻壁 { gotoxy(_x_, _y_); printf("--"); } else if (_x_ == WIDTH - 2) { gotoxy(_x_+1, _y_); printf("|"); } else if(_x_ == 0) { gotoxy(_x_, _y_); printf("|"); } } void sbjsjmhs() //失敗結束界面 { p = head; //釋放內存 while (head) { head = head->next; free(p); p = head; } system("cls"); gotoxy(45, 12); printf("游戲結束!"); gotoxy(44, 14); printf("最終得分:%d", score); gotoxy(0,28); } int updatetime() //獲取一次電腦現在的時間 { int now; SYSTEMTIME system_time; GetLocalTime(&system_time); now = system_time.wMinute * 60 + system_time.wSecond; return now; } int time_1 = updatetime(); //保存游戲剛開始的時間 void gametime() //寫在每次循環(huán)之內 { int time_2 = updatetime() - time_1; //更新游戲開始后時間,用現在的時間減去剛開始的時間 gotoxy(72, 28); printf("%d s", time_2); } int main()//主函數 { system("mode con cols=100 lines=30"); //設置控制臺大小 system("title 貪吃蛇游戲"); //設置標題 HideCursor(); //隱藏光標 sjcsswhs(); //初始化隨機產生一個食物 dtxxcsh(); //初始化地圖、信息 for (; i >= 0; i--) //生命 { cshs(); //初始化蛇的位置 status = R; //初始化運動方向 while (1) { snakemove(); //蛇行動動畫,方向被控制變量控制 if (_kbhit()) { czfxhs(); //接收鍵盤按鍵,控制控制變量 } if (yxjstjjsmz_1() || yxjstjjsmz_2())//兩個掉落生命的條件 break; gametime(); //更新游戲時間 Sleep(SPEED); } qcsytmslbhs(); //清除上一條蛇留下來的痕跡,更新生命信息 } sbjsjmhs(); //失敗結束界面 return 0; } void HideCursor() { CONSOLE_CURSOR_INFO cursor_info = { 1, 0 }; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); } void gotoxy(int x, int y) { COORD pos = { x,y }; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos); }
不足之處:
因為這是我第一次使用鏈表做一個完整的小程序,所以對鏈表的運用還很稚嫩,在代碼規(guī)范和嚴謹性上面還有很多問題。
另外關于“食物如果刷新到蛇身上則再隨機產生一個食物”(172行)相關代碼,我不是很確定到底完不完善。
作為一名c語言新手,我對未知的知識始終抱有學習和謙卑的態(tài)度,如有貴人能夠對我的程序提出建議,我將不勝感激。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
C++編譯錯誤variable-sized?object?may?not?be?initiali問題
這篇文章主要介紹了C++編譯錯誤variable-sized?object?may?not?be?initiali問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05