c语言及数据结构实现简单贪吃蛇小游戏

简介: c语言及数据结构实现简单贪吃蛇小游戏

想必大家都玩过贪吃蛇吧,这次我们来简单的实现一下它,由于受到知识盲区的限制,只好来粗略的实现一下以及介绍一下对它的具体操作等。

首先,我们设计的这个游戏只有一条蛇母体本身,然后就是在规定的地图里移动,然后在地图所在范围里面会随机的生成不同样式的食物,蛇可以通过吃它来增大分值,每当吃它一次,蛇自身长度就会增加一节,然后为了增加体验感,我们增添了f3与f4来分别作为加速键和减速键,比如我们按f3减速时候,我们难度会增加那么每个食物的分值也会对应增加,当我们按下f4减速时,难度降低,每个食物的分值也会减低。因此也不能无限减速,因此我们通过食物分值来限制了减速。

这便是我们对这个游戏简单思路介绍。

二·贪吃蛇的实现的开始准备:
1.在这里我们简单介绍一下这里用到的一些win32api规定的一些操作设置:首先要用到的就是,由于我们需要在界面打印一些游戏内容故需要得到光标位置:

我们可以这么理解弹出来的界面就是一个坐标系:

然而它的x轴是朝向右,但是y轴是朝向下的而且它的x的两个值所对应的距离差不多为一个y值所对应的距离,这些对下面我们布局有很大关系。

void setpos(short x, short y) {
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(houtput, pos);
}
接下来我们调用句柄的相关使用可以创建的这个函数可以直接确定光标所在的位置方便我们后续打印。下面我们在进行操作时候需要输入一定的特殊符号等,这时我们就需要把它改成适应本地

include

setlocale(LC_ALL, "");
这里我们就可以后面用wprintf打印占两个位置的东西了。

下面我们还需要设置界面的名字以及弹窗的大小并且为了美观把光标位置隐藏于是就有了下面的操作:

system("mode con cols=100 lines=30");
system("title 贪吃蛇小游戏");
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(houtput, &CursorInfo);
CursorInfo.bVisible = false;
SetConsoleCursorInfo(houtput, &CursorInfo);

下面我们步入操作的正题:

2.1:欢迎界面的实现:

我们为了实现这个界面操作:我们配合光标定位打印以及pause,cls使用来实现暂停以及清屏操作来完成:

void welcomegame() {
setpos(40, 14);
wprintf(L"欢迎来到贪吃蛇\n");
Sleep(1000);
setpos(40, 15);
system("pause");
system("cls");
setpos(26, 5);
wprintf(L"用箭头控制移动方向,F3加速,F4减速\n");
setpos(32, 6);
wprintf(L"加速可以获得更高的分数\n");
setpos(44, 20);
system("pause");
system("cls");

}

这样就完成了此操作(我们在实现以及测试的时候可能会出现直接终止的情况,那么我们可以使用getchar()来防止它直接终止)。

2.2地图的绘制:
在地图绘制之前我们提前声明一下我们对它定义的操作以及对一些图案的define等:

define pos_x 24

define pos_y 5

define wall L'※'

define food0 L'▶'

define food1 L'★'

define food2 L'☆'

define food3 L'◆'

define food4 L'○'

define food5 L'◇'

define food6 L'▲'

define food7 L'□'

define food8 L'▼'

define food9 L'◀'

define body L'◎'

//这里我们定义了不同的食物以及蛇的身体的图案,以及我们蛇头出现的位置pos_x, pos_y等
下面我们还定义了蛇身节点的结构体,以及贪吃蛇游戏里面总的需要的比如状态,分数等一些数据。如:

enum dirction {
up=1,//这里我们为了后面蛇移动调用的Switch可以正确使用将up赋值为1.
down,
left,
right,
};//这里我们定义了一个枚举类型来存放蛇移动的方向。
enum game_Status{
ok,
kill_by_wall,
kill_by_self,
end_normal,//这里是游戏的状态
};
typedef struct snakenode {
int x;
int y;
struct snakenode* next;
}snakenode;//这里我们定义了一个蛇身节点的结构体并给它重命名为snakenode,存放坐标以及下一个节点的地址,这样我们用链表的知识完成了蛇的创建。

typedef struct snake {
snakenode psnake;//蛇头指针
snakenode
pfood;//食物指针
enum game_Status status;//游戏状态
enum dirction dir;蛇移动的方向
int foodscore;//每个食物的分数
int all_score;//游戏的总分
int sleep_time;//通过游戏的休眠时间来控制蛇移动的快慢

}snake;

下面我们就配合setpos函数定义光标的位置完成了地图打印:

void print_map() {
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 <= 25; i++) {
setpos(0, i);
wprintf(L"%lc", wall);

 }
 for (i = 1; i <=25; i++) {
     setpos(56,i );
     wprintf(L"%lc", wall);

 }

}

如:

2.3.1初始化蛇:
void initsnake(snake ps) {
int i = 0;
snakenode
cur = NULL;
for (i = 0; i < 5; i++) {
cur = (snakenode)malloc(sizeof(snakenode));
if (cur == NULL) {
perror("malloc:snakenode");
return;
}
else {
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;
}
}
}//这里我们完成蛇的创建是基于单链表,蛇初始长度为五个节点,因此我们开辟了五个snakenode的空间并把相关数据放进去再把它们连接起来。
cur = ps->psnake;
while (cur) {
setpos(cur->x, cur->y);
wprintf(L"%lc", body);
cur = cur->next;
}//通过遍历整个链表完成蛇身的打印
ps->dir = right;
ps->status = ok;
ps->foodscore = 10;
ps->all_score = 0;
ps->sleep_time = 200;
//把蛇的初始化状态以及游戏相关初始化设置好
}

这里我们对游戏以及蛇一些状态设置都要对游戏的一些相关数据操作故我们每次应给函数传递snake类型的自变量并用同类型ps指针接收。

2.3.2初始化食物:
初始化食物我们应注意:1.由于蛇能吃到食物故我们判断的就是蛇一节身子和食物的坐标能相同,故也就是食物的横坐标必须是2的倍数。2.其次就是食物出现的范围应该不仅在地图里面而且不能和蛇身子重合。


void set_food(snake ps) {
int x = 0;
int y = 0;
again:
do{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);//这里我们调用srand函数来随机生成坐标控制在x=0~54,y=1~25
snakenode
cur= ps->psnake ;
while (cur) {
if (x==cur->x && y==cur->y ) {
goto again;
}
cur = cur->next;//遍历蛇身节点防止食物与之重合
}
snakenode pd = NULL;
pd = (snakenode
)malloc(sizeof(snakenode));
if (pd == NULL) {
perror("malloc:set_food");
return;

 }
 else {
     pd->next = NULL;
     pd->x = x;
     pd->y = y;
     ps->pfood = pd;
     setpos(x, y);

     if (y % 10 == 0) {
         wprintf(L"%lc", food0);
     }
     else if (y%10 == 1) {
         wprintf(L"%lc", food1);
     }
     else if (y % 10== 2) {
         wprintf(L"%lc", food2);
     }
     else if (y % 10 == 3) {
         wprintf(L"%lc", food3);
     }
     else if (y % 10 == 4) {
         wprintf(L"%lc", food4);
     }
     else if (y % 10 == 5) {
         wprintf(L"%lc", food5);
     }
     else if (y % 10 == 6) {
         wprintf(L"%lc", food6);
     }
     else if (y % 10 == 7) {
         wprintf(L"%lc", food7);
     }
     else if (y % 10 == 8) {
         wprintf(L"%lc", food8);
     }
     else if (y % 10 == 9) {
         wprintf(L"%lc", food9);
     }//根据生成的坐标y值不同来防止不同的食物
 }

}

三·贪吃蛇的运行操作:
我们要明白蛇要想动起来我们可以看成一个循环把玩家的每一步操作对蛇所产生的影响串起来,通过计算休眠时间来及时调整,因此我们对整个循环可以从第一步开始考虑,也就是它的移动情况以及下一个碰到食物该怎么坐或者不是食物又该怎么坐,在下面我们来深度剖析一下:

3.1辅助信息的打印:
为了游戏界面美观以及起到提示作用故我们进行了一下操作:

setpos(64, 10);
printf("总分数:%d",ps->all_score);
setpos(64, 11);
printf("每个食物分数:%3d", ps->foodscore);
void printhelpinfo() {

setpos(64, 14);
wprintf(L"蛇不能穿墙哦");
setpos(64, 15);
wprintf(L"可以使用箭头键盘来控制蛇移动的方向哦");
setpos(64, 16);
wprintf(L"按fn+F3加速,fn+F4减速哟");
setpos(64, 17);
wprintf(L"按ESC可以快速退出游戏,空格暂停哦");
setpos(90,19);
wprintf(L"@羑悻");

}
效果如下:

3.2蛇的下一步移动操作:
这里我们分情况列出蛇移动的第一步该怎么样,因此这里我们需要周到的想到众多情况才能防止游戏出现不必要的bug。这里蛇的每一步应该是状态为ok才能一直循环也就是蛇还活着。

snakenode* smove = (snakenode*)malloc(sizeof(snakenode));
if (smove == NULL) {
    perror("snakemove:malloc");
    return;
}
else {
    switch (ps->dir) {
    case up:
        smove->x = ps->psnake->x;
        smove->y= ps->psnake->y-1;
        break;

    case down:
        smove->x = ps->psnake->x;
        smove->y = ps->psnake->y +1;
        break;

    case right:
        smove->x = ps->psnake->x+2;
        smove->y = ps->psnake->y ;
        break;
    case left:
        smove->x = ps->psnake->x -2;
        smove->y = ps->psnake->y;
        break;
    }

//因为我们会判断出玩家按的键是什么来通过蛇头确定下一个坐标的位置
if (nextisfood(smove,ps)) {
eatfood(smove,ps);
}
else {
noeatfood(smove,ps);
}
killbywall(ps);
killbyself(ps);
Sleep(ps->sleep_time);

}

}

加减速的设置操作:

这里由于f3与f4来分别作为加速键和减速键,比如我们按f3减速时候,我们难度会增加那么每个食物的分值也会对应增加,当我们按下f4减速时,难度降低,每个食物的分值也会减低。因此也不能无限减速,因此我们通过食物分值来限制了减速,这样来适当增加游戏的乐趣。


else if (judgkeypush(VK_F3)) {
if (ps->sleep_time > 80) {
ps->sleep_time -= 30;
ps->foodscore += 2;//这里我们每加速一次那么休眠时间就会减少,难度也会增大,那么我们设置的食物分数就让它增加两分。
}
}
else if (judgkeypush(VK_F4)) {
if (ps->foodscore > 2) {
ps->sleep_time += 30;
ps->foodscore -= 2;//这里我们每减速一次那么操作就会变得容易一些,因此我们让食物分值也减少2。
}
}


3.2.1判断玩家按键情况:
为了能方便比较玩家按的哪个键因此我们创建了一个宏来专门判断:

define judgkeypush(vk) ( (GetAsyncKeyState(vk)&1)?1:0)//我们这里又调用了系统自己的函数,作用:如果你按了某个键,他有规定的数值,当传给GetAsyncKeyState这个函数,按了就返回的值二进制末位是1,否则是0.

//这里我们在判断按键的时候比如按了↑就不能按↓类似操作完成对它的控制
if (judgkeypush(VK_UP) && ps->dir != down) {
ps->dir=up;
}

else if(judgkeypush(VK_DOWN) && ps->dir != up) {
ps->dir = down;
}
else if (judgkeypush(VK_LEFT) && ps->dir != right) {
ps->dir = left;
}
else if (judgkeypush(VKRIGHT) && ps->dir != left) {
ps->dir = right;
}
else if (judgkeypush(VK
的SPACE)) {
snake_pause();//这里掉用的函数是通过死循环的休眠来满足暂停的,如果想暂停只需要判断空格是否被按过来break循环
//
//void snake_pause() {
// while (1) {
// Sleep(200);
// if (judgkeypush(VK_SPACE)) {
// break;
// }
// }
//}

}
else if (judgkeypush(VK_ESCAPE)) {
ps->status = end_normal;
}
else if (judgkeypush(VK_F3)) {
if (ps->sleep_time > 80) {
ps->sleep_time -= 30;
ps->foodscore += 2;
}
}
else if (judgkeypush(VK_F4)) {
if (ps->foodscore > 2) {
ps->sleep_time += 30;
ps->foodscore -= 2;
}
}

3.2.2下一步遇到食物:
int nextisfood(snakenodep, snake ps) {
if (ps->pfood->x == p->x && ps->pfood->y == p->y) {
return 1;
}
else {
return 0;
}
}//这里我们需要比较生成的食物坐标和蛇下一步移动的坐标来判断是否相同
void eatfood(snakenode p, snake ps) {
ps->pfood->next = ps->psnake;
ps->psnake = ps->pfood;
free(p);
p = NULL;
snakenode* cur = ps->psnake;
while (cur) {
setpos(cur->x, cur->y);
wprintf(L"%lc", body);
cur = cur->next;
}
ps->all_score += ps->foodscore;
set_food(ps);
}//如果是食物的话我们蛇就要把它吃掉并且延长蛇自身一个节点长度那么我们只需把食物这个地方的节点跟蛇头连接起来再把它当做新的蛇头再次打印蛇身即可,并且吃了它后我们分数也要做相应的改变,并重新生成新的食物

3.2.3下一步不是食物:
void noeatfood(snakenode p, snake ps) {
p->next = ps->psnake;
ps->psnake = p;
snakenode* cur = ps->psnake;
while (cur->next->next!=NULL) {
setpos(cur->x, cur->y);
wprintf(L"%lc", body);
cur = cur->next;
}
setpos(cur->x, cur->y);
wprintf(L"%lc", body);
setpos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
//如果我们走的下一步不是食物的话我们蛇的自身长度就应该不变,因此我们还可以照样把食物节点指针指向蛇头,然后让其成为新的蛇头,完成连接再次打印,这时我们就要把蛇尾去掉一个节点就是把它释放掉让新的蛇尾节点指针为NULL,把我们之前在蛇尾打印的蛇身让其打印空格,完成地图的恢复。

}

3.2.4蛇移动撞到自己死亡以及撞墙死亡:
这里蛇在运行起来的死亡分为两种,一个是撞到墙再一个就是撞到自己那么我们根据这两种情况来实现对代码的编辑:

void killbywall(snake 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(snake
ps) {
snakenode* 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 endgame(snake* ps) {
setpos(24, 12);
switch (ps->status) {
case kill_by_wall:
wprintf(L"你撞到墙死亡了!!!");
break;
case kill_by_self:
wprintf(L"你撞到自己了!!!");
break;
case end_normal:
wprintf(L"%ls",L"你主动退出游戏了!!!");
break;

}
snakenode* cur = ps->psnake;
snakenode* del = NULL;
while (cur) {
    del = cur;
    cur = cur->next;
    free(del);//销毁malloc开辟空间

}

}

然后为了游戏及结束后玩家还能接着玩这里我们调用了写游戏经常会用的do while来完成:

do {
test();
Sleep(1000);
system("cls");
setpos(20, 15);
printf("是否需要再来一局???(y/n)");
ch = getchar();
while (getchar() != '\n');
} while (ch == 'y' || ch == 'Y');
测试函数:


void test() {
snake bs = { 0 };
game_start(&bs);
game_run(&bs);
endgame(&bs);

​}
游戏所用到的函数声明:

void game_start(snakeps);//开始
void setpos(short x, short y);
void welcomegame();
void print_map();
void initsnake(snake
ps);
void set_food(snake ps);
void game_run(snake
ps);//运行
void snake_pause();
void printhelpinfo();
int nextisfood(snakenode p, snake ps);
void eatfood(snakenode p, snake ps);
void noeatfood(snakenode p, snake ps);
void killbywall(snake ps);
void killbyself(snake
ps);
void endgame(snake* ps);//结束
这里我们的游戏相关介绍与操作就完成的差不多了。

效果图:

五·源代码展示:
snake.h

pragma once

include

include

include

include

include

define pos_x 24

define pos_y 5

define wall L'※'

define food0 L'▶'

define food1 L'★'

define food2 L'☆'

define food3 L'◆'

define food4 L'○'

define food5 L'◇'

define food6 L'▲'

define food7 L'□'

define food8 L'▼'

define food9 L'◀'

define body L'◎'

enum dirction {
up=1,
down,
left,
right,
};
enum game_Status{
ok,
kill_by_wall,
kill_by_self,
end_normal,
};
typedef struct snakenode {
int x;
int y;
struct snakenode* next;
}snakenode;

typedef struct snake {
snakenode psnake;
snakenode
pfood;
enum game_Status status;
enum dirction dir;
int foodscore;
int all_score;
int sleep_time;

}snake;
void game_start(snakeps);//开始
void setpos(short x, short y);
void welcomegame();
void print_map();
void initsnake(snake
ps);
void set_food(snake ps);
void game_run(snake
ps);//运行
void snake_pause();
void printhelpinfo();
int nextisfood(snakenode p, snake ps);
void eatfood(snakenode p, snake ps);
void noeatfood(snakenode p, snake ps);
void killbywall(snake ps);
void killbyself(snake
ps);
void endgame(snake* ps);//结束

snake.c

define _CRT_SECURE_NO_WARNINGS

include"snake.h"

void setpos(short x, short y) {
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(houtput, pos);
}
void welcomegame() {
setpos(40, 14);
wprintf(L"欢迎来到贪吃蛇\n");
Sleep(1000);
setpos(40, 15);
system("pause");
system("cls");
setpos(26, 5);
wprintf(L"用箭头控制移动方向,F3加速,F4减速\n");
setpos(32, 6);
wprintf(L"加速可以获得更高的分数\n");
setpos(44, 20);
system("pause");
system("cls");

}
void print_map() {
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 <= 25; i++) {
setpos(0, i);
wprintf(L"%lc", wall);

 }
 for (i = 1; i <=25; i++) {
     setpos(56,i );
     wprintf(L"%lc", wall);

 }

}
void initsnake(snake ps) {
int i = 0;
snakenode
cur = NULL;
for (i = 0; i < 5; i++) {
cur = (snakenode)malloc(sizeof(snakenode));
if (cur == NULL) {
perror("malloc:snakenode");
return;
}
else {
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;
}
}
}
cur = ps->psnake;
while (cur) {
setpos(cur->x, cur->y);
wprintf(L"%lc", body);
cur = cur->next;
}
ps->dir = right;
ps->status = ok;
ps->foodscore = 10;
ps->all_score = 0;
ps->sleep_time = 200;

}

void set_food(snake ps) {
int x = 0;
int y = 0;
again:
do{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);
snakenode
cur= ps->psnake ;
while (cur) {
if (x==cur->x && y==cur->y ) {
goto again;
}
cur = cur->next;
}
snakenode pd = NULL;
pd = (snakenode
)malloc(sizeof(snakenode));
if (pd == NULL) {
perror("malloc:set_food");
return;

 }
 else {
     pd->next = NULL;
     pd->x = x;
     pd->y = y;
     ps->pfood = pd;
     setpos(x, y);

     if (y % 10 == 0) {
         wprintf(L"%lc", food0);
     }
     else if (y%10 == 1) {
         wprintf(L"%lc", food1);
     }
     else if (y % 10== 2) {
         wprintf(L"%lc", food2);
     }
     else if (y % 10 == 3) {
         wprintf(L"%lc", food3);
     }
     else if (y % 10 == 4) {
         wprintf(L"%lc", food4);
     }
     else if (y % 10 == 5) {
         wprintf(L"%lc", food5);
     }
     else if (y % 10 == 6) {
         wprintf(L"%lc", food6);
     }
     else if (y % 10 == 7) {
         wprintf(L"%lc", food7);
     }
     else if (y % 10 == 8) {
         wprintf(L"%lc", food8);
     }
     else if (y % 10 == 9) {
         wprintf(L"%lc", food9);
     }
 }

}

void game_start(snake*ps) {
system("mode con cols=100 lines=30");
system("title 贪吃蛇小游戏");
HANDLE houtput = NULL;
houtput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(houtput, &CursorInfo);
CursorInfo.bVisible = false;
SetConsoleCursorInfo(houtput, &CursorInfo);
welcomegame();
print_map();
initsnake(ps);
set_food(ps);
}
void printhelpinfo() {

setpos(64, 14);
wprintf(L"蛇不能穿墙哦");
setpos(64, 15);
wprintf(L"可以使用箭头键盘来控制蛇移动的方向哦");
setpos(64, 16);
wprintf(L"按fn+F3加速,fn+F4减速哟");
setpos(64, 17);
wprintf(L"按ESC可以快速退出游戏,空格暂停哦");
setpos(90,19);
wprintf(L"@羑悻");

}

define judgkeypush(vk) ( (GetAsyncKeyState(vk)&1)?1:0)

void snake_pause() {
while (1) {
Sleep(200);
if (judgkeypush(VK_SPACE)) {
break;
}
}
}
int nextisfood(snakenodep, snake ps) {
if (ps->pfood->x == p->x && ps->pfood->y == p->y) {
return 1;
}
else {
return 0;
}
}
void eatfood(snakenode p, snake ps) {
ps->pfood->next = ps->psnake;
ps->psnake = ps->pfood;
free(p);
p = NULL;
snakenode cur = ps->psnake;
while (cur) {
setpos(cur->x, cur->y);
wprintf(L"%lc", body);
cur = cur->next;
}
ps->all_score += ps->foodscore;
set_food(ps);
}
void noeatfood(snakenode
p, snake ps) {
p->next = ps->psnake;
ps->psnake = p;
snakenode
cur = ps->psnake;
while (cur->next->next!=NULL) {
setpos(cur->x, cur->y);
wprintf(L"%lc", body);
cur = cur->next;
}
setpos(cur->x, cur->y);
wprintf(L"%lc", body);
setpos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;

}
void killbywall(snake 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(snake
ps) {
snakenode* 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 snakemove(snake ps) {
snakenode
smove = (snakenode*)malloc(sizeof(snakenode));
if (smove == NULL) {
perror("snakemove:malloc");
return;
}
else {
switch (ps->dir) {
case up:
smove->x = ps->psnake->x;
smove->y= ps->psnake->y-1;
break;

    case down:
        smove->x = ps->psnake->x;
        smove->y = ps->psnake->y +1;
        break;

    case right:
        smove->x = ps->psnake->x+2;
        smove->y = ps->psnake->y ;
        break;
    case left:
        smove->x = ps->psnake->x -2;
        smove->y = ps->psnake->y;
        break;
    }
    if (nextisfood(smove,ps)) {
        eatfood(smove,ps);
    }
    else {
        noeatfood(smove,ps);
    }
    killbywall(ps);
    killbyself(ps);
    Sleep(ps->sleep_time);



}

}
void game_run(snake* ps) {
printhelpinfo();
do {
setpos(64, 10);
printf("总分数:%d",ps->all_score);
setpos(64, 11);
printf("每个食物分数:%3d", ps->foodscore);
if (judgkeypush(VK_UP) && ps->dir != down) {
ps->dir=up;
}

    else if(judgkeypush(VK_DOWN) && ps->dir != up) {
        ps->dir = down;
    }
    else if  (judgkeypush(VK_LEFT) && ps->dir != right) {
        ps->dir = left;
    }
    else if (judgkeypush(VK_RIGHT) && ps->dir != left) {
        ps->dir = right;
    }
    else if (judgkeypush(VK_SPACE)) {
        snake_pause();
    }
    else if (judgkeypush(VK_ESCAPE)) {
        ps->status = end_normal;
    }
    else if (judgkeypush(VK_F3)) {
        if (ps->sleep_time > 80) {
            ps->sleep_time -= 30;
            ps->foodscore += 2;
        }
    }
    else if (judgkeypush(VK_F4)) {
        if (ps->foodscore > 2) {
            ps->sleep_time += 30;
            ps->foodscore -= 2;
        }
    }
    snakemove(ps);


} while (ps->status==ok);

}
void endgame(snake* ps) {
setpos(24, 12);
switch (ps->status) {
case kill_by_wall:
wprintf(L"你撞到墙死亡了!!!");
break;
case kill_by_self:
wprintf(L"你撞到自己了!!!");
break;
case end_normal:
wprintf(L"%ls",L"你主动退出游戏了!!!");
break;

}
snakenode* cur = ps->psnake;
snakenode* del = NULL;
while (cur) {
    del = cur;
    cur = cur->next;
    free(del);

}

}

test.c

define _CRT_SECURE_NO_WARNINGS

include"snake.h"

include

void test() {
snake bs = { 0 };
game_start(&bs);
game_run(&bs);
endgame(&bs);
}
int main() {

setlocale(LC_ALL, "");
srand((unsigned int)time(NULL));
/*test();
setpos(20, 15);
printf("是否需要再来一局??(y/n)");*/
char ch = 0;
do {
    test();
    Sleep(1000);
    system("cls");
    setpos(20, 15);
    printf("是否需要再来一局???(y/n)");
     ch = getchar();
     while (getchar() != '\n');
} while (ch == 'y' || ch == 'Y');

/*getchar();*/

/*wprintf(L"%ls\n", L"我");
printf("%d%d", 4,5);*/
return 0;

}

六·通过简单实现贪吃蛇游戏的总结与体会:
通过此次对这个的构造以及代码的编写,让我感受到了一个看似很简单的一个小游戏却有很复杂的逻辑以及实现操作,当完成它时候首先我们要构思,想想自己就是玩家每一步对应会遇到什么情况该如何用代码去把它实现,不放过每一处细节的设置以防止出现bug,当然在写这个游戏时候避免不了bug的出现,出现不可怕,可怕的是你写了好几百行代码突然运行调试的时候出现bug而且要从这几百行代码里面去一一安排断点去调试,监视这就是一件很麻烦的事情,当然我也是遇到了找出的错误回事函数名字的一个字母写错了,然而却浪费了很长时间,因此我们在以后写的时候一定要仔细然后每写一点就测试一下,养成好习惯以免不必要的麻烦发生。然而,当我看到最后效果的时候,让我想我了那些断断续续的调试,思考,改调没有白费,为的就是将它呈现除了,回想起从hello world 到现在确实看到了进步,每一次挑战都是成长的阶梯,每一次失败都是成长的垫脚石,无论前路多么曲折,我都会保持对知识的那份渴望,对技术的那份钻研。不断超越前一次的自我,攀登最高的山峰,翻山越岭,只为遇见更加优秀的自己。

相关文章
|
4月前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
137 1
|
4月前
|
存储 算法 搜索推荐
【趣学C语言和数据结构100例】91-95
本文涵盖多个经典算法问题的C语言实现,包括堆排序、归并排序、从长整型变量中提取偶数位数、工人信息排序及无向图是否为树的判断。通过这些问题,读者可以深入了解排序算法、数据处理方法和图论基础知识,提升编程能力和算法理解。
91 4
|
4月前
|
存储 机器学习/深度学习 搜索推荐
【趣学C语言和数据结构100例】86-90
本文介绍并用C语言实现了五种经典排序算法:直接插入排序、折半插入排序、冒泡排序、快速排序和简单选择排序。每种算法都有其特点和适用场景,如直接插入排序适合小规模或基本有序的数据,快速排序则适用于大规模数据集,具有较高的效率。通过学习这些算法,读者可以加深对数据结构和算法设计的理解,提升解决实际问题的能力。
70 4
|
4月前
|
存储 算法 数据处理
【趣学C语言和数据结构100例】81-85
本文介绍了五个经典算法问题及其C语言实现,涵盖图论与树结构的基础知识。包括使用BFS求解单源最短路径、统计有向图中入度或出度为0的点数、统计无向无权图各顶点的度、折半查找及二叉排序树的查找。这些算法不仅理论意义重大,且在实际应用中极为广泛,有助于提升编程能力和数据结构理解。
71 4
|
4月前
|
算法 数据可视化 数据建模
【趣学C语言和数据结构100例】76-80
本文介绍了五种图论算法的C语言实现,涵盖二叉树的层次遍历及广度优先搜索(BFS)和深度优先搜索(DFS)的邻接表与邻接矩阵实现。层次遍历使用队列按层访问二叉树节点;BFS利用队列从源节点逐层遍历图节点,适用于最短路径等问题;DFS通过递归或栈深入图的分支,适合拓扑排序等场景。这些算法是数据结构和算法学习的基础,对提升编程能力和解决实际问题至关重要。
79 4
|
4月前
|
存储 算法 vr&ar
【趣学C语言和数据结构100例】71-75
本文介绍了五个C语言数据结构问题及其实现,涵盖链表与二叉树操作,包括按奇偶分解链表、交换二叉树左右子树、查找节点的双亲节点、计算二叉树深度及求最大关键值。通过递归和遍历等方法,解决了理论与实际应用中的常见问题,有助于提升编程能力和数据结构理解。
69 4
|
4月前
|
存储 算法 C语言
【趣学C语言和数据结构100例】66-70
本书《趣学C语言和数据结构100例》精选了5个典型的数据结构问题及C语言实现,涵盖链表与数组操作,如有序集合的集合运算、有序序列表的合并、数组中两顺序表位置互换、三递增序列公共元素查找及奇偶数重排。通过详细解析与代码示例,帮助读者深入理解数据结构与算法设计的核心思想,提升编程技能。
58 4
|
4月前
|
存储 算法 C语言
【趣学C语言和数据结构100例】51-55
本文介绍了五个关于链表操作的C语言实现案例,包括删除单链表中的重复元素、从两个有序链表中查找公共元素、判断一个链表是否为另一链表的连续子序列、判断循环双链表是否对称及合并两个循环单链表。每个案例都详细解析了算法思路与实现方法,涵盖了链表操作的多种场景,旨在帮助读者深入理解链表数据结构的应用,提升算法设计与编程能力。
63 4
|
2月前
|
搜索推荐 C语言
数据结构(C语言)之对归并排序的介绍与理解
归并排序是一种基于分治策略的排序算法,通过递归将数组不断分割为子数组,直到每个子数组仅剩一个元素,再逐步合并这些有序的子数组以得到最终的有序数组。递归版本中,每次分割区间为[left, mid]和[mid+1, right],确保每两个区间内数据有序后进行合并。非递归版本则通过逐步增加gap值(初始为1),先对单个元素排序,再逐步扩大到更大的区间进行合并,直至整个数组有序。归并排序的时间复杂度为O(n*logn),空间复杂度为O(n),且具有稳定性,适用于普通排序及大文件排序场景。
|
4月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
116 5