C++实现cmd界面简单贪吃蛇游戏

article/2025/9/30 10:39:16

贪吃蛇的玩法我想应该大家都是耳熟能详了。但是这游戏虽然简单,但是编写的难度对一个刚刚学完c++,准备考研的苦逼大学生来说却是一件非常艰难的事情。

date:10月3日,国庆节的头3天,大家在外玩耍我却苦逼的在这里写代码痛苦ing,不知道痛苦会不会持续(有发泄情绪的成分嘿嘿),i don't want that

 接下来,我会说明本次的代码中实现的各个函数的功能,已经调用外部一些包的解释---同样也是我新学的,对我自己来说也是一种学习。

那贪吃蛇需要实现什么功能呢,如何分步骤?

1.如何画网格?

2.蛇撞墙或者吃东东后会怎么样?

3.蛇的方向控制怎么实现?

问完自己3个问题后就可以开始开发啦,首先先定义一个20*20(可改)的一个正方形的网格图,那这种网格图的话,一定是有墙的喽,我这里把墙和蛇儿可以移动的砖块分别用1,0表示,这和扫雷有些类似哦,所以这些的变量得通过一个函数init_map函数来实现,这个函数就是用来初始化区域的。

void init_map(int** map,int high,int width) {for (int i = 0; i < width; ++i) {for (int j = 0; j < high; ++j) {if (i == 0 || i == width - 1) map[i][j] =1;//表示是墙else if(j == high - 1 || j == 0)map[i][j] = 1;elsemap[i][j] = 0;//剩余的情况就是会是空格}cout << "\n";//这个就是一个输入输出流,是属于instream包中的}return;
}

如果定义好了活动区域和墙壁区域,那蛇的定义还会远 吗???很明显,不会啊,那好蛇如何来定义呢?蛇头是不是应该用一个别的图形来当比较好,这里的话我把具体的蛇抽象成了链表,刚刚好,头尾相连非常的适宜,那使用链表之前也得在一开始时候就有定义吧,所以,一开始得定义一个链表的结构体---在最后完整代码中会给上,那使用了结构体后就得对蛇开始一个初始化喽,我这里用到的函数就是init_snake,但是用的也是结构体指针的形式哦,指针真是一个神奇的东西呢,这个p其实就是指向s的一个移动结构体。通过这次的小游戏开发确实是让我受益匪浅。

就好像我和你一样处于同个起跑线,10年后,我还是我,你已经不是你了。哦我的天


struct snake* init_snake() {struct snake* s = (struct snake*)malloc(sizeof(struct snake));//申请一个头struct snake* p = s;//其实这个p是一个移动的结构体指针int x[6] = {4,5,6,7,8,9};int y[6] = {4,4,4,4,4,4};//这是一开始蛇头的位置for (int i = 0; i < 6; i++) {struct snake* node = (struct snake*)malloc(sizeof(struct snake));p->next = node;//这里等同于s->next =nodep = p->next;p->i = x[i];p->j = y[i];//定义了的坐标}p->next = NULL;return s;//这里是关键哦,返回的值还是s表头,但是它里面的数据居然是通过for循环后的,你说神奇不神奇呢???
}	

接下来的函数来头就有些大了,他是用来获取某个位置的,x,y表示,当然了,我认为现在的工作中应该不会用了,就知道一下就好。//这个只需要脊柱就行了

	
void gotoxy(int i, int j) {COORD position;position.X = i;position.Y = j;//调用PISetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);//这条语句前面的第一个参数一般是固定的呢,后面的参数是需要填写一个结构体的//刚刚好position是符合这一特性的
}

根据名字就知道了,是用来画蛇的,之前就定义过了嘛,因为这里的话蛇它的身体的表示是和墙一样的,这里很巧妙的一个设计就是,它自己的身体也是墙!!!就是撞自己等于撞墙了,这里我把蛇头定义成了2,其他的部分就变成了1,然后显示在map中,别急后边有一个判定的可以用来,把数据重新加载。


void drawsnake(int** map, struct snake* s) {struct snake* p = s->next;map[p->i][p->j] = 2;//第一个表示额是蛇头p = p->next;while (p){map[p->i][p->j] = 1;p = p->next;}return;
}

很好,蛇,墙壁,版面都画好了,接下来就还剩下那个魔鬼撒旦要吃的“苹果”了,这里是用小星星来实现的,首先是对场上所有的空白的那个格子统计,就是得统计有多少个,然后通过在全局模式下定义的,然后再行进逐条排查当是空格子的时候再给他减掉,最后剩下的那个空格子就是随机的啦,然后给他塞进去“食物”就好了。//其实这个也不算画,就是对星星一个定义罢了


void draw_star(int** map, int* star, int hight, int width) {int sum = 0;int index = 0;//计算出空格子的数量for (int i = 0; i < hight; ++i) {for (int j = 0; j < width; ++j) {if (map[i][j] == 0) sum += 1;}}index = random(sum) + 1;//从1到sum中随机生成星星for (int i = 0; i < hight; ++i) {for (int j = 0; j < width; ++j) {if (map[i][j] == 0) {index -= 1;}if (index == 0) {star[0] = i;star[1] = j;return;}}}return;
}

接下来就是画图了,你看最开始的时候是有一个清屏的,所以这次的游戏就是建立在,每次的不断刷新上的,再看第二句就是给定位重新的到0,0,接下来就是判定了,根据之前写的那些鬼玩意儿,进行判定是蛇头,蛇神,墙体(当然墙体和蛇神是一个道理),食物,空砖头等。


void draw(int** map,int* star,int height,int width) {system("cls");//清除屏幕,就是每次的屏幕刷新gotoxy(0, 0);//光标定位到0,0for (int i = 0; i < height; ++i) {for (int j = 0; j < width; ++j) {if (map[i][j] == 1)//1表示的是墙cout << "■";else if (map[i][j] == 2)//2表示的是哪个蛇头cout << "●";else if (i == star[0] && j == star[1])cout << "★";elsecout << "□";}cout << "\n";//每次输出完后需要空一行}return;
}

接下来的话就是比较过分的一个代码了,之所以说他很过分的原因就是他这个又臭又长,而且重点是什么?不好意思,刚刚差点骂人,backspace回去了,里面有些代码真的很重复,我后面看看能不能给他改进了吧。好接下来说一下这个代码,首先是定一个新的蛇结构体指针,对它进行传参判定,还有蛇的吃,移动,撞墙的判定。

void updata_snake(char c, struct snake* s, int* star) {snake* newsnake;newsnake = (snake*)malloc(sizeof(snake));if (c == 'w') {//把蛇网上移动一格newsnake->i = s->next->i - 1;//网上的话就是i-1,也就是直角坐标系上纵坐标+1newsnake->j = s->next->j;if (map[newsnake->i][newsnake->j] == 1) {//这里就是判断这蛇撞墙了的情况sign = 3;//给个信号就是蛇挂了}if (newsnake->i == star[0] && newsnake->j == star[1]) {//表示吃到星星了哦,congratulationnewsnake->next = s->next;s->next = newsnake;//这里就是添加struct snake* q = s;sign = 2;//这个就是吃到星星了的意识}else {newsnake->next = s->next;//因为移动的话一般都是关乎蛇头的嘛//然后后面的也不会动的,这时候的蛇头就变成蛇身子了s->next = newsnake;//这里进行好后,也就是说会多出一个尾巴来struct snake* q = s;//定位到本来是蛇头的位置???while (q->next->next != NULL) {q = q->next;}map[q->next->i][q->next->j] = 0;//这里是定位到蛇尾的位置的free(q->next);q->next = NULL;}}else if (c == 'a') {//把蛇往左移一格newsnake->i = s->next->i;newsnake->j = s->next->j-1;if (map[newsnake->i][newsnake->j] == 1) {//这里就是判断这蛇撞墙了的情况sign = 3;//给个信号就是蛇挂了}if (newsnake->i == star[0] && newsnake->j == star[1]) {//表示吃到星星了哦,congratulationnewsnake->next = s->next;s->next = newsnake;//这里就是添加struct snake* q = s;sign = 2;//这个就是吃到星星了的意识}else {newsnake->next = s->next;//因为移动的话一般都是关乎蛇头的嘛//然后后面的也不会动的,这时候的蛇头就变成蛇身子了s->next = newsnake;//这里进行好后,也就是说会多出一个尾巴来struct snake* q = s;//定位到本来是蛇头的位置???while (q->next->next != NULL) {q = q->next;}/*	gotoxy(q->i, q->j);printf("■");*/map[q->next->i][q->next->j] = 0;//这里是定位到蛇尾的位置的free(q->next);q->next = NULL;}}else if (c == 'd') {newsnake->i = s->next->i;newsnake->j = s->next->j + 1;if (map[newsnake->i][newsnake->j] == 1) {//这里就是判断这蛇撞墙了的情况sign = 3;//给个信号就是蛇挂了}if (newsnake->i == star[0] && newsnake->j == star[1]) {//表示吃到星星了哦,congratulationnewsnake->next = s->next;s->next = newsnake;//这里就是添加struct snake* q = s;sign = 2;//这个就是吃到星星了的意思}else {newsnake->next = s->next;//因为移动的话一般都是关乎蛇头的嘛//然后后面的也不会动的,这时候的蛇头就变成蛇身子了s->next = newsnake;//这里进行好后,也就是说会多出一个尾巴来struct snake* q = s;//定位到本来是蛇头的位置???while (q->next->next != NULL) {q = q->next;}map[q->next->i][q->next->j] = 0;//这里是定位到蛇尾的位置的free(q->next);q->next = NULL;}}else {newsnake->i = s->next->i+1;//因为移动的话一般都是关乎蛇头的嘛//然后后面的也不会动的,这时候的蛇头就变成蛇身子了newsnake->j = s->next->j;if (map[newsnake->i][newsnake->j] == 1) {//这里就是判断这蛇撞墙了的情况sign = 3;//给个信号就是蛇挂了}if (newsnake->i == star[0] && newsnake->j == star[1]) {//表示吃到星星了哦,congratulationnewsnake->next = s->next;s->next = newsnake;//这里就是添加struct snake* q = s;sign = 2;//这个就是吃到星星了的意识}else{newsnake->next = s->next;//因为移动的话一般都是关乎蛇头的嘛//然后后面的也不会动的,这时候的蛇头就变成蛇身子了s->next = newsnake;//这里进行好后,也就是说会多出一个尾巴来struct snake* q = s;//定位到本来是蛇头的位置???while (q->next->next != NULL) {q = q->next;}map[q->next->i][q->next->j] = 0;//这里是定位到蛇尾的位置的free(q->next);q->next = NULL;}}
}

 这个就是完全为了上面的函数服务的,我觉得上面那个函数应该是难点。这个函数就是判定输入的是字符是多少


void action_snack(struct snake* s,int* star) {//if (_kbhit()) {//	c = _getch();//	//这里呢_kbhit函数是一个连续敲击,就是会一直执行下去//	//而c呢就是记录每次敲击下的字母或者是字符//}switch (c){case 'w':updata_snake(c, s,star);break;//这里还有一个函数就是更新数据,相当于每次敲击后//蛇儿的动向都需要更新的case 's':updata_snake(c, s,star); break;case 'a':updata_snake(c, s,star);break;case 'd':updata_snake(c, s,star); break;}}

 接下来就是自动化了,因为定义的c是一个全局所以在后续每次的循环中都是起到同样的作用,还有判定按键冲突的bug修复


void check_snake() {DWORD time = 500;//1表示一毫秒 1000表示一秒 要改变速度可以修改timeDWORD time_start = GetTickCount();//gettickcount返回的是操作系统到现在经历的毫秒数char t;//Dword 全程是double word//那这里为何要用到DWORD呢?原因是int在各个机器上的位数不一样//而且DWORD是windows包里面的东西while (true) {if (_kbhit()) {char ch = _getch();//这俩就是一个组合,获得当前键位用的if (ch == 97 || ch == 100 || ch == 115 || ch == 119) {//a-97 d-100 s-115 w-119t = ch;if (t == 97 && c == 'd') {c = 'd';}else if (t == 100 && c == 'a') {c = 'a';}else if (t == 115 && c == 'w') {c = 'w';}else if (t == 119 && c == 's') {c = 's';}else {c = ch;}}}DWORD time_end = GetTickCount();//获取键位if (time_end - time_start > time) {time_start = time_end;break;}}return;
}

 最后的最后就是一个main,和之前说的一样,首先是进行对墙,空地,食物进行标出


int main() {//1.首先是画出地图哦int hight = 20;int width = 20;struct snake* s = init_snake();//定一个mapmap = (int**)malloc(sizeof(int*) * hight);for (int i = 0; i < hight; ++i) {map[i] = (int*)malloc(sizeof(int) * width);//申请的是行的大小}init_map(map, hight, width);//以上的效果大概drawsnake(map, s);int star[2] = { 0,0 };draw_star(map, star, hight, width);draw(map, star, hight, width);while (1) {//这个循环就是会一直执行下去的if (sign == 3) {break;}//也就是说吃到了三个星星就会退出啦check_snake();action_snack(s, star);//action_snack里面嵌套着updata函数init_map(map, hight, width);//重新的初始化一下地图drawsnake(map, s);//同样也得初始化一下蛇if (sign == 2) {draw_star(map, star, hight, width);sign = 0;//sign=2是吃到星星了然后重新计数的}draw(map, star, hight, width);gotoxy(0, 0);//光标重新到0,0}system("cls");gotoxy(hight / 2, width / 2);cout << "游戏结束";system("pause");return 0;
}

 截稿---10.4 凌晨1点,洗洗睡了,顶不住

10.5日早上10:46,起锅烧炉,开始干活啊哈哈哈。


http://chatgpt.dhexx.cn/article/rqtOM0Th.shtml

相关文章

手敲最基础C语言代码----“贪吃蛇”

C语言创作游戏----第二弹----贪吃蛇&#xff08;无限吃&#xff09; 主函数系列&#xff1a; 创建引入头文件----方便查看代码&#xff01;&#xff01; #include<stdio.h> #include<Windows.h> #include<stdlib.h> #include<conio.h> #include<tim…

创建链表和遍历链表算法演示

#include <stdio.h> #include <malloc.h> #include <string.h> #include <stdlib.h>typedef struct Node {int data; //数据域struct Node * pNext; //指针域}Node, *pNode;//函数声明 pNode create_list(); void traverse_list(pNode pHead); int…

C++ 创建链表

本文旨在解决两个问题&#xff1a; 1、如何写一个创建链表函数 2、为什么对于单个节点必须要new&#xff0c;而不能使用& 1、如何写一个创建链表函数 代码如下 ListNode* createListNode(vector<int> input) {ListNode dummy ListNode(-1);ListNode* pre &d…

单链表创建

单链表的创建与操作 链表作为基本的数据结构&#xff0c;学习好链表的创建与操作是数据结构入门的基础。 &#xff08;小白make for myself&#xff09; 单链表的创建 typedef struct Node {int data;struct Node* next; }Node;//结构体创建&#xff0c;也可以使用*Node取址…

动态链表的创建

#include <stdio.h> //List结构样式 typedef struct node { int data; struct node *next; }Node; //创建head的空链 Node *createList() { Node *head (Node *)malloc(sizeof(Node)); if(NULL head) exit(-1); head->next NULL; return head; } Node *insertList(…

C++创建一个链表

这个是在参加面试的时候遇到的题目&#xff0c;说句实话&#xff0c;我当时不懂。 后面查了资料&#xff0c;里面写的比较仔细就不多说了。 #include <iostream> using namespace std; struct node {int data;node* next;node(int data, node* next NULL) {this->d…

如何在Python中创建与使用链表(单链表)

如何在Python中创建与使用链表&#xff08;单链表&#xff09; 最近用Python语言在Leetcode中刷题&#xff0c;接触到不少关于链表的题&#xff0c;学校目前还没有开设数据结构的课程&#xff08;开设的话应该也是以C/C语言写的&#xff09;。 因为不太了解链表使用方式&#…

循环链表的创建

循环链表的创建以及基本操作 上篇我们讲了运用头插法和尾插法创建单链表的方法&#xff0c;和两种方法的比较。 接着我们学习循环链表的创建。 只要学会了单链表的创建&#xff0c;循环链表的创建就变得很简单。 循环链表创建 单链表的结构&#xff1a; 循环链表&#xff1a…

单链表的创建

单链表类型定义 单链表是由一串结点组成的&#xff0c;其中每个结点都包含指向下一个结点的指针&#xff0c;最后一个结点的指针为空&#xff1b; 假设结点只包括一个整数和指向下一结点的指针 typedef struct node{int data;struct node *next; }LNode,*LinkList; //LNode为…

创建双向链表(详解)

双向链表操作 在学习了单链表之后&#xff0c;就顺带学习了双链表的操作。 什么是双链表&#xff1f; 双链表顾名思义&#xff0c;就是链表由单向的链变成了双向链。 使用这种数据结构&#xff0c;我们可以不再拘束于单链表的单向创建于遍历等操作&#xff0c;大大减少了在使…

如何构建一个简单链表

如何构建一个简单链表 一、 含构造函数和默认实参的结构体 typedef struct node {int data;struct node* next;node(int data 0, struct node* next NULL): data(data), next(next) {}} node; 二、 创建一个一定长度的链表 (一) 错误样例&#xff1a; int n 3;node* head …

C++:创建链表的过程详解

创建链表的过程详解 本人是一名刚开始学习算法的小白&#xff0c;今天遇到了一些关于链表的创建问题&#xff0c;查了一些资料&#xff0c;我把它们整理了一下&#xff0c;希望大家多多指教。 整体的代码&#xff1a; #include<iostream> using namespace std;struct …

链表的创建与使用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 目录 文章目录 前言 一、链表是什么&#xff1f; 二、链表的创建与基本操作 1.链表的创建 3.链表的头插 4.链表的尾插 5.链表的销毁 6.链表的查找 7.链表的删除…

链表的创建

目录 一&#xff1a;链表的定义 二&#xff1a;链表的改进 链表的实现可以为后面JAVA的类集框架服务。 链表是一种最简单的数据结构&#xff0c;其主要目的是依靠引用关系实现多个数据的保存。 一&#xff1a;链表的定义 定义一个Node类&#xff0c;保存的数据是String型&a…

C语言之创建链表

自己琢磨着思考了一下书上的单链表的创建案例&#xff0c;记录一下自己的理解 代码如下&#xff1a; #include<stdio.h> #include<stdlib.h>struct Student{char cName[20];int age;struct Student* pNext; }; /*节点数量*/ int iCount0;/*创建链表的函数 返回头…

如何创建链表?

链表&#xff1a; 链表的组成其实很简单&#xff0c;就是由很多结点组成的。 一个结点包含数据域和指针域&#xff0c;数据域用来存放数据&#xff0c;指针域负责指向其他结点&#xff0c;起到链接的作用。创建链表&#xff1a; 其实创建一个链表也很简单&#xff0c;在我看来…

用CodeBlocks写SFML程序

vs2019 写sfml程序简直杀鸡用牛刀&#xff0c;vs2019占用资源太大了。 所以我想到了用Dev-C&#xff0c;然而我不会配置&#xff0c;卑鄙的CSDN相关资料查阅需要VIP&#xff0c;然而VIP太贵了。 SFML官方教程是用Code::Blocks&#xff0c;于是去下一个。 setup安装........ …

[笔记]使用SFML来生成分形图片

前言 最近在上《优秀科普纪录片》时&#xff0c;看了一部有关 分形 的纪录片&#xff0c;在观看的过程中&#xff0c;想着自己也来生成一些分形图片&#xff0c;正好偶然了解到了SFML这个简单的图形库&#xff0c;所以天时地利人和&#xff0c;正好查一些资料来学习一下。 以…

SFML环境配置

材料&#xff1a; 1.visual studio 2017 2.SFML-2.5.1-windows-vc15-32-bit 准备阶段 1.进入SFML官网下载sfml-vs2017-32bit版本 2.将该压缩包解压在一个文件夹中 步骤&#xff1a; 1.进入vs&#xff0c;在上述文件夹中新建Empty Project&#xff0c;右键资源文件->添加-…

[SFML] 多个OpenGL上下文

代码 #include <iostream> #include <gl/glew.h> #include <SFML/Graphics.hpp> #include <windows.h>int main() {auto getInstance [](){return (HINSTANCE)GetModuleHandle(nullptr);};auto debug [](GLenum source, GLenum type, GLuint id, GL…