C语言循环链表实现贪吃蛇游戏

 更新时间:2020年11月08日 17:03:35   作者:yzw丶  
这篇文章主要为大家详细介绍了C语言循环链表实现贪吃蛇,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了C语言表实现贪吃蛇游戏的具体代码,供大家参考,具体内容如下

总体思想

利用循环链表将一条蛇的坐标进行存储,然后利用gotoxy()函数(可以将光标定位到指定的位置),此时根据蛇的坐标进行输出“@”,输出多几个既可以产生一条蛇。通过遍历循环链表进行蛇的移动,对循环链表的插入元素,产生蛇变长的效果。下面为各功能实现的函数

1.贪吃蛇地图函数map()
2.蛇的移动move(),up(),left()等函数
3.产生食物food()和吃到食物eat_food()
4.蛇吃到食物时产生的变长效果snake_link()函数
5.判断蛇的死亡,分别为撞墙hit_wall()和自杀suicide()

1.贪吃蛇地图函数map()

游戏地图采用的是应该封闭的区域,采用一个数组a[25][50],将此数组初始化为0,将游戏墙的边缘赋值为1,当数组为0,输出" ",数组为1,输出“#”,产生一个地图。

代码如下:

void map() //创建蛇的地图
{
 int a[25][50] = {0};
 int i,j;
 for(i = 0; i < 50; i++)
 {
 a[0][i] = 1;
 a[24][i] =1;
 }
 for(i = 0; i < 25; i++)
 {
 a[i][0] = 1;
 a[i][49] =1;
 }
 for(i = 0; i < 25; i++)
 for(j = 0; j < 50; j++)
 {
  if(j%50 == 0)
  printf("\n");
  if(a[i][j] == 0)
  {
  printf(" ");
  }
  else
  {
  printf("#");
  }
 }
}

2.蛇的移动move(),up(),left()等函数

move()函数主要对蛇的上下左右进行更改在此采用switch函数进行解决(下面代码中ch为全局变量)
代码如下

void move(struct snake *p) //蛇的移动函数
{
 while(1)
 {
 ch = getch();
 switch(ch)
 {
 case 'W':p = up(p);break;
 case 'A':p = left(p);break;
 case 'D':p = right(p);break;
 case 'S':p = down(p);break;
 }
 }
}

让蛇动起来的即我们主要对蛇的坐标进行更改,此时蛇头移动一次我们就利用gotoxy()函数进行输出“@”,然后在蛇尾输出“ ”,循环往复就可以产生蛇移动的效果,蛇的上下左右则只需在移动一个方向的时候对单一的坐标x或y进行更改,然后对更改的坐标保存进循环链表即可。移动函数则主要有up(),left()等,因为做法差不多,在此只对up()函数进行展示

代码如下

struct snake *up(struct snake *p) //向上移动
{

 int x;
 int y;
 x = p->pre->x;   //将蛇头坐标赋值给x,y
 y = p->pre->y;
 while(p)   //对循环链表的遍历,即蛇的遍历
 {
 Sleep(SNAKE_SPEED); //蛇移动的速度
 y--;   //向上移动则只需将纵坐标进行减,就可以实现蛇向上移动的效果
 gotoxy(p->x,p->y);  //定位到蛇尾,输出“ ”即蛇尾消失
 printf(" ");
 gotoxy(x, y);  //定位到蛇头输出,"@",结合上面的蛇尾消失又进行蛇头打印,产生蛇移动的效果
 printf("@");
 suicide(p,x,y);  //判断蛇头是否撞到蛇身
 p = p->next;  //将蛇头的坐标变为下一个
 p->pre->x = x;  //此时将前一个蛇头变成蛇尾,通过不断的遍历产生不断移动的效果
 p->pre->y = y;
 food();   //产生食物
 eat_food(p,x,y);  //判断是否吃到食物
 hit_wall(y);  //判断是否撞墙
 if(kbhit()) break; //判断是否有按键输入,有就进行蛇移动方向的改变
 }

 return p;
}

3.产生食物food()和吃到食物eat_food()

食物和吃到食物,产生食物则采用了产生随机数,产生一个食物的x,y坐标分别存放在全局变量food_xy[2]数组里面,最后利用gotoxy(food_xy[0],food_xy[1])随机产生食物
代码如下

void food()   //产生食物
{
 int i;
 if(!flag)   //根据flag的值来判断地图上是否有食物
 {
 srand( (unsigned)time( NULL ) );
 for( i = 0; i < 2; i++ ) //对food_(x,y)来随机赋值
 {
  food_xy[i] = rand()%24+2;
  while(food_xy[0] == 1 || food_xy[0] == 25) //这两个while为了防止食物
  food_xy[0] = rand()%24+2;  //的坐标与地图的边缘重叠
  while(food_xy[1] >= 49 || food_xy[1] == 1)
  food_xy[1] =rand()%24+2;
 }
 gotoxy(food_xy[0],food_xy[1]); //打印食物
 printf("*");
 flag = 1;
 }
}

吃到食物eat_food(),则我们只需判断蛇头是否和食物的坐标重叠,若重叠则表明蛇吃到了食物

代码如下

void eat_food(struct snake *p,int x, int y)  //蛇吃到食物,即主要是对蛇头的x,y坐标和
{       //food_xy的坐标进行匹配对比,若相同即调
 if(x == food_xy[0] && y == food_xy[1])  //snake_link函数即可
 {
 p = snake_link(p);
 flag = 0;     //表明食物被吃,准备重新产生食物
 printSnake(p);
 gotoxy(8,0);
 score = score + 1;    //得分
 printf("%d",score);
 }
}

4.蛇吃到食物时产生的变长效果snake_link()函数

蛇的变长,当蛇吃到食物的时候,此时我们将食物的坐标变成蛇头,然后进行重新的打印蛇,即可以有蛇变成的效果产生,实质为对循环链表进行元素的插入。

即通过这样将食物的坐标插进去循环链表,达到蛇变成的效果
代码如下

struct snake *snake_link(struct snake *p) //蛇的连接
{
 struct snake *q;
 q = (struct snake *)malloc(sizeof(struct snake)); //即主要是实现了对循环链表的插入元素,再
 q->x = food_xy[0];    //进行打印蛇,即可有吃到食物蛇变长的结果
 q->y = food_xy[1];
 q->pre = p->pre;
 p->pre->next = q;
 p->pre = q;
 q->next = p;
 return p;
}

5.判断蛇的死亡,分别为撞墙hit_wall()和自杀suicide()

撞墙,则只需判断蛇头的单一坐标x轴或者y轴是否与墙壁的坐标是否相等,若相等则说明蛇撞墙了
代码如下

void hit_wall(int n) //判断蛇是否撞墙,即对蛇的单一坐标x或者y进行判断,若等于墙壁的值,即游戏结束
{
 if(ch == 'W'|| ch == 'S' )
 if(n == 1 || n == 25)  //墙壁的坐标值
 {
  gotoxy(0,26);
  printf("游戏结束!");
  printf("你的得分:%d",score);
  exit(0);
 }
 if(ch == 'A'|| ch == 'D' )
 if(n == 0 || n == 49)
 {
  gotoxy(0,26);
  printf("游戏结束!");
  printf("你的得分:%d",score);
  exit(0);
 }
}

自杀suicide()即蛇头是否有撞到了蛇身,做法是把蛇头的坐标拿出来,与蛇身的坐标进行对比如果相等,说明蛇头撞到了蛇身,本质上是循环链表的值进行匹配,遍历

代码如下

void suicide(struct snake *p, int x, int y) //自杀,即撞到自己本身的时候游戏结束
{
 struct snake *q;    //把蛇头坐标传递进来,然后与其自己身体坐标做对比若有相等则表明,蛇头撞到了蛇身
 q = p;
 while(q != p->next)   //即对循环链表的遍历匹配
 {
 if(p->x == x && p->y == y)
 {
  gotoxy(0,26);
  printf("游戏结束!");
  printf("你的得分:%d",score);
  exit(0);
 }
 else
  p = p->next;
 }
}

到此蛇的基本功能已经讲完,以下是全部代码。

全部代码如下

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <time.h>
#define SNAKE_SPEED 200 //蛇移动的速度
int score = 0;  //成绩得分
int flag = 0;  //用于判断地图上是否存在食物,0为不存在食物
int food_xy[2];  //定位食物的位置
char ch;  //用来决定蛇的移动方向
struct snake //定义一条循环链表的蛇
{
 int x;
 int y;
 struct snake *next;
 struct snake *pre;
};

void HideCursor()//把蛇移动的时候产生的光标进行隐藏,隐藏光标函数
{
 CONSOLE_CURSOR_INFO cursor;
 cursor.bVisible = FALSE;
 cursor.dwSize = sizeof(cursor);
 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
 SetConsoleCursorInfo(handle, &cursor);
}

void gotoxy(int x, int y) //定位光标函数,用来实现蛇的移动,和食物的出现(传送x,y可以将光标定位到x,y)
{
 HideCursor();
 COORD coord = {x,y};
 SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void map() //创建蛇的地图
{
 int a[25][50] = {0};
 int i,j;
 for(i = 0; i < 50; i++)
 {
 a[0][i] = 1;
 a[24][i] =1;
 }
 for(i = 0; i < 25; i++)
 {
 a[i][0] = 1;
 a[i][49] =1;
 }
 for(i = 0; i < 25; i++)
 for(j = 0; j < 50; j++)
 {
  if(j%50 == 0)
  printf("\n");
  if(a[i][j] == 0)
  {
  printf(" ");
  }
  else
  {
  printf("#");
  }
 }
}

struct snake *createSnake() //给蛇进行初始化,构建一条蛇,本质为循环链表
{
 int i;
 struct snake *head,*p,*q;
 p = q = (struct snake *)malloc(sizeof(struct snake));
 head = NULL;
 head = p;
 head->pre = NULL;
 for( i = 0; i < 5; i++)
 {
 p->x = 25 - i;
 p->y = 13;
 p->pre = head->pre;
 head->pre = p;
 q->next = p;
 q = p;
 p = (struct snake *)malloc(sizeof(struct snake));
 }
 q->next = head;
 return head;

}

void printSnake(struct snake *p) //打印蛇,利用gotoxy()来对蛇进行打印,只需遍历一次循环链表即可将坐标可视化为一条蛇
{
 struct snake *q;
 q = p;
 while(q != p->next)  //循环链表的遍历
 {
 gotoxy(p->x,p->y);  //根据坐标和定位光标函数来打@,实现输出蛇
 printf("@");
 p = p->next;
 }
 gotoxy(p->x,p->y);
 printf("@");
 gotoxy(0,0);
 printf("你的得分:");  //初始化得分
}

void food()   //产生食物
{
 int i;
 if(!flag)   //根据flag的值来判断地图上是否有食物
 {
 srand( (unsigned)time( NULL ) );
 for( i = 0; i < 2; i++ ) //对food_(x,y)来随机赋值
 {
  food_xy[i] = rand()%24+2;
  while(food_xy[0] == 1 || food_xy[0] == 25) //这两个while为了防止食物的坐标与地图的边缘重叠
  food_xy[0] = rand()%24+2;
  while(food_xy[1] >= 49 || food_xy[1] == 1)
  food_xy[1] =rand()%24+2;
 }
 gotoxy(food_xy[0],food_xy[1]); //打印食物
 printf("*");
 flag = 1;
 }
}

struct snake *snake_link(struct snake *p) //蛇的连接
{
 struct snake *q;
 q = (struct snake *)malloc(sizeof(struct snake)); //即主要是实现了对循环链表的插入元素,再进行打印蛇,即可有吃到食物蛇变长的结果
 q->x = food_xy[0];
 q->y = food_xy[1];
 q->pre = p->pre;
 p->pre->next = q;
 p->pre = q;
 q->next = p;
 return p;
}

void eat_food(struct snake *p,int x, int y)  //蛇吃到食物,即主要是对蛇头的x,y坐标和food_xy的坐标进行匹配对比,若相同即调用snake_link函数即可
{
 if(x == food_xy[0] && y == food_xy[1])
 {
 p = snake_link(p);
 flag = 0;
 printSnake(p);
 gotoxy(8,0);
 score = score + 1;
 printf("%d",score);
 }
}

void hit_wall(int n) //判断蛇是否撞墙,即对蛇的单一坐标x或者y进行判断,若等于墙壁的值,即游戏结束
{
 if(ch == 'W'|| ch == 'S' )
 if(n == 1 || n == 25)
 {
  gotoxy(0,26);
  printf("游戏结束!");
  printf("你的得分:%d",score);
  exit(0);
 }
 if(ch == 'A'|| ch == 'D' )
 if(n == 0 || n == 49)
 {
  gotoxy(0,26);
  printf("游戏结束!");
  printf("你的得分:%d",score);
  exit(0);
 }
}


void suicide(struct snake *p, int x, int y) //自杀,即撞到自己本身的时候游戏结束
{
 struct snake *q;    //把蛇头坐标传递进来,然后与其自己身体坐标做对比若有相等则表明,蛇头撞到了蛇身
 q = p;
 while(q != p->next)   //即对循环链表的遍历匹配
 {
 if(p->x == x && p->y == y)
 {
  gotoxy(0,26);
  printf("游戏结束!");
  printf("你的得分:%d",score);
  exit(0);
 }

 else
  p = p->next;
 }
}

struct snake *up(struct snake *p) //向上移动
{

 int x;
 int y;
 x = p->pre->x;   //将蛇头坐标赋值给x,y
 y = p->pre->y;
 while(p)   //对循环链表的遍历,即蛇的遍历
 {

 Sleep(SNAKE_SPEED); //蛇移动的速度
 y--;   //向上移动则只需将纵坐标进行减,就可以实现蛇向上移动的效果
 gotoxy(p->x,p->y);  //定位到蛇尾,输出“ ”即蛇尾消失
 printf(" ");
 gotoxy(x, y);  //定位到蛇头输出,"@",结合上面的蛇尾消失又进行蛇头打印,产生蛇移动的效果
 printf("@");
 suicide(p,x,y);  //判断蛇头是否撞到蛇身
 p = p->next;  //将蛇头的坐标变为下一个
 p->pre->x = x;  //此时将前一个蛇头变成蛇尾,通过不断的遍历产生不断移动的效果
 p->pre->y = y;
 food();   //产生食物
 eat_food(p,x,y);  //判断是否吃到食物
 hit_wall(y);  //判断是否撞墙
 if(kbhit()) break; //判断是否有按键输入,有就进行蛇移动方向的改变
 }

 return p;


}

struct snake *left(struct snake *p) //向左移动
{
 int x;
 int y;
 x = p->pre->x;
 y = p->pre->y;
 while(p)
 {

 Sleep(SNAKE_SPEED);
 x--;
 gotoxy(p->x,p->y);
 printf(" ");
 gotoxy(x, y);
 printf("@");
 suicide(p,x,y);
 p = p->next;
 p->pre->x = x;
 p->pre->y = y;
 food();
 eat_food(p,x,y);
 hit_wall(x);
 if(kbhit()) break;
 }
 return p;
}

struct snake *down(struct snake *p) //向下移动
{
 int x;
 int y;
 x = p->pre->x;
 y = p->pre->y;
 while(p)
 {

 Sleep(SNAKE_SPEED);
 y++;
 gotoxy(p->x,p->y);
 printf(" ");
 gotoxy(x, y);
 printf("@");
 suicide(p,x,y);
 p = p->next;
 p->pre->x = x;
 p->pre->y = y;
 food();
 eat_food(p,x,y);
 hit_wall(y);
 if(kbhit()) break;
 }
 return p;
}

struct snake *right(struct snake *p) //向右移动
{
 int x;
 int y;
 x = p->pre->x;
 y = p->pre->y;
 while(p)
 {

 Sleep(SNAKE_SPEED);
 x++;
 gotoxy(p->x,p->y);
 printf(" ");
 gotoxy(x, y);
 printf("@");
 suicide(p,x,y);
 p = p->next;
 p->pre->x = x;
 p->pre->y = y;
 food();
 eat_food(p,x,y);
 hit_wall(x);
 if(kbhit()) break;
 }
 return p;


}
void move(struct snake *p) //蛇的移动函数
{
 while(1)
 {
 ch = getch();
 switch(ch)
 {
 case 'W':p = up(p);break;
 case 'A':p = left(p);break;
 case 'D':p = right(p);break;
 case 'S':p = down(p);break;
 }
 }
}
int main()
{
 struct snake *p;
 map(); //产生地图
 p = createSnake(); // 初始化蛇
 printSnake(p); // 打印蛇
 move(p); //移动蛇

 return 0;
}

更多有趣的经典小游戏实现专题,分享给大家:

C++经典小游戏汇总

python经典小游戏汇总

python俄罗斯方块游戏集合

JavaScript经典游戏 玩不停

java经典小游戏汇总

javascript经典小游戏汇总

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

相关文章

  • C++运算符重载实例代码详解(调试环境 Visual Studio 2019)

    C++运算符重载实例代码详解(调试环境 Visual Studio 2019)

    这篇文章主要介绍了C++运算符重载实例(调试环境 Visual Studio 2019),本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • C语言完数的实现示例

    C语言完数的实现示例

    C语言中的完数指的是一个正整数,本文主要介绍了C语言完数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • 详谈C++ socket网络编程实例(2)

    详谈C++ socket网络编程实例(2)

    这篇文章主要为大家介绍了C++ socket网络编程实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-11-11
  • C/C++程序链接与反汇编工具objdump的使用介绍

    C/C++程序链接与反汇编工具objdump的使用介绍

    这篇文章主要介绍了C/C++程序链接与反汇编工具objdump的使用,程序构建过程的第二个阶段就是链接,链接过程输入的是目标文件的集合。每个目标文件可以被看作单个源代码文件的二进制存储版本
    2023-02-02
  • C++实例详解lambda表达式的使用

    C++实例详解lambda表达式的使用

    Lambda表达式是现代C++在C ++ 11和更高版本中的一个新的语法糖 ,在C++11、C++14、C++17和C++20中Lambda表达的内容还在不断更新。 lambda表达式(也称为lambda函数)是在调用或作为函数参数传递的位置处定义匿名函数对象的便捷方法
    2022-05-05
  • C语言实现素因子分解

    C语言实现素因子分解

    这篇文章主要为大家详细介绍了C语言实现素因子分解,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • C++设计模式编程中使用Bridge桥接模式的完全攻略

    C++设计模式编程中使用Bridge桥接模式的完全攻略

    这篇文章主要介绍了C++设计模式编程中使用Bridge桥接模式的完全攻略,Bridge将抽象部分与它的实现部分分离,使它们都可以独立地变化需要的朋友可以参考下
    2016-03-03
  • 总结UNIX/LINUX下C++程序计时的方法

    总结UNIX/LINUX下C++程序计时的方法

    本文总结了下UNIX/LINUX下C++程序计时的一些函数和方法,对日常使用C++程序的朋友很有帮助,有需要的小伙伴们可以参考学习,下面一起来看看吧。
    2016-08-08
  • C语言二维数组的处理实例

    C语言二维数组的处理实例

    这篇文章主要介绍了C语言二维数组的处理实例,有需要的朋友可以参考一下
    2013-12-12
  • C语言递归应用实现扫雷游戏

    C语言递归应用实现扫雷游戏

    这篇文章主要为大家详细介绍了C语言递归应用实现扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06

最新评论