C语言实现单链表控制台贪吃蛇小游戏,供大家参考。
编译环境:vs2019
需求:
统计游戏开始后的时间,控制贪吃蛇;吃到食物蛇身加长,得分加一;碰墙或蛇头碰到身体减一条生命;生命消耗完则结束游戏。
思路:
使用wasd键控制蛇的移动方向,蛇头碰到食物得分加一,并在地图上随机产生一个食物,累加得分,碰墙或碰自己减一条生命,并初始化整条蛇,生命值为0时结束游戏。
做法:
使用单链表控制贪吃蛇移动的核心思想就是:链表存储贪吃蛇所有坐标,每次循环贪吃蛇不断向一个方向插入一个新的结点作为新的蛇头,按下按键控制新蛇头产生的位置,然后从新蛇头处遍历链表输出蛇身到上一个蛇尾,清除上一个蛇尾的痕迹,并释放相关结点。
每次向链表插入新节点后,判断新节点的坐标是否和食物的坐标重合,如果重合本轮循环不释放清除蛇尾结点,反之释放清除上一个蛇尾的结点。
另外,在写蛇生命相关代码的时候,还需要注意一下哪些值应该初始化,哪些值不应该初始化。
只要明白了贪吃蛇运动的核心思想,整个程序其实就不难写出来。
难点:
wsad控制贪吃蛇上下左右移动,并清除蛇尾。
说明:
使用单链表实现贪吃蛇的核心思想是我一开始没有想到的,部分相关代码我学习并借鉴了一些网络上搜索到的代码,如果有违反了相关版权协议,请告知我修改相关代码。
注意:
由于编译器原因程序中_kbhit()和_getch()函数可能在其他编译器上编译会出现错误,解决办法是去掉函数前面的“_”。
运行效果:

代码实现:
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
#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 //游戏速度、循环休眠时间
#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语言新手,我对未知的知识始终抱有学习和谦卑的态度,如有贵人能够对我的程序提出建议,我将不胜感激。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/qq_46239972/article/details/104132471








发表评论
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。