贪吃蛇(单人版):

实现过程:

本人先来介绍一个函数 —— bioskey函数:

int bioskey (int cmd)

参数 (cmd) 基本功能
0 返回下一个从键盘键入的值(若不键入任何值,则将等下一个键入)它返回一个16位的二进制数,包括两个不同的值:1.当按下一个普通键时,它的低8位数存放该字符的ASCII码,高8位存放该键的扫描码2.对于特殊键(如方向键、F1~F12等 等),低8位为0高8位字节存放该键的扫描码
1 查询是否按下一个键,若按下一个键返回非零值否则返回0
2 bioskey()返回Shift、Ctrl、Alt、ScrollLock、NumLock、CapsLock、Insert键的状态。各键状态存放在返回值的低8位字节中。

在所有操作开始之前,本人先按照以往博文的惯例,编写家师所授的模仿Java中的boolean类型,自定义的伪 boolean型:

typedef unsigned char boolean;

#define TRUE  1
#define FALSE 0

本人先来构造一个可以用来表示一条蛇的结构体:

typedef struct SNAKE{
int head; //这个成员是为了我们之后判断蛇头方向用的
int len; //这个成员用来记录蛇当前 “应该”有的长度
int curlen; //这个成员用来记录蛇当前 “实际”的长度
int direct; //这个成员表示键盘输入的指令
SNAKE_BODY *snake; //这个成员是我们用来存储蛇的身体所在坐标用的
}

现在,我们构造一个能够存储蛇身体信息的结构体:

typedef struct SNAKE_BODY{
int xPostion;
int yPostion;
}SNAKE_BODY;

那么,为了,根据我们上面的蛇头和蛇方向的成员,我们现在来给出两个数组来存储蛇头的形状 和 蛇的方向:

首先,我们再来构造一个结构体,用来存储辅助蛇移动的结构体:

typedef struct DELTA_MOVE{
int deltRow;
int deltCol;
}DELTA_MOVE;

那么,现在本人就可以根据以上的结构体来给出两个数组了:

DELTA_MOVE delta[4] = {
{0, -1}, //向上运动
{0, 1}, //向下运动
{-1, 0}, //向左运动
{1, 0} //向右运动
}; char SnakeHead[4] = {
"^", "v", "<", ">" //这个数组存储的蛇头方向的 “上下左右”,分别存在下标为0、1、2、3的单元中
}

现在,根据上面的数组,我们来编写一个获取蛇头形状的函数:

char getHeadType(int snakeHeadIndex) {
return snakeHead[snakeHeadIndex];
}

那么,现在我们就可以初始化一条蛇了:

#define MAX_LEN 	1000
//我们设定蛇最长为100(也可以设置地大一点,一般玩家都不会玩到100,所以本人设置最长长度是 100)
//由于最长长度是我们用宏定义定义的,所以,我们之后如果想要改的话,在这里改也比较方便 SNAKE player = {
0, //设定 开始时 蛇头信息存储在下标为0的数组空间内
5, //设定 开始时 蛇应该有5个长度
1, //因为刚开始是一个点,所以初始长度是1
3, //因为向右运动,所以设定运动方向是 右
NULL}; //因为我们还没有初始化身体,所以先令身体为NULL SNAKE_BODY snakeBody[MAX_LEN] = {{0,0}};

现在,我们来制定一下边界,使得我们做出来的游戏界面更加美观:

#define WALL -2

int screenPoint[Full_Screen_COUNT] = {0};	//整个屏幕一共分为25行、80列

void init() {
int i;
int j;
int tmp; for(i = 1; i <= 80; i++) {
for(j = 1; j <= 25; j++) {
if(!(i > 1 && i < 80 && j > 1 && j < 25)) {
tmp = (j - 1) * 80 + i - 1;
screenPoint[tmp] = WALL;
}
}
}
clrscr();
gotoxy(22, 12); /*这里的行、列值完全是一个点一个点测出来的,为了使下面的话能够在屏幕正中央显示*/
printf("Enter any char to start the game!");
} void showBorder() {
int i; clrscr();
for(i = 2; i < MAX_X; i++) { /*将“上下墙壁”显示出来*/
gotoxy(i, 2);
putch(223);
gotoxy(i, 25);
putch(220);
}
for(i = 2; i <= MAX_Y; i++) { /*将“左右墙壁”显示出来*/
gotoxy(1, i);
putch(219);
gotoxy(80, i);
putch(219);
}
}

现在,蛇 和 边界 的问题我们就基本上解决了,现在,我们来处理一个更为重要的事——从键盘读取有效指令,并将在未获得指令时执行上一次的指令:

#define UP	  				0x4800
#define DOWN 0x5000
#define LEFT 0x4b00
#define RIGHT 0x4d00 //以下两行:设置右侧数字键盘那里的+、-为加速减速键
#define PGUP 0x4900
#define PGDN 0x5100 #define ESC 0x11b #define MAX_COUNT 20000
#define MIN_COUNT 125
#define DEFAULT_COUNT 5000
//以上的宏定义,分别将各字符定义为其“键盘扫描码”,以便我们之后的函数的使用 int readValidOrder(int key, int *tempCount, SNAKE *snake) {
int tmp; tmp = snake->direct;
switch(key) {
case RIGHT: return tmp == 2 ? 2 : 3;
case LEFT: return tmp == 3 ? 3 : 2;
case DOWN: return tmp == 0 ? 0 : 1;
case UP: return tmp == 1 ? 1 : 0; /*上面的返回值分别对应“上”、“下”、“左”、“右”在“蛇头类型数组”中的下标*/ case PGUP: if(*tempCount > MIN_COUNT) {
*tempCount = *tempCount / 2;
}
return -1;
case PGDN: if(*tempCount < MAX_COUNT) {
*tempCount = *tempCount * 2;
}
return -1;
}
} int main() {
int key;
int newKey;
int tempIndex;
int i = 0; //因为编译软件运行地太快,所以,为了能让我们反应地过来,我们使得计算机需运行cd次,才能有效执行一次
int cdTime = DEFAULT_COUNT; //(即:通俗来讲,就是为蛇的自动移动加一个cd,使得蛇移动速度降低) SNAKE_BODY snakeBody[MAX_LEN] = {{0,0}}; SNAKE *player = {
0,
5,
1,
3,
NULL};
hideCursor(); init();
getch();
snakeBody[0].xPostion = headXPos;
snakeBody[0].yPostion = headYPos;
player.sb = snakeBody; clrscr();
showBorder();
while(//TODO 蛇未死亡) {
i++;
key = bioskey(1);
if(key != 0){
newKey = bioskey(0);
if(newKey == ESC) {
break;
}
tempIndex = readValidOrder(newKey, &cdTime, &player);
if(tempIndex >= 0 && tempIndex <= 3) {
player.direct = tempIndex;
snakeHeadType = getHeadType(player.direct);
}
}
if(i > cdTime) {
//TODO 生成食物
//TODO 蛇移动
i = 0;
}
}
//善后处理
}

现在,我们来实现下蛇移动的要求:

#define BLOCK 0

void move(int *headXPos, int *headYPos, DELTA_MOVE *delta, char headType, SNAKE *snake) {
int tailRow;
int tailCol;
int tail;
int tempX;
int tempY; tempX = snake->sb[snake->head].xPostion;
tempY = snake->sb[snake->head].yPostion;
gotoxy(tempX, tempY);
printf("*");
tempX = tempX + delta->deltRow;
tempY = tempY + delta->deltCol;
gotoxy(tempX, tempY);
printf("%c", headType);
snake->head = (snake->head + 1) % MAX_LEN;
snake->sb[snake->head].xPostion = tempX;
snake->sb[snake->head].yPostion = tempY;
*headXPos = tempX;
*headYPos = tempY;
if(snake->curLen < snake->len) {
(snake->curLen)++;
return;
} tail = (snake->head - snake->len + MAX_LEN) % MAX_LEN;
tailRow = snake->sb[tail].xPostion;
tailCol = snake->sb[tail].yPostion;
screenPoint[(tailCol - 1) * MAX_X + tailRow - 1] = 0;
gotoxy(tailRow, tailCol);
printf(" ");
}

接下来就是产生食物的函数了:

void creatFoodNum() {
foodNum = rand()%2 ? 3 : 2;
} void dealFood() {
dealFoodIndex();
showFood();
} /*显示食物*/
void showFood() {
int i;
int x;
int y; for(i = 0; i < Full_Screen_COUNT; i++) {
if(screenPoint[i] == USUAL_FOOD || screenPoint[i] == SUPER_FOOD) {
x = (i + 1) % MAX_X;
y = (i + 1) / MAX_X + 1;
gotoxy(x, y);
printf((screenPoint[i] == USUAL_FOOD) ? "#" : "$");
}
}
eatUpFood = FALSE;
} /*放置食物*/
void dealFoodIndex() {
int randNum;
int i;
int count = 0;
int index[Full_Screen_COUNT] = {0}; /*这个数组是为我们之后的“发牌算法”的使用做准备*/
int j = Full_Screen_COUNT-1; for(i = 0; i < Full_Screen_COUNT; i++) {
if(screenPoint[i] == BLOCK) {
index[count] = i;
count++;
}
} for(i = 0; i < foodNum; i++) {
srand(time(0) + i);
randNum = rand() % count; screenPoint[index[randNum]] = rand()%2 ? USUAL_FOOD : SUPER_FOOD;
index[randNum] = index[j--];
count--;
}
}

那么,现在基本的准备就准备好了,我们现在来解决一下生成食物和蛇吃食变长的问题:

#define BLOCK  		 		0
#define USUAL_FOOD 2
#define SUPER_FOOD 1
#define BODY -1
#define WALL -2 boolean dealEat(SNAKE *snake) {
int i;
int k;
int tmp;
int tempHeadX;
int tempHeadY; tempHeadX = snake->sb[snake->head].xPostion;
tempHeadY = snake->sb[snake->head].yPostion;
tmp = (tempHeadY - 1) * MAX_X + tempHeadX - 1;
if((screenPoint[tmp] == USUAL_FOOD) || (screenPoint[tmp] == SUPER_FOOD)) { /*吃到食物*/
foodNum--;
screenPoint[tmp] = 0;
(snake->len) += (screenPoint[tmp] == USUAL_FOOD) ? 1 : 2;
} if(screenPoint[tmp] == BODY) { /*吃到自身*/
clrscr();
gotoxy(40, 12);
printf("Game over!");
getch(); return TRUE;
} screenPoint[(tempHeadY - 1) * MAX_X + tempHeadX - 1] = BODY; /*将这个点转换为蛇身*/
if(foodNum <= 0) {
eatUpFood = TRUE;
creatFoodNum();
}
return FALSE;
}

单人版完整代码:

Single-Version


那么,单人版的贪吃蛇我们就做好了。

本人现在来展示以下运行结果:

贪吃蛇 单人版

那么,作为本人的最后一篇《数据结构与算法》专栏的博文,当然不能就这么草草了事,本人既然提到了单人版,那么,在这篇博文中就要讲到进阶版——贪吃蛇(双人版)

贪吃蛇(双人版):

因为上面有本人的单人版的说明,那么接下来的双人版的代码中所用到的算法知识,就都在单人版中讲解过了,那么,本人直接上代码:


双人版完整代码:

Supper-Tramper


那么,本人来展示一下运行结果:

贪食蛇 双人版


那么,需要本篇博文两个版本贪吃蛇的完整代码的同学,请点击下方链接:

Gluttonous-Snake

做到这里,还是感慨良多的,这篇博文的内容,本应该在几个月前发布,但当时本人能力较弱,没完成这篇博文的内容,本人本来已经不打算编写这篇博文的内容了,但是,这篇博文的未完成,反而成了本人的一个心结,一直困扰着本人。于是,本人耗时一周课余时间,反复推敲、交流,终于完成了这篇博文的内容。

那么,以这篇博文做为本专栏的正式结尾篇,本人也算是可以全心全意地投入Java的学习中了。还望各位在今后的学习中能够持续关注本人的博文,谢谢一直以来一直在关注的大家啦!

那么,本专栏博文的全部内容本人就讲解完成了。

若对本专栏博文有任何疑问或者意见以及建议,请在下方评论区提出,本人将尽早予以讲解以及答复。

C语言 贪吃蛇的更多相关文章

  1. c语言贪吃蛇详解3.让蛇动起来

    c语言贪吃蛇详解3.让蛇动起来 前几天的实验室培训课后作业我布置了贪吃蛇,今天有时间就来写一下题解.我将分几步来教大家写一个贪吃蛇小游戏.由于大家c语言未学完,这个教程只涉及数组和函数等知识点. 上次 ...

  2. c语言贪吃蛇详解-2.画出蛇

    c语言贪吃蛇详解-2.画出蛇 前几天的实验室培训课后作业我布置了贪吃蛇,今天有时间就来写一下题解.我将分几步来教大家写一个贪吃蛇小游戏.由于大家c语言未学完,这个教程只涉及数组和函数等知识点. 蛇的身 ...

  3. c语言贪吃蛇详解1.画出地图

    c语言贪吃蛇详解-1.画出地图 前几天的实验室培训课后作业我布置了贪吃蛇,今天有时间就来写一下题解.我将分几步来教大家写一个贪吃蛇小游戏.由于大家c语言未学完,这个教程只涉及数组和函数等知识点. 首先 ...

  4. c语言贪吃蛇详解5.GameOver功能与显示成绩

    c语言贪吃蛇详解5.GameOver功能与显示成绩 以前我们已经做出来了一个能吃东西变长的蛇.不过它好像不会死... 现在就来实现一下game over的功能吧. 写个函数判断蛇是否撞到自己或者撞到墙 ...

  5. c语言贪吃蛇详解4.食物的投放与蛇的变长

    c语言贪吃蛇详解4.食物的投放与蛇的变长 前几天的实验室培训课后作业我布置了贪吃蛇,今天有时间就来写一下题解.我将分几步来教大家写一个贪吃蛇小游戏.由于大家c语言未学完,这个教程只涉及数组和函数等知识 ...

  6. 程序游戏推荐(C语言贪吃蛇,python天天酷跑(需要安装pygame),js是狠人就坚持30s)

    下面是下载位置,我把他们上传到我的文件下了. C语言贪吃蛇:https://files.cnblogs.com/files/ITXiaoAng/%E8%B4%AA%E5%90%83%E8%9B%87. ...

  7. C/C++编程笔记:C语言贪吃蛇源代码控制台(二),分数和食物!

    接上文<C/C++编程笔记:C语言贪吃蛇源代码控制台(一),会动的那种哦!>如果你在学习C语言开发贪吃蛇的话,零基础建议从上一篇开始哦!接下来正式开始吧! 三.蛇的运动 上次我已经教大家画 ...

  8. C/C++编程笔记:C语言贪吃蛇源代码控制台(一),会动的那种哦!

    前几天有个同学加我QQ私聊我说他们老师布置了一个贪吃蛇,他不知道怎么写所以来找我求解,我给他简单讲解了思路和一些难点之后他也能够自己独立将项目完成了!考虑到更多同学可能有贪吃蛇上的问题,今天有时间就来 ...

  9. c语言贪吃蛇

    思路:函数gotoxy(x,y)使光标移植屏幕的x,y坐标(屏幕左上角为0,0),用来绘制蛇和界面,color()函数用来设置绘制的颜色.设有snakelong节,第i节蛇的x坐标为x[i],y坐标为 ...

  10. [C语言]贪吃蛇_结构数组实现

    一.设计思路 蛇身本质上就是个结构数组,数组里存储了坐标x.y的值,再通过一个循环把它打印出来,蛇的移动则是不断地刷新重新打印.所以撞墙.咬到自己只是数组x.y值的简单比较. 二.用上的知识点 结构数 ...

随机推荐

  1. 【笔记3-24】Python语言基础

    环境搭建与语法入门 遇到问题解决问题 积累 英语单词 认真听讲,多敲代码 计算机是什么 计算机的组成 计算机的使用方式 TUI文本交互 GUI图形化交互 windows 的命令行 Shell.Term ...

  2. IO多路复用(IO Multiplexing)

    什么是IO多路复用 为什么要有IO多路复用 作者总结 遵循学习新知识的三部曲:是什么?为什么?怎么用? 作者前言:IO多路复用本质上是网络通信过程中的一个技术名词. 什么是IO多路复用 一个用机场管理 ...

  3. Java生鲜电商平台-电商中"再来一单"功能架构与详细设计(APP/小程序)

    Java生鲜电商平台-电商中"再来一单"功能架构与详细设计(APP/小程序) 说明:在实际的业务场景中(无论是TO B还是TO C)不管是休闲食品.餐饮.水果.日用百货.母婴等高频 ...

  4. .NET 5.0 Preview 2发布

    2020年4月2日微软.NET 团队的项目经理 Richard 在博客上 发布了.NET 5 Preview 2:https://devblogs.microsoft.com/dotnet/annou ...

  5. MySql查询当天、本周、本月、本季度、本年的数据

    1.今天 SELECT * FROM 表名 WHERE TO_DAYS(时间字段名) = TO_DAYS(NOW()); 2.昨天 ; 3.本周 SELECT * FROM 表名 WHERE YEAR ...

  6. JS 剑指Offer(三) 替换字符串中的空格

    请实现一个函数,把字符串 s 中的每个空格替换成"%20". 实现这个函数的方法很简单,在JS中可以直接应用正则表达式,代码如下 直接将全局中的space换成%20,一行代码搞定 ...

  7. STM32F103ZET6 GPIO的使用

    1.GPIO简介 STM32F103ZET6有多个GPIO组,如GPIOA.GPIOB.GPIOC...等等.每个GPIO组具有16个IO口. GPIO组的寄存器都是类似的,每个GPIO组都有2个32 ...

  8. 大曾Blogs使用说明书😊——Super ITZ

    大曾Blogs使用说明书 先敲黑板,四句话: pipe搜索,简洁,用于跳转,博客园及csdn和github 博客园炫酷界面,用于查看主要博文 csdn所有博客汇总,查看详细信息 github项目源码汇 ...

  9. 《综合》MMM集群

    <综合>MMM集群 部署集群基础环境 MySQL-MMM架构部署 MySQL-MMM架构使用 1 部署集群基础环境 1.1 问题 本案例要求为MySQL集群准备基础环境,完成以下任务操作: ...

  10. Jmeter 压力测试笔记(3)--脚本调试/签名/cookie/提升吞吐量/降低异常率/提升单机并发性能

    import XXXsign.Openapi2sign;---导入jar包中的签名方法 String str1 = "12121"; ---需要被签名的字段:向开发了解需要哪些哪些 ...