链表c语言小程序,C语言单链表贪吃蛇小游戏

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

编译环境:vs2019

需求:

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

思路:

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

做法:

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

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

另外,在写蛇生命相关代码的时候,还需要注意一下哪些值应该初始化,哪些值不应该初始化。

只要明白了贪吃蛇运动的核心思想,整个程序其实就不难写出来。

难点:

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

说明:

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

注意:

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

运行效果:

f7baa7ecb9546bcaff45ed0398467bc0.png

代码实现:

#include

#include

#include

#include

#include

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 //游戏速度、循环休眠时间

#define U 1 //使用宏代替需要数字代替的蛇的行动方向

#define D 2 //宏名含义是各方向英文单词首字母

#define L 3 //蛇的状态,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++) //在蛇尾处创建链表

{

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; //蛇前进状态

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; //因为每次运动都是新创建一个头结点再删除一个尾结点,

} //所以要增加一节身体,只需要这次循环不释放尾结点,并输出一次尾结点就好了

//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() //写在每次循环之内

{

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);

}

不足之处:

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

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值