C语言如何实现单链表控制台贪吃蛇小游戏

这篇文章给大家分享的是有关C语言如何实现单链表控制台贪吃蛇小游戏的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

C语言实现单链表控制台贪吃蛇小游戏,供大家参考。

编译环境:vs2019

需求:

统计游戏开始后的时间,控制贪吃蛇;吃到食物蛇身加长,得分加一;碰墙或蛇头碰到身体减一条生命;生命消耗完则结束游戏。

思路:

使用wasd键控制蛇的移动方向,蛇头碰到食物得分加一,并在地图上随机产生一个食物,累加得分,碰墙或碰自己减一条生命,并初始化整条蛇,生命值为0时结束游戏。

做法:

使用单链表控制贪吃蛇移动的核心思想就是:链表存储贪吃蛇所有坐标,每次循环贪吃蛇不断向一个方向插入一个新的结点作为新的蛇头,按下按键控制新蛇头产生的位置,然后从新蛇头处遍历链表输出蛇身到上一个蛇尾,清除上一个蛇尾的痕迹,并释放相关结点。

每次向链表插入新节点后,判断新节点的坐标是否和食物的坐标重合,如果重合本轮循环不释放清除蛇尾结点,反之释放清除上一个蛇尾的结点。

另外,在写蛇生命相关代码的时候,还需要注意一下哪些值应该初始化,哪些值不应该初始化。只要明白了贪吃蛇运动的核心思想,整个程序其实就不难写出来。

难点:

wsad控制贪吃蛇上下左右移动,并清除蛇尾。

说明:

使用单链表实现贪吃蛇的核心思想是我一开始没有想到的,部分相关代码我学习并借鉴了一些网络上搜索到的代码,如果有违反了相关版权协议,请告知我修改相关代码。

注意:

由于编译器原因程序中_kbhit()和_getch()函数可能在其他编译器上编译会出现错误,解决办法是去掉函数前面的“_”。

运行效果:

C语言如何实现单链表控制台贪吃蛇小游戏

代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#include<time.h>
#include<windows.h>

voidHideCursor();//光标隐藏
voidgotoxy(intx,inty);//光标定位

typedefstructsnake
{
intx;
inty;
structsnake*next;
}snake;

#defineWIDTH100//控制台窗口宽度
#defineHEIGHT30//控制台窗口高度
#defineSNAKEN4//贪吃蛇初始长度
#defineLIFE3//初始生命次数
#defineSPEED200//游戏速度、循环休眠时间
#defineU1//使用宏代替需要数字代替的蛇的行动方向
#defineD2//宏名含义是各方向英文单词首字母
#defineL3//蛇的状态,U:上;D:下;L:左R:右
#defineR4


voiddtxxcsh()//输出地图
{

for(inti=1;i<WIDTH-1;i++)//输出上下面墙
{
gotoxy(i,26);
printf("-");
gotoxy(i,0);
printf("-");
}
for(inti=0;i<HEIGHT-3;i++)//输出左右两面墙
{
gotoxy(0,i);
printf("|");
gotoxy(99,i);
printf("|");
}
gotoxy(24,28);
printf("得分:0生命:%d时间:0",LIFE);
//xy30,28可用得分数值14个空格
}


intfoodx,foody;//食物位置坐标

voidsjcsswhs()//随机产生一个食物
{
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;//蛇头指针

voidcshs()//初始化蛇的位置
{
snake*tail;//蛇尾指针
inti;

tail=(snake*)malloc(sizeof(snake));
tail->next=NULL;
tail->x=HEIGHT-6;
tail->y=8;

//贪吃蛇初始长度5SNAKEN
for(i=1;i<=SNAKEN;i++)//在蛇尾处创建链表
{
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;
}
}

intstatus=R;//蛇前进状态

snake*p=NULL;//工作指针
snake*nexthead;//下一个蛇头
intscore=0;//得分

voidsnakemove()//蛇前进,上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;//因为每次运动都是新创建一个头结点再删除一个尾结点,
}//所以要增加一节身体,只需要这次循环不释放尾结点,并输出一次尾结点就好了

//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;
}
}

voidczfxhs()//操作方向函数,接收从键盘输入的按键,控制贪吃蛇行进方向
{
charch=_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;//空格暂停
}
}

intyxjstjjsmz_1()//生命掉落条件1咬自己
{
snake*self=head->next;//self为蛇身上的一个结点
while(self)
{
if(self->x==head->x&&self->y==head->y)//head和self的成员作比较,蛇头一直存在,这里遍历的是蛇身
{
return1;
}
self=self->next;
}
return0;
}

intyxjstjjsmz_2()//生命掉落条件2碰墙
{
if(head->x<=1||head->x>=98||head->y<=0||head->y>=26)
return1;

return0;
}

inti=LIFE-1;//变量存储生命次数

voidqcsytmslbhs()//清除并释放上一条蛇留下来的痕迹,更新生命信息
{
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("--");
}
elseif(_x_==WIDTH-2)
{
gotoxy(_x_+1,_y_);
printf("|");
}
elseif(_x_==0)
{
gotoxy(_x_,_y_);
printf("|");
}
}

voidsbjsjmhs()//失败结束界面
{
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);
}

intupdatetime()//获取一次电脑现在的时间
{
intnow;
SYSTEMTIMEsystem_time;
GetLocalTime(&system_time);
now=system_time.wMinute*60+system_time.wSecond;
returnnow;
}

inttime_1=updatetime();//保存游戏刚开始的时间

voidgametime()//写在每次循环之内
{
inttime_2=updatetime()-time_1;//更新游戏开始后时间,用现在的时间减去刚开始的时间
gotoxy(72,28);
printf("%ds",time_2);
}

intmain()//主函数
{
system("modeconcols=100lines=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();//失败结束界面

return0;
}

voidHideCursor()
{
CONSOLE_CURSOR_INFOcursor_info={1,0};
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}

voidgotoxy(intx,inty)
{
COORDpos={x,y};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),pos);
}

不足之处:

因为这是我第一次使用链表做一个完整的小程序,所以对链表的运用还很稚嫩,在代码规范和严谨性上面还有很多问题。

另外关于“食物如果刷新到蛇身上则再随机产生一个食物”(172行)相关代码,我不是很确定到底完不完善。

作为一名c语言新手,我对未知的知识始终抱有学习和谦卑的态度,如有贵人能够对我的程序提出建议,我将不胜感激。

感谢各位的阅读!关于“C语言如何实现单链表控制台贪吃蛇小游戏”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

发布于 2021-06-13 23:19:41
收藏
分享
海报
0 条评论
194
上一篇:Jenkins中怎么打包android应用 下一篇:java String不可变的优势是什么
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码