用c语言编写贪吃蛇要会什么_c语言贪吃蛇原理

用c语言编写贪吃蛇要会什么_c语言贪吃蛇原理贪吃蛇(单人版):实现过程:本人先来介绍一个函数——bioskey函数:intbioskey(intcmd)参数(cmd)基本功能0返回下一个从键盘键入的值(若不键入任何值,则将等下一个键入)它返回一个16位的二进制数,包括两个不同的值:1.当按下一个普通键时,它的低8位数存放该字符的ASCII码,高8位存放该键的扫描码;2.对于特殊键(如方向键、F1~F12等等),低8位为0,高8…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

97c4f954a5a66d4c404f1172f843cb51.png

贪吃蛇(单人版):

实现过程:

本人先来介绍一个函数 —— 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;

}

单人版完整代码:

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

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

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

贪吃蛇(双人版):

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

双人版完整代码:

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

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

Gluttonous-Snake

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

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/197187.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • MySQL8.0.26安装配置教程(windows 64位)

    MySQL8.0.26安装配置教程(windows 64位)一.进入MySQL官网下载安装进入Mysql官网MySQL点击DOWNLOADS下拉页面点击红框内容跳转页面后点击红框下载后并解压到目标文件夹(一定要记住路径)二.配置并初始化MySQL首先创建一个txt文档,并复制如下代码其中安装目录需更改为你所下载的目录存放目录需改为事先新建好的data文件夹目录[mysqld]#设置3306端口port=3306#设置mysql的安装目录basedir=D:\mysql-8.0.26-winx.

  • java缓存设置_缓存数据可以清除吗

    java缓存设置_缓存数据可以清除吗1、@Cacheable(key="#vo.toString()",value="licence")//载入缓存2、@CacheEvict(key="#vo.toString()",value="licence")//清除缓存3、缓存设置在service层生效4、config目录下建ehcache.xml5、ehcache.xml配置如下&lt;ehcachex…

  • BZOJ2440(全然平方数)二分+莫比乌斯容斥

    BZOJ2440(全然平方数)二分+莫比乌斯容斥

  • visio2013首要事项闪退_visio一保存就闪退

    visio2013首要事项闪退_visio一保存就闪退问题描述visio2013安装完成后,新建流程图或者打开流程图,程序就会自动关闭,为了找到错误,我们打开“控制面板->事件查看器->windows事件日志”可以看到一个错误日志,错误如下:错误应用程序名称:VISIO.EXE,版本:15.0.4420.1017,时间戳:0x506742a9错误模块名称:VISLIB.dll,版本:15.0.4420.1017,时间戳:0x506741bc异常代码:0xc0000005错误偏移量:0x000000000010a887错

  • matlab最优化问题的函数(fminbnd),fmincon,globalsearch,multistart(全局局部最优)

    matlab最优化问题的函数(fminbnd),fmincon,globalsearch,multistart(全局局部最优)在讨论优化问题时我们先来讨论全局最优和局部最优全局最优:问题所有的可能解中效果最好的解。局部最优:问题的部分可能解中效果最好的解。一个针对的全局,一个针对的部分。就像我们设初值一样,设置了以后函数开始迭代变化。这时可能出现两种现象①迭代到一个解,该解距离初值较近,此处该值很有可能是局部最优。②迭代到一个解,该解距离初值相对较远,此处该值很大可能是全局最优,当然也可能是局部最优。上…

  • 安卓framework面试题(高级Android面试题)

    Framework面试题 Android 系统基础 JVM、Dalvik和ART是什么以及他们的关系 01 Binder机制 02 系统级app和第三方应用级app分别在什么目录下?system/priva-app和system/app目录的权限有什么…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号