【C语言】实践:贪吃蛇小游戏(附源码)(三)

简介: 【C语言】实践:贪吃蛇小游戏(附源码)

【C语言】实践:贪吃蛇小游戏(附源码)(二)https://developer.aliyun.com/article/1621361

源代码

Snake.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
 
#include<stdio.h>
#include<stdlib.h>
#include<locale.h>
#include<windows.h>
#include<stdbool.h>
#include<wchar.h> 
#include<time.h>
#include<conio.h>
 
//初始数据
#define SNAKE_X 24
#define SNAKE_Y 5
#define WALL L'□'
#define SNAKENODE L'●'
#define FOOD L'★'
 
//蛇的节点
typedef struct Snakenode
{
  int x;
  int y;
  struct Snakenode* next;
}Snakenode;
typedef Snakenode* pSnakenode;
 
//蛇的方向
enum DIRECT
{
  UP = 1,
  DOWN,
  LEFT,
  RIGHT
};
//蛇的状态——游戏状态
//正常、撞墙、撞到自己、正常退出
enum GAME_STATE
{
  OK,
  KILL_WALL,
  KILL_SELF,
  NORMAL_END
};
 
//贪吃蛇的相关信息
typedef struct Snake
{
  pSnakenode psnake; //指向蛇头部的指针
  pSnakenode pfood;   //指向食物的指针
  enum DIRECT dir;//蛇的方向
  enum GAME_STATE state;//蛇的状态
  int food_scores;//每个食物的分数
  int all_scores; //总分数
  int sleep_time;  //休息的时间 --即蛇的速度
}Snake;
typedef Snake* pSnake;
 
//设置光标位置
void SetPos(int x, int y);
//游戏初始化
void GameStart(pSnake ps);
//欢迎界面的输出
void WelcomeToGame();
//地图绘制
void CreatMap();
//创建贪吃蛇
void InitSnake(pSnake ps);
//创建食物
void CreatFood(pSnake ps);
 
//游戏运行
void GameRun(pSnake ps);
//贪吃蛇的移动
void SnakeMove(pSnake ps);
//判断下一个位置是不是食物
int NextIsFood(pSnakenode next , pSnake ps);
//下一个位置是食物,吃掉食物 
void IsFood(pSnakenode next, pSnake ps);
//下一个位置不是食物
void NoFood(pSnakenode next, pSnake ps);
//判断贪吃蛇是否撞墙
void KillByWall(pSnake ps);
//判断贪吃蛇是否撞到自己
void KillBySelf(pSnake ps);
//游戏结束
void GameOver(pSnake ps);
 
void KeyFun();

Snake.c

#include"Snake.h"
 
 
//设置光标位置
void SetPos(int x, int y)
{
  HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  COORD pos = { x,y };
  SetConsoleCursorPosition(houtput, pos);
}
 
//欢迎界面打印
void WelcomeToGame()
{
  //设置光标位置
  SetPos(40, 15);
  printf("欢迎进入贪吃蛇小游戏\n");
  SetPos(42, 20);
  system("pause");
  system("cls");//清理屏幕
  SetPos(20, 11);
  printf("请使用↑ 、 ↓ 、 ← 、 → 来控制贪吃蛇的移动,按F3加速、F4减速 ");
  SetPos(20, 13);
  printf("加速可以获得更多的分数");
  SetPos(20, 15);
  system("pause");
  system("cls"); //清理屏幕
}
//地图绘制
void CreatMap()
{
  //上
  
  int i = 0;
  for (i = 0; i < 29; i++)
  {
    wprintf(L"%lc", WALL);
  }
  //下
  SetPos(0, 26);
  for(i = 0; i < 29; i++)
  {
    wprintf(L"%lc", WALL);
  }
  //左
  
  for (i = 1; i < 26; i++)
  {
    SetPos(0, i);
    wprintf(L"%lc", WALL);
  }
  //右
  for (i = 1; i < 26; i++)
  {
    SetPos(56, i);
    wprintf(L"%lc", WALL);
  }
  //system("pause");
}
//创建贪吃蛇
void InitSnake(pSnake ps)
{
  //创建蛇的身体
  pSnakenode pcur = NULL;
  int i = 0;
  int x, y;//蛇初始位置
  do
  {
    x = rand() % 31 + 4; //x: 4 - 34
    y = rand() % 20 + 2; //y: 1 - 25
  } while (x % 2 != 0);
  for (i = 0; i < 5; i++)
  {
    pcur = (pSnakenode)malloc(sizeof(Snakenode));
    if (pcur == NULL)
    {
      perror("InitSnake()::malloc()");
      return;
    }
    pcur->next = NULL;
    //pcur->x = SNAKE_X + i * 2;
    //pcur->y = SNAKE_Y;
    pcur->x = x + i * 2;
    pcur->y = y;
    //头插到贪吃蛇链表中
    if (ps->psnake == NULL) //链表为空
    {
      ps->psnake = pcur;
    }
    else
    {
      pcur->next = ps->psnake;
      ps->psnake = pcur;
    }
  }
  //输出蛇的初始位置
  pcur = ps->psnake;
  while (pcur)
  {
    SetPos(pcur->x, pcur->y);
    wprintf(L"%lc", SNAKENODE);
    pcur = pcur->next;
  }
  //初始化贪吃蛇的信息
  ps->dir = RIGHT; //蛇的方向
  ps->pfood = NULL; //指向食物 --NULL  
  ps->state = OK;  //状态
  ps->food_scores = 10; //每个食物的得分
  ps->all_scores = 0;  //总分
  ps->sleep_time = 200;//速度,即休息时间 单位是毫秒
  //getchar();
}
//创建食物
void CreatFood(pSnake ps)
{
  int x, y;//随机生成坐标 x , y 
  //x 2-54
  //y 1-25
 
  again:
  do {
    x = rand() % 53 + 2;
    y = rand() % 25 + 1;
  } while (x % 2 != 0);
  //x y 不能与贪吃蛇身体重复
  pSnakenode pcur = ps->psnake;
  while (pcur)
  {
    if (x == pcur->x && y == pcur->y)
    {
      goto again;
    }
    pcur = pcur->next;
  } 
  pSnakenode food = (pSnakenode)malloc(sizeof(pSnakenode));
  if (food == NULL)
  {
    perror("CreatFood()::malloc");
    return;
  }
 
  food->x = x;
  food->y = y;
  food->next = NULL;
  ps->pfood = food;
  SetPos(x, y);
  wprintf(L"%lc", FOOD);
  //getchar();
}
 
//游戏初始化
void GameStart(pSnake ps)
{
  //设置窗口名称大小,隐藏光标
  system("title 贪吃蛇");
  system("mode con cols=100 lines=33");
 
  //隐藏光标
  HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  CONSOLE_CURSOR_INFO CursorInfo;
  GetConsoleCursorInfo(houtput, &CursorInfo);//获得有关指定控制台屏幕缓冲区的光标大小和可见的信息
  CursorInfo.bVisible = false; //隐藏控制台光标
  SetConsoleCursorInfo(houtput, &CursorInfo);//设置控制台光标状态  
  
  //打印欢迎界面和功能介绍
  WelcomeToGame();
  //绘制地图
  CreatMap();
  //创建贪吃蛇
  InitSnake(ps);
  //创建食物
  CreatFood(ps);
  //system("pause");
}
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
//暂停
void Pause()
{
  while (1)
  {
    Sleep(200);
    if (KEY_PRESS(VK_SPACE))
      break;
  }
}
//判断下一个位置是不是食物
int NextIsFood(pSnakenode next, pSnake ps)
{
  return (next->x == ps->pfood->x && next->y == ps->pfood->y);
}
//下一个位置是食物
void IsFood(pSnakenode next, pSnake ps)
{
  //把下一个位置的节点头插到贪吃蛇中
  next->next = ps->psnake;
  ps->psnake = next;
  //打印贪吃蛇
  pSnakenode cur = ps->psnake;
  while (cur)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"%lc", SNAKENODE);
    cur = cur->next;
  }
  ps->all_scores += ps->food_scores;
  CreatFood(ps);
  //SetPos(ps->pfood->x, ps->pfood->y);
  //wprintf(L"%lc", FOOD);
}
//下一个位置不是食物
void NoFood(pSnakenode next, pSnake ps)
{
  //把下一个位置的节点头插到贪吃蛇中
  next->next = ps->psnake;
  ps->psnake = next;
 
  pSnakenode cur = ps->psnake;
  while (cur->next->next != NULL)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"%lc", SNAKENODE);
    cur = cur->next;
  }
  SetPos(cur->next->x, cur->next->y);
  wprintf(L"%ls", L"  ");
 
  free(cur->next);
  cur->next = NULL;
}
 
//贪吃蛇的移动
void SnakeMove(pSnake ps)
{
  pSnakenode next = (pSnakenode)malloc(sizeof(Snakenode));
  if (next == NULL)
  {
    perror("SnakeMove():malloc()");
    exit(1);
  }
  switch (ps->dir)
  {
  case UP:
    next->x = ps->psnake->x;
    next->y = ps->psnake->y - 1;
    break;
  case DOWN:
    next->x = ps->psnake->x;
    next->y = ps->psnake->y + 1;
    break;
  case LEFT:
    next->x = ps->psnake->x - 2;
    next->y = ps->psnake->y;
    break;
  case RIGHT:
    next->x = ps->psnake->x + 2;
    next->y = ps->psnake->y;
    break;
  }
  //判断下一个位置是不是食物
  if (NextIsFood(next, ps))
  {
    IsFood(next, ps);
  }
  else {
    NoFood(next, ps);
  }
}
 
//
void Printgame(pSnake ps) {
  SetPos(60, 15);
  printf("请使用↑ 、 ↓ 、 ← 、 → 来控制贪吃蛇");
  SetPos(60, 16);
  printf("按F3加速、F4减速 ");
  SetPos(60, 18);
  printf("加速可以获得更高的分数 ");
  SetPos(60, 20);
  printf("ESC:退出游戏  space:暂停 ");
  SetPos(60, 10);
  printf("当前总得分:%d", ps->all_scores);
  SetPos(60, 12);
  printf("当前每个食物得分:%d", ps->food_scores);
  SetPos(60, 22);
  printf("努力学习的小廉");
}
//判断贪吃蛇是否撞墙
void KillByWall(pSnake ps)
{
  if (ps->psnake->x == 0 || ps->psnake->x == 56
    || ps->psnake->y == 0 || ps->psnake->y == 26)
    ps->state = KILL_WALL;
}
//判断贪吃蛇是否撞到自己
void KillBySelf(pSnake ps)
{
  pSnakenode pcur = ps->psnake->next;
  while (pcur)
  {
    if (pcur->x == ps->psnake->x && pcur->y == ps->psnake->y)
    {
      ps->state = KILL_SELF;
      break;
    }
    pcur = pcur->next;
  }
}
//游戏运行
void GameRun(pSnake ps)
{
  do
  {
    Printgame(ps);
    //判断按键是否被按过
    if (KEY_PRESS(VK_UP) && ps->dir != DOWN)
    {
      ps->dir = UP;
    }
    else if(KEY_PRESS(VK_DOWN) && ps->dir != UP){
      ps->dir = DOWN;
    }
    else if (KEY_PRESS(VK_LEFT) && ps->dir != RIGHT) {
      ps->dir = LEFT;
    }
    else if (KEY_PRESS(VK_RIGHT) && ps->dir != LEFT) {
      ps->dir = RIGHT;
    }
    else if (KEY_PRESS(VK_SPACE)) //空格 -- 暂停
    {
      Pause();
    }
    else if (KEY_PRESS(VK_ESCAPE)) //游戏正常退出
    {
      ps->state = NORMAL_END;
      break;
    }
    else if (KEY_PRESS(VK_F3))
    {
      if (ps->sleep_time >= 100)
      {
        ps->sleep_time -= 50;
        ps->food_scores += 5;//设定食物分数最高25
      }
    }
    else if (KEY_PRESS(VK_F4))
    {
      if (ps->sleep_time < 300)
      {
        ps->sleep_time += 100;
        ps->food_scores -= 5;//⼀个⻝物分数最低是5分
      }
    }
    Sleep(ps->sleep_time);
    //贪吃蛇的移动
    SnakeMove(ps);
    //判断贪吃蛇是否撞墙
    KillByWall(ps);
    //判断贪吃蛇是否撞到自己
    KillBySelf(ps);
 
  } while (ps->state == OK);
 
}
//游戏结束
void GameOver(pSnake ps)
{
  SetPos(8, 12);
  switch(ps->state)
  {
    case KILL_WALL:
      wprintf(L"Sorry,game over because you hit the wall !\n");
      break;
    case KILL_SELF:
      wprintf(L"Sorry,game over because you hit youself !\n");
      break;
    case NORMAL_END:
      wprintf(L"Game exits normally !");
      break;
  }
  
  //释放贪吃蛇的节点内存
  pSnakenode pcur = ps->psnake;
  while (pcur)
  {
    pSnakenode del = pcur;
    pcur = pcur->next;
    free(del);
  }
  ps->psnake = NULL;
}
 
void KeyFun()
{
  while (_kbhit())
  {
    int key = _getch();
  }
}

test.c

#include"Snake.h"
 
void test()
{
  Snake snake = { 0 };
  int ch = 0;
  do
  {
    ch = 0;
    system("cls");
    //游戏初始化
    GameStart(&snake);
    //游戏运行
    GameRun(&snake);
    //游戏结束
    GameOver(&snake);
    KeyFun();
    SetPos(30, 20);
    wprintf(L"再来一局吗? (Y/N)");
    ch = getchar();
    while (getchar() != '\n');
  } while (ch == 'Y' || ch == 'y');
  SetPos(0, 27);
}
int main()
{
  //本地化
  setlocale(LC_ALL, "");
  srand((unsigned int)time(NULL));
  test();
  //KeyFun();
  return 0;
}

制作不易,如果本篇内容对你有帮助,可以一键三连支持一下!!!

   

目录
打赏
0
0
0
0
13
分享
相关文章
c语言及数据结构实现简单贪吃蛇小游戏
c语言及数据结构实现简单贪吃蛇小游戏
|
3月前
|
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
166 16
|
3月前
|
【C语言程序设计——循环程序设计】求解最大公约数(头歌实践教学平台习题)【合集】
采用欧几里得算法(EuclideanAlgorithm)求解两个正整数的最大公约数。的最大公约数,然后检查最大公约数是否大于1。如果是,就返回1,表示。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。作为新的参数传递进去。这个递归过程会不断进行,直到。有除1以外的公约数;变为0,此时就找到了最大公约数。开始你的任务吧,祝你成功!是否为0,如果是,那么。就是最大公约数,直接返回。
143 18
【C语言程序设计——循环程序设计】利用循环求数值 x 的平方根(头歌实践教学平台习题)【合集】
根据提示在右侧编辑器Begin--End之间的区域内补充必要的代码,求解出数值x的平方根;运用迭代公式,编写一个循环程序,求解出数值x的平方根。注意:不能直接用平方根公式/函数求解本题!开始你的任务吧,祝你成功!​ 相关知识 求平方根的迭代公式 绝对值函数fabs() 循环语句 一、求平方根的迭代公式 1.原理 在C语言中,求一个数的平方根可以使用牛顿迭代法。对于方程(为要求平方根的数),设是的第n次近似值,牛顿迭代公式为。 其基本思想是从一个初始近似值开始,通过不断迭代这个公式,使得越来越接近。
96 18
|
3月前
|
【C语言程序设计——循环程序设计】统计海军鸣放礼炮声数量(头歌实践教学平台习题)【合集】
有A、B、C三艘军舰同时开始鸣放礼炮各21响。已知A舰每隔5秒1次,B舰每隔6秒放1次,C舰每隔7秒放1次。编程计算观众总共听到几次礼炮声。根据提示,在右侧编辑器Begin--End之间的区域内补充必要的代码。开始你的任务吧,祝你成功!
95 13
【C语言程序设计——选择结构程序设计】预测你的身高(头歌实践教学平台习题)【合集】
分支的语句,这可能不是预期的行为,这种现象被称为“case穿透”,在某些特定情况下可以利用这一特性来简化代码,但在大多数情况下,需要谨慎使用。编写一个程序,该程序需输入个人数据,进而预测其成年后的身高。根据提示,在右侧编辑器补充代码,计算并输出最终预测的身高。分支下的语句,提示用户输入无效。常量的值必须是唯一的,且在同一个。语句的作用至关重要,如果遗漏。开始你的任务吧,祝你成功!,程序将会继续执行下一个。常量都不匹配,就会执行。来确保程序的正确性。
175 10
|
3月前
|
【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】
目录 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果: 任务描述 相关知识 编程编写一个程序,从键盘输入3个变量的值,例如a=5,b=6,c=7,然后将3个变量的值进行交换,使得a=6,b=7,c=5。面积=sqrt(s(s−a)(s−b)(s−c)),s=(a+b+c)/2。使用输入函数获取半径,格式指示符与数据类型一致,实验一下,不一致会如何。根据提示,在右侧编辑器补充代码,计算并输出圆的周长和面积。
100 10
【C语言程序设计——函数】分数数列求和2(头歌实践教学平台习题)【合集】
函数首部:按照 C 语言语法,函数的定义首部表明这是一个自定义函数,函数名为fun,它接收一个整型参数n,用于指定要求阶乘的那个数,并且函数的返回值类型为float(在实际中如果阶乘结果数值较大,用float可能会有精度损失,也可以考虑使用double等更合适的数据类型,这里以float为例)。例如:// 函数体代码将放在这里函数体内部变量定义:在函数体中,首先需要定义一些变量来辅助完成阶乘的计算。比如需要定义一个变量(通常为float或double类型,这里假设用float。
93 3
【C语言程序设计——函数】分数数列求和1(头歌实践教学平台习题)【合集】
if 语句是最基础的形式,当条件为真时执行其内部的语句块;switch 语句则适用于针对一个表达式的多个固定值进行判断,根据表达式的值与各个 case 后的常量值匹配情况,执行相应 case 分支下的语句,直到遇到 break 语句跳出 switch 结构,若没有匹配值则执行 default 分支(可选)。例如,在判断一个数是否大于 10 的场景中,条件表达式为 “num> 10”,这里的 “num” 是程序中的变量,通过比较其值与 10 的大小关系来确定条件的真假。常量的值必须是唯一的,且在同一个。
78 2
|
3月前
|
【C语言程序设计——循环程序设计】利用数列的累加和求 sinx(头歌实践教学平台习题)【合集】
项的累加和,一般会使用循环结构,在每次循环中计算出当前项的值(可能基于通项公式或者递推关系),然后累加到一个用于存储累加和的变量中。在C语言中推导数列中的某一项,通常需要依据数列给定的通项公式或者前后项之间的递推关系来实现。例如,对于一个简单的等差数列,其通项公式为。的级数,其每一项之间存在特定的递推关系(后项的分子是其前项的分子乘上。,计算sinx的值,直到最后一项的绝对值小于。为项数),就可以通过代码来计算出指定项的值。对于更复杂的数列,像题目中涉及的用于近似计算。开始你的任务吧,祝你成功!
113 6
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等