C语言单链表贪吃蛇小游戏

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

编译环境:vs2019

需求:

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

思路:

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

做法:

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

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

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

难点:

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

说明:

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

注意:

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

运行效果:

代码实现:

#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语言新手,我对未知的知识始终抱有学习和谦卑的态度,如有贵人能够对我的程序提出建议,我将不胜感激。

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

(0)

相关推荐

  • C语言手把手教你实现贪吃蛇AI(下)

    本文实例为大家分享了C语言实现贪吃蛇AI的具体代码,供大家参考,具体内容如下 1. 目标 这一部分的目标是把之前写的贪吃蛇加入AI功能,即自动的去寻找食物并吃掉. 2. 控制策略 为了保证蛇不会走入"死地",所以蛇每前进一步都需要检查,移动到新的位置后,能否找到走到蛇尾的路径,如果可以,才可以走到新的位置:否则在当前的位置寻找走到蛇尾的路径,并按照路径向前走一步,开始循环之前的操作,如下图所示.这个策略可以工作,但是并不高效,也可以尝试其他的控制策略,比如易水寒的贪吃蛇AI 运行效果如

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

    阅读学习了源代码,并做了简单的注释和修改,里面只用了链表数据结构,非常适合C语言入门者学习阅读. 程序可在VS2013下编译运行. #include<stdio.h> #include<time.h> #include<windows.h> #include<stdlib.h> #define U 1 #define D 2 #define L 3 #define R 4 //蛇的状态,U:上 :D:下:L:左 R:右 typedef struct SNAK

  • 70行C语言代码实现贪吃蛇

    本文实例为大家分享了C语言实现贪吃蛇的具体代码,供大家参考,具体内容如下 #include <stdio.h> #include <Windows.h> #include <conio.h> #include <time.h> #define MAX_WIDE 50 #define MAX_HIGH 16 short dx = 1, dy = 0, randxy, score = 0; COORD coord; struct Snake{ short len

  • C语言结构数组实现贪吃蛇小游戏

    一.设计思路 蛇身本质上就是个结构数组,数组里存储了坐标x.y的值,再通过一个循环把它打印出来,蛇的移动则是不断地刷新重新打印.所以撞墙.咬到自己只是数组x.y值的简单比较. 二.用上的知识点 结构数组Windows API函数 三.具体实现 先来实现静态页面,把地图.初始蛇身.食物搞定. 这里需要用到Windows API的知识,也就是对控制台上坐标的修改 //这段代码来自参考1 void Pos(int x, int y) { COORD pos; HANDLE hOutput; pos.X

  • 基于C语言实现的贪吃蛇游戏完整实例代码

    本文以实例的形式讲述了基于C语言实现的贪吃蛇游戏代码,这是一个比较常见的游戏,代码备有比较详细的注释,对于读者理解有一定的帮助. 贪吃蛇完整实现代码如下: #include <graphics.h> #include <conio.h> #include <stdlib.h> #include <dos.h> #define NULL 0 #define UP 18432 #define DOWN 20480 #define LEFT 19200 #defi

  • C语言手把手教你实现贪吃蛇AI(上)

    本文实例为大家分享了手把手教你实现贪吃蛇AI的具体步骤,供大家参考,具体内容如下 1. 目标 编写一个贪吃蛇AI,也就是自动绕过障碍,去寻找最优路径吃食物. 2. 问题分析 为了达到这一目的,其实很容易,总共只需要两步,第一步抓一条蛇,第二步给蛇装一个脑子.具体来说就是,首先我们需要有一条普通的贪吃蛇,也就是我们常玩儿的,手动控制去吃食物的贪吃蛇:然后给这条蛇加入AI,也就是通过算法控制,告诉蛇怎么最方便的绕开障碍去吃食物.为了讲清楚这个问题,文章将分为三部分:上,写一个贪吃蛇程序:中,算法基础

  • C语言手把手教你实现贪吃蛇AI(中)

    手把手教你实现贪吃蛇AI,具体内容如下 1. 目标 这一部分主要是讲解编写贪吃蛇AI所需要用到的算法基础. 2. 问题分析 贪吃蛇AI说白了就是寻找一条从蛇头到食物的一条最短路径,同时这条路径需要避开障碍物,这里仅有的障碍就是蛇身.而A star 算法就是专门针对这一个问题的.在A star 算法中需要用到排序算法,这里采用堆排序(当然其他排序也可以),如果对堆排序不熟悉的朋友,请移步到这里--堆排序,先看看堆排序的内容. 3. A*算法 A star(也称A*)搜寻算法俗称A星算法.这是一种在

  • DOS简易版C语言贪吃蛇

    本文实例为大家分享了C语言实现贪吃蛇的具体代码,供大家参考,具体内容如下 #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <time.h> #include <windows.h> #define WALL_LENGTH 22 #define LEFT 0x4b #define RIGHT 0x4d #define DOWN 0x50 #define UP 0x

  • C语言贪吃蛇经典小游戏

    一.贪吃蛇小游戏简介: 用上下左右控制蛇的方向,寻找吃的东西,每吃一口就能得到一定的积分,而且蛇的身子会越吃越长,身子越长玩的难度就越大,不能碰墙,也不能咬到自己的身体,等到了一定的分数,就能过关. 二.函数框架 三.数据结构 typedef struct Snake { size_t x; //行 size_t y; //列 struct Snake* next; }Snake, *pSnake; 定义蛇的结构体,利用单链表来表示蛇,每个结点为蛇身体的一部分. 四.代码实现(vs2010  c

  • C语言实现贪吃蛇游戏

    最近整理下电脑,看到了自己在上个学期打的贪吃蛇游戏的c代码,觉得真的是略微有点冗长,但是实现起来应该也算是比较好理解,于是把自己的代码搬上来,网络上写贪吃蛇的c语言的文章很多,我这篇也仅是给大家作为一个参考而已. 我的代码是在Windows下运行的,因为需要用到windows.h这个库. 然后也做了一个简单的ai模式,这在没有障碍物的情况下前期还是蛮不错的,但是到了后期蛇变长了之后就会有bug了. 好了,直接上代码吧: 1)头文件和宏定义 #include<stdio.h> #include&

随机推荐