贪吃蛇游戏(代码篇)

简介: 贪吃蛇游戏(代码篇)

前言

 这是我自己做的第五个小项目---贪吃蛇游戏(代码篇)。后期我会继续制作其他小项目并开源至博客上。


 上一小项目是贪吃蛇游戏(必备知识篇),没看过的同学可以去看看:


有关贪吃蛇必备知识的小项目

https://blog.csdn.net/hsy1603914691/article/details/142455297?sharetype=blogdetail&sharerId=142455297&sharerefer=PC&sharesource=hsy1603914691&spm=1011.2480.3001.8118


实现代码

1. 下面代码直接复制即可运行。

2. 每个代码块都有简洁的总结和解释。

<snake.h>文件

#define _CRT_SECURE_NO_WARNINGS
#include <locale.h>
#include <stdio.h>
#include <windows.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
 
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0) //设置键值
#define POS_X 24 //蛇初始位置
#define POS_Y 5 //蛇初始位置
 
//节点类型
typedef struct SnakeNode
{
  //节点的坐标
  int x;
  int y;
  //指向下一个节点的指针
  struct SnakeNode* next;
}SnakeNode;
typedef struct SnakeNode*  pSnakeNode;
 
//贪吃蛇的信息
typedef struct Snake
{
  pSnakeNode _pSnake;//贪吃蛇的身体节点
  pSnakeNode _pFood;//食物节点
  enum Direction _dir;//贪吃蛇的方向
  enum Game_Statues _status;//贪吃蛇的状态
  int _food_weight;//一个食物的分数
  int _score;//总分数
  int _sleep_time;//休息时间,即贪吃蛇的速度
}Snake;
typedef struct Snake* pSnake;
 
//方向
enum Direction
{
  UP,
  DOWN,
  LEFT,
  RIGHT
};
 
//状态
enum Game_Status
{
  OK,
  KILL_BY_WALL,
  KILL_BY_SELF,
  END_NORMAL
};
 
//游戏开始
void GameStart(pSnake ps);
//欢迎函数
void WelcomeToGame();
//定位坐标
void SetPos(int x, int y);
//打印地图
void CreateMap();
//初始化贪吃蛇
void InitSnake(pSnake ps);
//创造食物
void CreateFood(pSnake ps);
//游戏运行
void GameRun(pSnake ps);
//打印帮助信息
void PrintHelpInfo();
//暂停设置
void Pause();
//实现贪吃蛇的移动
void SnakeMove(pSnake ps);
//判断是否吃到食物
int NextIsFood(pSnakeNode pn, pSnake ps);
//实现贪吃蛇吃食物并增长蛇身
void EatFood(pSnakeNode pn, pSnake ps);
//吃到食物后使食物消失
void NoFood(pSnakeNode pn, pSnake ps);
//被墙杀死
void KillByWall(pSnake ps);
//被自己杀死
void KillBySelf(pSnake ps);
//游戏结束
void GameEnd(pSnake ps);

<snake.c>文件

#include "snake.h"
//游戏开始
void GameStart(pSnake ps)
{
  //设置窗口
  system("mode con cols=100 lines=30");//调整CMD行与列
  system("title 贪吃蛇");//修改CMD的标题
 
  //获取标准输出的句柄,存放在houtput中。
  HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  if (houtput == INVALID_HANDLE_VALUE) // 处理错误,例如输出错误信息
  {
    fprintf(stderr, "Failed to get standard output handle.\n");
    return;
  }
 
  //创建一个CONSOLE_CURSOR_INFO的结构体
  CONSOLE_CURSOR_INFO CursorInfo;
  if (!GetConsoleCursorInfo(houtput, &CursorInfo)) // 处理错误,例如输出错误信息
  {
    fprintf(stderr, "Failed to get console cursor info.\n");
    return;
  }
 
  //隐藏控制台光标
  CursorInfo.bVisible = false; 
  if (!SetConsoleCursorInfo(houtput, &CursorInfo)) // 处理错误,例如输出错误信息
  {
    fprintf(stderr, "Failed to set console cursor info.\n");
    return;
  }
  
  //欢迎函数
  WelcomeToGame();
  //打印地图
  CreateMap();
  //初始化贪吃蛇
  InitSnake(ps);
  //设置食物的位置
  CreateFood(ps);
}
 
//欢迎函数
void WelcomeToGame()
{
  SetPos(32, 13);
  printf("Welcome to the Classic Snake Game!");
  SetPos(39, 22);
  system("pause");//打印完一个界面后直接暂停,直到点击继续
  system("cls");//在清空界面,打印新的一个界面
  SetPos(30, 13);
  wprintf(L"Navigate the Snake using ↑ ↓ ← →.");
  SetPos(33, 15);
  wprintf(L"Accelerate to earn more points.");
  SetPos(38, 23);
  system("pause");
  system("cls");
}
 
//定位坐标
void SetPos(int x, int y)
{
  //获取标准输出的句柄,存放在houtput中
  HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  //设定我们想要定位的坐标
  COORD pos = { x,y };
  //将光标定位到pos2
  SetConsoleCursorPosition(houtput, pos);
}
 
//打印地图
void CreateMap()
{
  int i = 0;
  //打印上边界
  for (i = 0; i < 29; i++)
  {
    wprintf(L"□");
  }
  //打印下边界
  SetPos(0, 26);
  for (i=0; i < 29; i++)
  {
    wprintf(L"□");
  }
  //打印左边界
  for (i = 1; i <= 25; i++)
  {
    SetPos(0, i);
    wprintf(L"□");
  }
  //打印右边界
  for (i = 1; i <= 25; i++)
  {
    SetPos(56, i);
    wprintf(L"□");
  }
}
 
//初始化贪吃蛇
void InitSnake(pSnake ps)
{
  int i = 0;
  for (i = 0; i < 5; i++)//开始贪吃蛇一共设置五个长度
  {
    pSnakeNode  cur = (pSnakeNode)malloc(sizeof(SnakeNode));
    if (cur == NULL)
    {
      perror("InitSnake error");
      exit(1);
    }
    cur->next = NULL;
    cur->x = POS_X + 2 * i;
    cur->y = POS_Y;
    //头插法
    if (ps->_pSnake == NULL)
    {
      ps->_pSnake = cur;
    }
    else
    {
      cur->next = ps->_pSnake;
      ps->_pSnake = cur;
    }
  }
  pSnakeNode  cur = ps->_pSnake;
  while (cur != NULL)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"●");
    cur = cur->next;
  }
  //设置贪吃蛇的属性
  ps->_dir = RIGHT;
  ps->_score = 0;
  ps->_food_weight = 10;
  ps->_sleep_time = 200;
  ps->_status = OK;
}
 
//设置食物的位置
void CreateFood(pSnake ps)
{
  int x;
  int y;
again:
  do
  {
    x = (rand()) % 53 + 2;
    y = (rand()) % 25 + 1;
  } while (x % 2 != 0);
  //不能与蛇身冲突
  pSnakeNode  cur = ps->_pSnake;
  while (cur != NULL)
  {
    if ((x == cur->x) && (y == cur->y))
    {
      goto again;
    }
    cur = cur->next;
  }
  //创建食物节点
  pSnakeNode  pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
  if (pFood == NULL)
  {
    perror("CreateFood error");
    exit(1);
  }
  pFood->x = x;
  pFood->y = y;
  pFood->next = NULL;
  SetPos(x, y);
  wprintf(L"★");
  ps->_pFood = pFood;
}
 
//游戏运行
void GameRun(pSnake ps)
{
  //打印欢迎界面
  PrintHelpInfo();
  //游戏开始运行
  do
  {
    //显示分数
    SetPos(64, 7);
    printf("Current score: %d", ps->_score);
    SetPos(64, 8);
    printf("Current food score: %2d", ps->_food_weight);
    //判断玩家操作
    if (KEY_PRESS(VK_UP) && (ps->_dir != UP))
    {
      ps->_dir = UP;
    }
    else if (KEY_PRESS(VK_DOWN) && (ps->_dir != DOWN))
    {
      ps->_dir = DOWN;
    }
    else if (KEY_PRESS(VK_LEFT) && (ps->_dir != LEFT))
    {
      ps->_dir = LEFT;
    }
    else if (KEY_PRESS(VK_RIGHT) && (ps->_dir != RIGHT))
    {
      ps->_dir = RIGHT;
    }
    else if (KEY_PRESS(VK_SPACE))
    {
      Pause();//暂停设置
    }
    else if (KEY_PRESS(VK_ESCAPE))
    {
      ps->_status = END_NORMAL;
    }
    else if (KEY_PRESS(VK_F3))
    {
      if (ps->_sleep_time > 80)
      {
        ps->_sleep_time -= 30;
        ps->_food_weight += 2;
      }
    }
    else if (KEY_PRESS(VK_F4))
    {
      if (ps->_food_weight > 2)
      {
        ps->_sleep_time += 30;
        ps->_food_weight -= 2;
      }
    }
    //实现贪吃蛇的移动
    SnakeMove(ps);
    //通过短暂暂停来展现动态效果
    Sleep(ps->_sleep_time);
  } while (ps->_status == OK);
}
 
//打印欢迎界面
void PrintHelpInfo()
{
  SetPos(64, 10);
  wprintf(L"No wall passing. No self-biting.");
  SetPos(64, 12);
  wprintf(L"F3 to speed up. F4 to slow down.");
  SetPos(64, 14);
  wprintf(L"ESC to exit. Space to pause.");
  SetPos(74, 21);
  wprintf(L"Made by HSY,");
  SetPos(66, 22);
  wprintf(L"a uniquely independent pig.");
}
 
//暂停设置
void Pause()
{
  while (1)
  {
    Sleep(200);
    if (KEY_PRESS(VK_SPACE))
    {
      break;
    }
  }
}
 
//实现贪吃蛇的移动
void SnakeMove(pSnake ps)
{
  pSnakeNode  pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
  if (pNextNode == NULL)
  {
    perror("SnakeMove error");
    exit(1);
  }
  switch (ps->_dir)
  {
  case UP:
    pNextNode->x = ps->_pSnake->x;
    pNextNode->y = ps->_pSnake->y - 1;
    break;
  case DOWN:
    pNextNode->x = ps->_pSnake->x;
    pNextNode->y = ps->_pSnake->y + 1;
    break;
  case LEFT:
    pNextNode->x = ps->_pSnake->x - 2;
    pNextNode->y = ps->_pSnake->y;
    break;
  case RIGHT:
    pNextNode->x = ps->_pSnake->x + 2;
    pNextNode->y = ps->_pSnake->y;
    break;
  }
  if (NextIsFood(pNextNode,ps))
  {
    EatFood(pNextNode, ps);
  }
  else
  {
    NoFood(pNextNode, ps);
  }
  KillByWall(ps);//被墙杀死
  KillBySelf(ps);//被自己杀死
}
 
//判断是否吃到食物
int NextIsFood(pSnakeNode pn, pSnake ps)
{
  return (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y);
}
 
//实现贪吃蛇吃食物并增长蛇身
void EatFood(pSnakeNode pn, pSnake ps)
{
  ps->_pFood->next = ps->_pSnake;
  ps->_pSnake = ps->_pFood;
  free(pn);
  pn = NULL;
  pSnakeNode cur = ps->_pSnake;
  while (cur!=NULL)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"●");
    cur = cur->next;
  }
  ps->_score += ps->_food_weight;
  CreateFood(ps);
}
 
//吃到食物后使食物消失
void NoFood(pSnakeNode pn, pSnake ps)
{
  pn->next = ps->_pSnake;
  ps->_pSnake = pn;
  pSnakeNode  cur = ps->_pSnake;
  while (cur->next->next != NULL)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"●");
    cur = cur->next;
  }
  SetPos(cur->next->x, cur->next->y);
  printf("  ");
  free(cur->next);
  cur->next = NULL;
}
 
//被墙杀死
void KillByWall(pSnake ps)
{
  if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 || ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
  {
    ps->_status = KILL_BY_WALL;
  }
}
 
//被自己杀死
void KillBySelf(pSnake ps)
{
  pSnakeNode cur = ps->_pSnake->next;
  while (cur)
  {
    if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
    {
      ps->_status = KILL_BY_SELF;
      break;
    }
    cur = cur->next;
  }
}
 
//游戏结束
void GameEnd(pSnake ps)
{
  //判断哪种结束方式
  switch (ps->_status)
  {
  case END_NORMAL:
    SetPos(17, 12);
    printf("You have ended the game.");
    break;
  case KILL_BY_WALL:
    SetPos(10, 12);
    printf("You ended the game by hitting a wall.");
    break;
  case KILL_BY_SELF:
    SetPos(10, 12);
    printf("You ended the game by self-collision.");
    break;
  }
  //清除贪吃蛇
  pSnakeNode cur = ps->_pSnake;
  pSnakeNode prev = NULL;
  while (cur)
  {
    prev = cur;
    cur = cur->next;
    free(prev);
  }
 
}

<test.c>文件

#include "snake.h"
//游戏的主体进程
void test()
{
  char ch;
  do
  {
    system("cls");
    Snake snake = { 0 };
    GameStart(&snake);//游戏开始
    GameRun(&snake);//游戏运行
    GameEnd(&snake);//游戏结束
    SetPos(20, 15);//结束之后,询问是否再来一次
    printf("Play again? (Y/N)");
    ch = getchar();//用户输入一个字符并按回车后,实际上有两个字符进入了输入缓冲区:用户输入的字符和随后的换行符。第一个 getchar() 会读取用户输入的字符,而第二个 getchar() 则用来读取(并丢弃)换行符。
    getchar();
  } while (ch == 'Y'|| ch == 'y');
  SetPos(0, 28);//如果游戏结束,(为了美观)退出代码定位
}
 
//主函数
int main()
{
  //设置本地环境
  setlocale(LC_ALL, "");
  //生成随机值
  srand((unsigned int)time(NULL));
  //测试游戏
  test();
  return 0;
}

致谢

 感谢您花时间阅读这篇文章!如果您对本文有任何疑问、建议或是想要分享您的看法,请不要犹豫,在评论区留下您的宝贵意见。每一次互动都是我前进的动力,您的支持是我最大的鼓励。期待与您的交流,让我们共同成长,探索技术世界的无限可能!

相关文章
|
11月前
|
存储 C语言 C++
【c语言】运算符汇总(万字解析)
今天博主跟大家分享了c语言中各种操作符的功能、使用方法以及优先级和结合性,并且与大家深入探讨了表达式求值的两个重要规则--算数转换和整形提升。学习这些知识对我们的C语言和C++学习都有着极大的帮助。
656 2
|
Kubernetes 网络协议 关系型数据库
Kubernetes----ExternalName类型的Service
Kubernetes----ExternalName类型的Service
2698 0
|
C语言 定位技术 API
【C语言】实践:贪吃蛇小游戏(附源码)(二)
【C语言】实践:贪吃蛇小游戏(附源码)
【C语言】实践:贪吃蛇小游戏(附源码)(二)
|
3月前
|
人工智能 安全 API
一件数字藏品的全球之旅:Web3.0 API如何实现“链上确权+链下流通”?
Web3.0以区块链为核心,将数据主权归还用户,而API则成为连接分布式节点的“数字纽带”,推动去中心化应用实现跨链、跨平台数据互联。本文探讨Web3.0与API融合如何重塑互联网生态,开启数据价值流通新时代。
|
12月前
|
人工智能 算法 定位技术
元宇宙房地产:虚拟土地的价值评估
【10月更文挑战第28天】随着科技发展,元宇宙成为融合虚拟现实、增强现实和区块链技术的数字世界。本文探讨元宇宙房地产中的虚拟土地价值评估,分析其影响因素、评估方法及未来发展趋势,包括地理位置、平台流量、土地用途、用户基础、技术创新等方面。未来,元宇宙房地产将呈现跨平台交易、定制化城市建设及金融产品创新等趋势。
|
Python
NumPy 教程 之 NumPy 数学函数 2
本教程详细展示了如何使用 NumPy 进行三角函数运算,包括正弦(`sin()`)、余弦(`cos()`)、正切(`tan()`)及其反函数。通过实例,我们计算了一系列角度的三角函数值,并利用 `arcsin()`、`arccos()` 和 `arctan()` 计算了对应的反三角函数值。此外,还演示了如何借助 `numpy.degrees()` 将弧度单位转换为角度单位,验证计算结果。
185 1
|
人工智能 搜索推荐 API
一键解锁:快速上手文心一言指令编程实践
【7月更文第18天】随着人工智能技术的飞速发展,对话式AI已经成为连接人与信息的新桥梁。百度的“文心一言”(ERNIE)作为国内领先的预训练语言模型,以其强大的语义理解和生成能力,正逐步改变我们获取信息和交互的方式。本文旨在为开发者提供一份快速上手指南,通过实际代码示例,深入浅出地介绍如何利用文心一言API进行指令编程,解锁AI对话新体验。
705 7
|
机器学习/深度学习 人工智能 供应链
人工智能在社会中的影响与未来展望
人工智能(AI)作为一项重要的技术革新,正在深刻地改变着我们的生活方式、工作方式以及社会结构。本文旨在探讨人工智能在社会中的影响,并展望其未来发展趋势。首先,我们回顾了人工智能的发展历程,从其起源到当前的发展阶段,概述了人工智能技术的基本原理和应用领域。其次,我们分析了人工智能对各个行业的影响,包括但不限于医疗保健、金融、制造业、交通运输和教育等领域。人工智能的广泛应用使得这些行业更加智能化、高效化,但也带来了一些挑战,如就业岗位的变化、隐私安全问题等。接着,我们讨论了人工智能在社会中的伦理和道德问题,包括人工智能算法的公平性、透明性和责任问题。最后,我们展望了人工智能的未来发展,包括强化学习
773 0
|
监控 Unix Linux
ps aux 命令使用查看内存、cpu使用排名与top的区别
ps aux 命令使用查看内存、cpu使用排名与top的区别
1763 1