C語言單鏈表貪吃蛇小游戲
C語言實現(xiàn)單鏈表控制臺貪吃蛇小游戲,供大家參考。
編譯環(huán)境:vs2019
需求:
統(tǒng)計游戲開始后的時間,控制貪吃蛇;吃到食物蛇身加長,得分加一;碰墻或蛇頭碰到身體減一條生命;生命消耗完則結(jié)束游戲。
思路:
使用wasd鍵控制蛇的移動方向,蛇頭碰到食物得分加一,并在地圖上隨機產(chǎn)生一個食物,累加得分,碰墻或碰自己減一條生命,并初始化整條蛇,生命值為0時結(jié)束游戲。
做法:
使用單鏈表控制貪吃蛇移動的核心思想就是:鏈表存儲貪吃蛇所有坐標(biāo),每次循環(huán)貪吃蛇不斷向一個方向插入一個新的結(jié)點作為新的蛇頭,按下按鍵控制新蛇頭產(chǎn)生的位置,然后從新蛇頭處遍歷鏈表輸出蛇身到上一個蛇尾,清除上一個蛇尾的痕跡,并釋放相關(guān)結(jié)點。
每次向鏈表插入新節(jié)點后,判斷新節(jié)點的坐標(biāo)是否和食物的坐標(biāo)重合,如果重合本輪循環(huán)不釋放清除蛇尾結(jié)點,反之釋放清除上一個蛇尾的結(jié)點。
另外,在寫蛇生命相關(guān)代碼的時候,還需要注意一下哪些值應(yīng)該初始化,哪些值不應(yīng)該初始化。
只要明白了貪吃蛇運動的核心思想,整個程序其實就不難寫出來。
難點:
wsad控制貪吃蛇上下左右移動,并清除蛇尾。
說明:
使用單鏈表實現(xiàn)貪吃蛇的核心思想是我一開始沒有想到的,部分相關(guān)代碼我學(xué)習(xí)并借鑒了一些網(wǎng)絡(luò)上搜索到的代碼,如果有違反了相關(guān)版權(quán)協(xié)議,請告知我修改相關(guān)代碼。
注意:
由于編譯器原因程序中_kbhit()和_getch()函數(shù)可能在其他編譯器上編譯會出現(xiàn)錯誤,解決辦法是去掉函數(shù)前面的“_”。
運行效果:

代碼實現(xiàn):
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <time.h>
#include <windows.h>
void HideCursor(); //光標(biāo)隱藏
void gotoxy(int x, int y); //光標(biāo)定位
typedef struct snake
{
int x;
int y;
struct snake* next;
}snake;
#define WIDTH 100 //控制臺窗口寬度
#define HEIGHT 30 //控制臺窗口高度
#define SNAKEN 4 //貪吃蛇初始長度
#define LIFE 3 //初始生命次數(shù)
#define SPEED 200 //游戲速度、循環(huán)休眠時間
#define U 1 //使用宏代替需要數(shù)字代替的蛇的行動方向
#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可用得分數(shù)值 14個空格
}
int foodx, foody; //食物位置坐標(biāo)
void sjcsswhs() //隨機產(chǎn)生一個食物
{
srand(time(NULL));
foodx = rand() % (WIDTH - 4) + 2; //使用宏運算隨機數(shù)最大值需要加括號
while (foodx % 2) //如果食物的x坐標(biāo)不是偶數(shù),再獲取一個x坐標(biāo)
{
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這個數(shù)必須為偶數(shù),和食物坐標(biāo)偶數(shù)對應(yīng)
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; //確定新蛇頭的下一個坐標(biāo) 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) //輸出尾結(jié)點
{
gotoxy(p->x, p->y);
if (p == head)
printf("●");
else
printf("■");
p = p->next; //因為每次運動都是新創(chuàng)建一個頭結(jié)點再刪除一個尾結(jié)點,
} //所以要增加一節(jié)身體,只需要這次循環(huán)不釋放尾結(jié)點,并輸出一次尾結(jié)點就好了
//sjcsswhs(); //碰到食物則再產(chǎn)生一個食物
score++;
gotoxy(32, 28);
printf("%d", score);
}
else
{
while (p->next->next) //不輸出尾結(jié)點
{
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) //如果食物的坐標(biāo)刷新到了蛇身上則再產(chǎn)生一個食物 *
{
if (p->x == foodx && p->y == foody)
sjcsswhs();
p = p->next;
}
}
void czfxhs() //操作方向函數(shù),接收從鍵盤輸入的按鍵,控制貪吃蛇行進方向
{
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為蛇身上的一個結(jié)點
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; //變量存儲生命次數(shù)
void qcsytmslbhs() //清除并釋放上一條蛇留下來的痕跡,更新生命信息
{
p = head;
int _x_ = p->x; //用于保存死掉的蛇的蛇頭處的位置,用于輸出被蛇頭頂?shù)舻膲Ρ?
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() //失敗結(jié)束界面
{
p = head; //釋放內(nèi)存
while (head)
{
head = head->next;
free(p);
p = head;
}
system("cls");
gotoxy(45, 12);
printf("游戲結(jié)束!");
gotoxy(44, 14);
printf("最終得分:%d", score);
gotoxy(0,28);
}
int updatetime() //獲取一次電腦現(xiàn)在的時間
{
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)之內(nèi)
{
int time_2 = updatetime() - time_1; //更新游戲開始后時間,用現(xiàn)在的時間減去剛開始的時間
gotoxy(72, 28);
printf("%d s", time_2);
}
int main()//主函數(shù)
{
system("mode con cols=100 lines=30"); //設(shè)置控制臺大小
system("title 貪吃蛇游戲"); //設(shè)置標(biāo)題
HideCursor(); //隱藏光標(biāo)
sjcsswhs(); //初始化隨機產(chǎn)生一個食物
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(); //失敗結(jié)束界面
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ī)范和嚴(yán)謹性上面還有很多問題。
另外關(guān)于“食物如果刷新到蛇身上則再隨機產(chǎn)生一個食物”(172行)相關(guān)代碼,我不是很確定到底完不完善。
作為一名c語言新手,我對未知的知識始終抱有學(xué)習(xí)和謙卑的態(tài)度,如有貴人能夠?qū)ξ业某绦蛱岢鼋ㄗh,我將不勝感激。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
簡要說明C語言中指針函數(shù)與函數(shù)指針的區(qū)別
這篇文章主要介紹了C語言中指針函數(shù)與函數(shù)指針的區(qū)別,指針函數(shù)和函數(shù)指針是C語言入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2016-04-04
C語言實現(xiàn)繪制LoveBeat愛心曲線的示例代碼
這篇文章主要為大家詳細介紹了如何溧陽C語言實現(xiàn)繪制LoveBeat愛心曲線,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-03-03
C語言之?dāng)?shù)組名與數(shù)組起始地址的關(guān)系解析
這篇文章主要介紹了C語言之?dāng)?shù)組名與數(shù)組起始地址的關(guān)系,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
數(shù)據(jù)結(jié)構(gòu)之Treap詳解
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)之Treap詳解,本文講解了Treap的基本知識、Treap的基本操作、Treap的高級操作技巧等,需要的朋友可以參考下2014-08-08
C++編譯錯誤variable-sized?object?may?not?be?initiali問題
這篇文章主要介紹了C++編譯錯誤variable-sized?object?may?not?be?initiali問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05

