C语言#贪吃蛇#自动寻路
程序小白的自学编程之路,看完了基础的C语言语法之后,在网上查找了一些贪吃蛇的源码开始学习.本文源码仅用于学习与记录.
从最开始的控制台版本,会出现闪烁的问题;到后面下载了easyx库,解决了闪烁的问题,同时界面更丰富了.此版本有四种模式:普通模式、穿墙模式、无敌模式和自动模式.
该版本的特点是使用数组实现蛇的移动,可自动寻找距离最短的食物,食物数量可设置,位置随机。最后附带源码和注释。
我们先看主函数
int main()
{
DWORD t1 = 0, t2 = 0; //延时时间(32位无符号整型)
mciSendString(L"open D:\\22-音效\\ACG经典爆裂双吉他串烧.wav alias bgm", 0, 0, 0); //打开背景音乐
mciSendString(L"play bgm", 0, 0, 0); //播放背景音乐
initgraph(500, 520); //初始化图形界面: x=500, y=520
start(); //开始界面
outtextxy(170, 100, L"->"); //初始默认模式一
chose(); //选择游戏模式;Enter键进入游戏
init(); //初始化游戏参数
while (1)
{
t2 = GetTickCount();
DrawMap(); //绘制地图
if (_kbhit()) //等待键盘输入命令
{
ChangeDir(); //接收命令
move(); //移动
t2 = GetTickCount();
t1 = t2;
}
if (t2 - t1 > time1)
{
move(); //移动
if (mode == 3)
{
autoseek();
}
t1 = t2; //更新时间
}
}
return 0;
}
t1,t2是计时,time1决定蛇的移动速度,time1的大小可以根据得分越高而减少,提高游戏难度.
mciSendString()是播放背景音乐函数,L字母后面的内容是存放音频文件的位置,该函数的参数设置大家可以百度查询.
initgraph()函数是easyx库里的一个函数,需要先去easyx官网下载该库,安装很简单.然后再#include头文件就可以使用了.该函数的作用是初始化一个图形界面,第一个参数表示的是横向(x),第二个参数表示的是纵向(y).它的大小根据存放蛇的数组大小而设置,数值上是10倍的关系.
接下来是start()函数,它的功能是初始化一个开始界面.
void start()
{
setbkcolor(YELLOW); //设置窗口背景色;
cleardevice(); //清除屏幕
setbkmode(TRANSPARENT); //设置字体背景色为透明
settextcolor(RED); //设置字体颜色为红色
settextstyle(20, 0, _T("黑体")); //字体大小:20;字体类型:黑体
outtextxy(200, 40, L"进入模式");
outtextxy(190, 100, L"1.普通模式");
outtextxy(190, 150, L"2.穿墙模式");
outtextxy(190, 200, L"3.无敌模式");
outtextxy(190, 250, L"4.自动模式");
outtextxy(65, 300, L"数字键 1,2,3,4选择模式,Enter键进入游戏");
outtextxy(65, 350, L"字母键 W,S,A,D 控制方向: 上 下 左 右 ");
outtextxy(65, 400, L"空格键 暂停, Esc键 结束游戏");
}
每个函数后面都已经注释了该函数的功能,这里主要讲解一下outtextxy()函数.它的功能是指定到设置的(x,y)坐标下显示相应的文本内容.
好了,接下来chose()函数
void chose()
{
while (1)
{
switch (_getch()) //接收字符
{
case '1':
start();
outtextxy(170, 100, L"->");
mode = 0;
break;
case '2':
start();
outtextxy(170, 150, L"->");
mode = 1;
break;
case '3':
start();
outtextxy(170, 200, L"->");
mode = 2;
break;
case '4':
start();
outtextxy(170, 250, L"->");
mode = 3;
break;
case 13: //回车键
return; //返回
break;
}
}
}
可以看到,chose()函数里面有一个while循环,因为该函数的主要作用是选择一种游戏模式.由变量mode来接收._getch()函数是从键盘接收一个字符,本文通过数字键1234选择四种模式,然后回车键确定.
选择好了游戏模式,接下来就是通过init()函数初始化游戏参数啦.
void init()
{
srand((unsigned)time(NULL)); //随机数种子
setbkcolor(BLACK); //设置背景颜色
//初始化分数
score = 0;
//初始化map数组
memset(map, SPACE, sizeof(map));
// 每一行的 第一个 和 最后一个 是墙
for (int i = 0; i < ROW; i++)
{
map[i][0] = map[i][COL - 1] = WALL;
}
//每一列的 第二个 和 倒数第二个是墙
for (int j = 1; j < COL - 1; j++)
{
map[0][j] = map[ROW - 1][j] = WALL;
}
SnakeSize = 2; //蛇初始长度
SnakeDir = 'D';
//初始化蛇的位置
map[1][2] = SNAKE; //头部
map[1][1] = SNAKE; //身体
snake[0].X = 1; snake[0].Y = 2; //头部位置
snake[1].X = 1; snake[1].Y = 1; //身体位置
path_mark[0][0] = path_mark[0][1] = path_mark[1][0] = path_mark[1][1] = true;
//初始化食物位置
for (int i = 0; i < number; i++)
{
addfood(i);
}
//初始化路径(ROW-2,COL-2)
for (int i = 0; i < ROW - 2; i += 2)
{
for (int j = 0; j < COL - 2; j += 2)
{
path[i][j] = 'D';
path[i][j + 1] = 'S';
path[i + 1][j + 1] = 'A';
path[i + 1][j] = 'W';
}
}
//初始化路径标志(ROW-2,COL-2)
for (int i = 0; i < ROW - 2; i++)
{
for (int j = 0; j < COL - 2; j++)
{
path_mark[i][j] = false;
}
}
if (mode == 3)
{
autoseek();
}
}
先看第一句,srand()函数,它是一个随机数种子,设置一系列随机数.因为我们需要随机食物的位置.
memset()函数将SPACE写入到map数组里,起的是一个初始化的作用.这里提一下,本文使用枚举类型定义了四个变量,SPACE表示空地,WALL表示墙,SNAKE表示蛇,FOOD表示食物.
enum game //枚举类型
{
SPACE, WALL, SNAKE, FOOD //SPACE = 0, WALL = 1, SMAKE = 2, FOOD = 3
};
SnakeSize表示蛇的尺寸,SnakeDir表示蛇的方向,而snake数组存放的是蛇的位置.这里讲解一下COORD参数,它的作用是定义X,Y两个变量,我们用它来存储数组的行和列.
接下来是addfood()函数,它的作用是随机添加食物,food数组是用来存放食物位置的,number变量定义的是食物数量.
void addfood(int order)
{
int row, col;
int flag = 0;
//判断是否有空位
for (int i = 1; i <= ROW - 2; i++)
{
for (int j = 1; j <= COL - 2; j++)
{
if (map[i][j] == SPACE)
{
flag = 1;
break;
}
}
if (flag == 1)
{
break;
}
}
//如果有空位,设置食物位置范围
if (flag == 1)
{
do
{
row = rand() % (ROW - 2) + 1;
col = rand() % (COL - 2) + 1;
} while (map[row][col] != SPACE);
map[row][col] = FOOD;
food[order].X = row;
food[order].Y = col;
}
}
这里的flag变量是判断地图上是否还有空位存放食物,如果有空位的话我们就存放食物,没有的话就不生成食物了.
接下来我们主要讲解一下autoseek()函数,它在自动模式也就是mode=3的时候才生效.
void autoseek()
{
int temp = 0; //最短距离的食物序号
int d = pow((snake[0].X - food[0].X), 2) + pow((snake[0].Y - food[0].Y), 2);
//寻找最短距离的食物
if (number > 1)
{
for (int i = 1; i < number; i++)
{
if (d > pow((snake[0].X - food[i].X), 2) + pow((snake[0].Y - food[i].Y), 2))
{
d = pow((snake[0].X - food[i].X), 2) + pow((snake[0].Y - food[i].Y), 2);
temp = i; //存储最短距离的食物序号
}
}
}
//遍历整个路径,删除无效路径
for (int i = 0; i <= COL - 4; i += 2)
{
for (int j = 1; j <= ROW - 5; j += 2)
{
//纵向
if ((map[j][i + 1] != SNAKE && map[j][i + 2] != SNAKE && map[j + 1][i + 1] != SNAKE && map[j + 1][i + 2] != SNAKE)
|| (map[j + 2][i + 1] != SNAKE && map[j + 2][i + 2] != SNAKE && map[j + 3][i + 1] != SNAKE && map[j + 3][i + 2] != SNAKE))
{
//设置独立环路
path[j + 1][i] = 'D';
path[j][i + 1] = 'A';
//标记为0
if (map[j][i + 1] != SNAKE && map[j][i + 2] != SNAKE && map[j + 1][i + 1] != SNAKE && map[j + 1][i + 2] != SNAKE)
{
path_mark[j - 1][i] = false;
path_mark[j - 1][i + 1] = false;
path_mark[j][i] = false;
path_mark[j][i + 1] = false;
}
if (map[j + 2][i + 1] != SNAKE && map[j + 2][i + 2] != SNAKE && map[j + 3][i + 1] != SNAKE && map[j + 3][i + 2] != SNAKE)
{
path_mark[j + 1][i] = false;
path_mark[j + 2][i] = false;
path_mark[j + 1][i + 1] = false;
path_mark[j + 2][i + 1] = false;
}
}
}
}
for (int i = 0; i <= ROW - 4; i += 2)
{
for (int j = 1; j <= COL - 5; j += 2)
{
//横向
if ((map[i + 1][j] != SNAKE && map[i + 1][j + 1] != SNAKE && map[i + 2][j] != SNAKE && map[i + 2][j + 1] != SNAKE)
|| (map[i + 1][j + 2] != SNAKE && map[i + 1][j + 3] != SNAKE && map[i + 2][j + 2] != SNAKE && map[i + 2][j + 3] != SNAKE))
{
//设置独立环路
path[i][j] = 'S';
path[i + 1][j + 1] = 'W';
//标记为0
if (map[i + 1][j] != SNAKE && map[i + 1][j + 1] != SNAKE && map[i + 2][j] != SNAKE && map[i + 2][j + 1] != SNAKE)
{
path_mark[i][j - 1] = false;
path_mark[i + 1][j - 1] = false;
path_mark[i][j] = false;
path_mark[i + 1][j] = false;
}
if (map[i + 1][j + 2] != SNAKE && map[i + 1][j + 3] != SNAKE && map[i + 2][j + 2] != SNAKE && map[i + 2][j + 3] != SNAKE)
{
path_mark[i][j + 1] = false;
path_mark[i][j + 2] = false;
path_mark[i + 1][j + 1] = false;
path_mark[i + 1][j + 2] = false;
}
}
}
}
//生成路径
COORD head; //头部位置
head.X = snake[0].X - 1; head.Y = snake[0].Y - 1;
head.X = head.X % 2 == 1 ? head.X - 1 : head.X; //头部位置回到左上角(奇数,-1;偶数,不变)
head.Y = head.Y % 2 == 1 ? head.Y - 1 : head.Y;
COORD end; //食物位置
end.X = food[temp].X - 1; end.Y = food[temp].Y - 1;
end.X = end.X % 2 == 1 ? end.X - 1 : end.X; //食物位置左上角(奇数,-1;偶数,不变)
end.Y = end.Y % 2 == 1 ? end.Y - 1 : end.Y;
int dx, dy; //头部与食物位置之间的相对关系
dx = head.X > end.X ? -1 : head.X < end.X ? 1 : 0; //食物在下边,1;在上边,-1
dy = head.Y > end.Y ? -1 : head.Y < end.Y ? 1 : 0; //食物在右边,1;在左边,-1
while (head.Y != end.Y)
{
//横向
head.Y += dy;
if (head.Y % 2 == 1) //只在奇数时判断
{
if (!(path_mark[head.X][head.Y] && path_mark[head.X + 1][head.Y + 1]))
{
path[head.X][head.Y] = 'D';
path[head.X + 1][head.Y + 1] = 'A';
path_mark[head.X][head.Y - 1] = true; path_mark[head.X][head.Y] = true; path_mark[head.X + 1][head.Y - 1] = true; path_mark[head.X + 1][head.Y] = true;
path_mark[head.X][head.Y + 1] = true; path_mark[head.X][head.Y + 2] = true; path_mark[head.X + 1][head.Y + 1] = true; path_mark[head.X + 1][head.Y + 2] = true;
}
}
}
while (head.X != end.X)
{
//纵向
head.X += dx;
if (head.X % 2 == 1) //只在奇数时判断
{
if (!(path_mark[head.X][head.Y] && path_mark[head.X + 1][head.Y + 1]))
{
path[head.X + 1][head.Y] = 'W';
path[head.X][head.Y + 1] = 'S';
path_mark[head.X - 1][head.Y] = path_mark[head.X - 1][head.Y + 1] = path_mark[head.X][head.Y] = path_mark[head.X][head.Y + 1] = true;
path_mark[head.X + 1][head.Y] = path_mark[head.X + 1][head.Y + 1] = path_mark[head.X + 2][head.Y] = path_mark[head.X + 2][head.Y + 1] = true;
}
}
}
SnakeDir = path[snake[0].X - 1][snake[0].Y - 1];
}
可以看到前面的变量temp和d,是因为地图上的食物数量超过1时,需要判断哪个食物离蛇头的距离最短.因此需要计算头部到每个食物的距离d,使用temp记录距离最短的那个食物的编号.
接下来需要重点介绍两个数组,一个是path,另一个是path_mark.这两个数组的大小与map大小差不多,它的范围也就是蛇能够移动的范围.
首先来看path数组,它存放的是蛇的移动方向’D’,‘A’,‘S’,‘W’,也就是上下左右.这个数组的作用规划蛇的移动方向,让蛇在一个环路中移动,这样蛇头就不可能会撞到身体和墙等物体.
而path_mark数组的作用是标记路径,因为在规划路径的时候,如果是两个已存在的路径时,不能进行组合路径.组合路径的原则是存在未规划的路径.
del_path()函数和form_path()函数分别是删除路径和生成路径.
首先是del_path()函数,分别从横向和纵向遍历整个路径,当路径上不存在蛇的时候,需要删除该路径,并把标志置为0.
而form_path()函数,我们需要取出蛇头位置和食物位置,分别判断行号和列号是否为奇数,如果是奇数的话就减一,偶数不变.这样做的目的是让坐标回归到路径的左上角,方便计算蛇头和食物的相对位置关系(dx,dy).在生成路径过程中,利用path_mark数组判断是否是未添加过的路径,如果是的话,就需要把它添加到路径中;已经是路径的不做处理.
生成好路径之后,根据蛇头的位置检索它的移动方向,赋予给SnakeDir.
根据SnakeDir接收到的移动方向,在move()函数中,我们对蛇头位置进行相应的加减,用next变量接收,然后判断next位置上的值是什么,分别SNAKE,WALL,FOOD和SPACE四种情况,分别做相应的处理即可.score是吃到食物后的得分,得分越高,time1越小,蛇的移动速度越快,因为刷新速度越快.
接下来就很简单了,DrawMap()函数的功能是遍历整个map数组,根据map上的数值绘制相应的图形,这里我们是用正方形来模仿墙和蛇,用不同的颜色进行区分.
最后是整个源代码.
#include<stdio.h>
#include<easyx.h>
#include<graphics.h>
#include<conio.h>
#include<time.h>
#include<windows.h>
#include<math.h>
#pragma comment(lib, "Winmm.lib")
void start(); //开始界面
void chose(); //选择模式;0:普通模式 1:穿墙模式 2:无敌模式 3:自动模式
void init(); //初始化游戏数据
void DrawMap(); //绘制地图
void ChangeDir(); //接收命令,改变方向
void move(); //蛇的移动
void addfood(int order); //添加食物
void autoseek(); //自动寻找食物模式
enum game //枚举类型
{
SPACE, WALL, SNAKE, FOOD //SPACE = 0, WALL = 1, SMAKE = 2, FOOD = 3
};
int mode; //游戏模式
int score; //分数
const int ROW = 50; //地图行数
const int COL = 50; //地图列数
const int number = 3; //食物数量
const int width = 500; //图形宽度(x)
const int high = 520; //图形高度(y)
int map[ROW][COL]; //地图大小
COORD food[number]; //食物的位置
COORD snake[ROW * COL]; //蛇的位置
int SnakeSize; //蛇的尺寸
char SnakeDir; //蛇的移动方向
char path[ROW - 2][COL - 2] = { 0 }; //路径
bool path_mark[ROW - 2][COL - 2]; //路径标志
int time1 = 50; //设定延时时间
int main()
{
DWORD t1 = 0, t2 = 0; //延时时间(32位无符号整型)
mciSendString(L"open D:\\22-音效\\ACG经典爆裂双吉他串烧.wav alias bgm", 0, 0, 0); //打开背景音乐
mciSendString(L"play bgm", 0, 0, 0); //播放背景音乐
initgraph(500, 520); //初始化图形界面: x=500, y=520
start(); //开始界面
outtextxy(170, 100, L"->"); //初始默认模式一
chose(); //选择游戏模式;Enter键进入游戏
init(); //初始化游戏参数
while (1)
{
t2 = GetTickCount();
DrawMap(); //绘制地图
if (_kbhit()) //等待键盘输入命令
{
ChangeDir(); //接收命令
move(); //移动
t2 = GetTickCount();
t1 = t2;
}
if (t2 - t1 > time1)
{
move(); //移动
if (mode == 3)
{
autoseek();
}
t1 = t2; //更新时间
}
}
return 0;
}
void start()
{
setbkcolor(YELLOW); //设置窗口背景色;
cleardevice(); //清除屏幕
setbkmode(TRANSPARENT); //设置字体背景色为透明
settextcolor(RED); //设置字体颜色为红色
settextstyle(20, 0, _T("黑体")); //字体大小:20;字体类型:黑体
outtextxy(200, 40, L"进入模式");
outtextxy(190, 100, L"1.普通模式");
outtextxy(190, 150, L"2.穿墙模式");
outtextxy(190, 200, L"3.无敌模式");
outtextxy(190, 250, L"4.自动模式");
outtextxy(65, 300, L"数字键 1,2,3,4选择模式,Enter键进入游戏");
outtextxy(65, 350, L"字母键 W,S,A,D 控制方向: 上 下 左 右 ");
outtextxy(65, 400, L"空格键 暂停, Esc键 结束游戏");
}
void chose()
{
while (1)
{
switch (_getch()) //接收字符
{
case '1':
start();
outtextxy(170, 100, L"->");
mode = 0;
break;
case '2':
start();
outtextxy(170, 150, L"->");
mode = 1;
break;
case '3':
start();
outtextxy(170, 200, L"->");
mode = 2;
break;
case '4':
start();
outtextxy(170, 250, L"->");
mode = 3;
break;
case 13: //回车键
return; //返回
break;
}
}
}
void init()
{
srand((unsigned)time(NULL)); //随机数种子
setbkcolor(BLACK); //设置背景颜色
//初始化分数
score = 0;
//初始化map数组
memset(map, SPACE, sizeof(map));
// 每一行的 第一个 和 最后一个 是墙
for (int i = 0; i < ROW; i++)
{
map[i][0] = map[i][COL - 1] = WALL;
}
//每一列的 第二个 和 倒数第二个是墙
for (int j = 1; j < COL - 1; j++)
{
map[0][j] = map[ROW - 1][j] = WALL;
}
SnakeSize = 2; //蛇初始长度
SnakeDir = 'D';
//初始化蛇的位置
map[1][2] = SNAKE; //头部
map[1][1] = SNAKE; //身体
snake[0].X = 1; snake[0].Y = 2; //头部位置
snake[1].X = 1; snake[1].Y = 1; //身体位置
path_mark[0][0] = path_mark[0][1] = path_mark[1][0] = path_mark[1][1] = true;
//初始化食物位置
for (int i = 0; i < number; i++)
{
addfood(i);
}
//初始化路径(ROW-2,COL-2)
for (int i = 0; i < ROW - 2; i += 2)
{
for (int j = 0; j < COL - 2; j += 2)
{
path[i][j] = 'D';
path[i][j + 1] = 'S';
path[i + 1][j + 1] = 'A';
path[i + 1][j] = 'W';
}
}
//初始化路径标志(ROW-2,COL-2)
for (int i = 0; i < ROW - 2; i++)
{
for (int j = 0; j < COL - 2; j++)
{
path_mark[i][j] = false;
}
}
if (mode == 3)
{
autoseek();
}
}
void DrawMap()
{
BeginBatchDraw(); //开始绘图
setbkcolor(BLACK); //设置背景颜色
settextcolor(YELLOW); //设置文本颜色
cleardevice(); //清屏
WCHAR arr[10]; //保存成绩
wsprintf(arr, L"总分:%d", score); //将成绩格式化输出到字符串arr中
outtextxy(0, 0, arr); //显示成绩
for (int y = 0; y < ROW; y++) //y轴方向向下(行)
{
for (int x = 0; x < COL; x++) //x轴方向向右(列)
{
switch (map[y][x])
{
case SPACE:
break;
case WALL: //墙
setlinecolor(BLACK); //设置线条颜色为黑色
setfillcolor(WHITE); //设置填充颜色为白色
fillrectangle(x * 10, y * 10 + 20, x * 10 + 10, y * 10 + 30); //画方块,边长10,左右对角的坐标
break;
case SNAKE: //蛇
if (y == snake[0].X && x == snake[0].Y)
{
setfillcolor(LIGHTBLUE);
solidrectangle(x * 10, y * 10 + 20, x * 10 + 10, y * 10 + 30);
}
else
{
setfillcolor(RGB(255, 255, 0)); //黄 255 255 0
solidrectangle(x * 10, y * 10 + 20, x * 10 + 10, y * 10 + 30);
}
break;
case FOOD:
setfillcolor(LIGHTRED); //食物颜色
solidrectangle(x * 10, y * 10 + 20, x * 10 + 10, y * 10 + 30);
break;
default:
break;
}
}
}
EndBatchDraw(); //结束绘制
}
void autoseek()
{
int temp = 0; //最短距离的食物序号
int d = pow((snake[0].X - food[0].X), 2) + pow((snake[0].Y - food[0].Y), 2);
//寻找最短距离的食物
if (number > 1)
{
for (int i = 1; i < number; i++)
{
if (d > pow((snake[0].X - food[i].X), 2) + pow((snake[0].Y - food[i].Y), 2))
{
d = pow((snake[0].X - food[i].X), 2) + pow((snake[0].Y - food[i].Y), 2);
temp = i; //存储最短距离的食物序号
}
}
}
//遍历整个路径,删除无效路径
for (int i = 0; i <= COL - 4; i += 2)
{
for (int j = 1; j <= ROW - 5; j += 2)
{
//纵向
if ((map[j][i + 1] != SNAKE && map[j][i + 2] != SNAKE && map[j + 1][i + 1] != SNAKE && map[j + 1][i + 2] != SNAKE)
|| (map[j + 2][i + 1] != SNAKE && map[j + 2][i + 2] != SNAKE && map[j + 3][i + 1] != SNAKE && map[j + 3][i + 2] != SNAKE))
{
//设置独立环路
path[j + 1][i] = 'D';
path[j][i + 1] = 'A';
//标记为0
if (map[j][i + 1] != SNAKE && map[j][i + 2] != SNAKE && map[j + 1][i + 1] != SNAKE && map[j + 1][i + 2] != SNAKE)
{
path_mark[j - 1][i] = false;
path_mark[j - 1][i + 1] = false;
path_mark[j][i] = false;
path_mark[j][i + 1] = false;
}
if (map[j + 2][i + 1] != SNAKE && map[j + 2][i + 2] != SNAKE && map[j + 3][i + 1] != SNAKE && map[j + 3][i + 2] != SNAKE)
{
path_mark[j + 1][i] = false;
path_mark[j + 2][i] = false;
path_mark[j + 1][i + 1] = false;
path_mark[j + 2][i + 1] = false;
}
}
}
}
for (int i = 0; i <= ROW - 4; i += 2)
{
for (int j = 1; j <= COL - 5; j += 2)
{
//横向
if ((map[i + 1][j] != SNAKE && map[i + 1][j + 1] != SNAKE && map[i + 2][j] != SNAKE && map[i + 2][j + 1] != SNAKE)
|| (map[i + 1][j + 2] != SNAKE && map[i + 1][j + 3] != SNAKE && map[i + 2][j + 2] != SNAKE && map[i + 2][j + 3] != SNAKE))
{
//设置独立环路
path[i][j] = 'S';
path[i + 1][j + 1] = 'W';
//标记为0
if (map[i + 1][j] != SNAKE && map[i + 1][j + 1] != SNAKE && map[i + 2][j] != SNAKE && map[i + 2][j + 1] != SNAKE)
{
path_mark[i][j - 1] = false;
path_mark[i + 1][j - 1] = false;
path_mark[i][j] = false;
path_mark[i + 1][j] = false;
}
if (map[i + 1][j + 2] != SNAKE && map[i + 1][j + 3] != SNAKE && map[i + 2][j + 2] != SNAKE && map[i + 2][j + 3] != SNAKE)
{
path_mark[i][j + 1] = false;
path_mark[i][j + 2] = false;
path_mark[i + 1][j + 1] = false;
path_mark[i + 1][j + 2] = false;
}
}
}
}
//生成路径
COORD head; //头部位置
head.X = snake[0].X - 1; head.Y = snake[0].Y - 1;
head.X = head.X % 2 == 1 ? head.X - 1 : head.X; //头部位置回到左上角(奇数,-1;偶数,不变)
head.Y = head.Y % 2 == 1 ? head.Y - 1 : head.Y;
COORD end; //食物位置
end.X = food[temp].X - 1; end.Y = food[temp].Y - 1;
end.X = end.X % 2 == 1 ? end.X - 1 : end.X; //食物位置左上角(奇数,-1;偶数,不变)
end.Y = end.Y % 2 == 1 ? end.Y - 1 : end.Y;
int dx, dy; //头部与食物位置之间的相对关系
dx = head.X > end.X ? -1 : head.X < end.X ? 1 : 0; //食物在下边,1;在上边,-1
dy = head.Y > end.Y ? -1 : head.Y < end.Y ? 1 : 0; //食物在右边,1;在左边,-1
while (head.Y != end.Y)
{
//横向
head.Y += dy;
if (head.Y % 2 == 1) //只在奇数时判断
{
if (!(path_mark[head.X][head.Y] && path_mark[head.X + 1][head.Y + 1]))
{
path[head.X][head.Y] = 'D';
path[head.X + 1][head.Y + 1] = 'A';
path_mark[head.X][head.Y - 1] = true; path_mark[head.X][head.Y] = true; path_mark[head.X + 1][head.Y - 1] = true; path_mark[head.X + 1][head.Y] = true;
path_mark[head.X][head.Y + 1] = true; path_mark[head.X][head.Y + 2] = true; path_mark[head.X + 1][head.Y + 1] = true; path_mark[head.X + 1][head.Y + 2] = true;
}
}
}
while (head.X != end.X)
{
//纵向
head.X += dx;
if (head.X % 2 == 1) //只在奇数时判断
{
if (!(path_mark[head.X][head.Y] && path_mark[head.X + 1][head.Y + 1]))
{
path[head.X + 1][head.Y] = 'W';
path[head.X][head.Y + 1] = 'S';
path_mark[head.X - 1][head.Y] = path_mark[head.X - 1][head.Y + 1] = path_mark[head.X][head.Y] = path_mark[head.X][head.Y + 1] = true;
path_mark[head.X + 1][head.Y] = path_mark[head.X + 1][head.Y + 1] = path_mark[head.X + 2][head.Y] = path_mark[head.X + 2][head.Y + 1] = true;
}
}
}
SnakeDir = path[snake[0].X - 1][snake[0].Y - 1];
}
void ChangeDir()
{
switch (_getch())
{
case'A':
case'a':
case 75:
if (SnakeDir != 'D') SnakeDir = 'A';
break;
case'D':
case'd':
case 77:
if (SnakeDir != 'A') SnakeDir = 'D';
break;
case'W':
case'w':
case 72:
if (SnakeDir != 'S') SnakeDir = 'W';
break;
case'S':
case's':
case 80:
if (SnakeDir != 'W') SnakeDir = 'S';
break;
case 32:
_getch();
break;
case 27:
MessageBox(GetHWnd(), L"游戏结束", L"SORRY", MB_OK);
start(); //开始界面
outtextxy(200, 100, L"->"); //初始默认模式一
chose(); //选择模式;Enter键进入游戏
init(); //初始化游戏界面
default:
break;
}
}
void move()
{
COORD next; //蛇头的下一个位置
static int times = 1; //游戏难度
//选择移动方向
switch (SnakeDir)
{
case'A':
next.X = snake[0].X;
next.Y = snake[0].Y - 1;
break;
case'W':
next.X = snake[0].X - 1;
next.Y = snake[0].Y;
break;
case'D':
next.X = snake[0].X;
next.Y = snake[0].Y + 1;
break;
case'S':
next.X = snake[0].X + 1;
next.Y = snake[0].Y;
break;
default:
break;
}
//判断下一个位置是什么
switch (map[next.X][next.Y])
{
case SPACE:
map[snake[SnakeSize - 1].X][snake[SnakeSize - 1].Y] = SPACE; //地图蛇尾所在地置空
for (int i = SnakeSize - 1; i > 0; i--) //蛇尾到蛇头整体移动一位
{
snake[i] = snake[i - 1];
map[snake[i].X][snake[i].Y] = SNAKE; //蛇身位置必须赋予SNAKE
}
snake[0] = next; //将下一个位置赋予给头部
map[snake[0].X][snake[0].Y] = SNAKE; //设置头部
break;
case WALL:
if (mode == 1 || mode == 2) //模式一模式二可穿墙
{
map[snake[SnakeSize - 1].X][snake[SnakeSize - 1].Y] = SPACE; //蛇尾置空
for (int i = SnakeSize - 1; i > 0; i--) //蛇尾到蛇头整体移动一位
{
snake[i] = snake[i - 1];
map[snake[i].X][snake[i].Y] = SNAKE;
}
switch (SnakeDir) //穿墙
{
case 'A':next.Y = COL - 2; break;
case 'D':next.Y = 1; break;
case 'W':next.X = ROW - 2; break;
case 'S':next.X = 1; break;
default:
break;
}
snake[0] = next; //蛇头移动到新位置
map[snake[0].X][snake[0].Y] = SNAKE; //新的蛇头所在的位置
}
else {
MessageBox(GetHWnd(), L"游戏结束", L"SORRY", MB_OK);
start(); //开始界面
outtextxy(200, 100, L"->"); //初始默认模式一
chose(); //选择模式;Enter键进入游戏
init(); //初始化游戏界面
}
break;
case SNAKE:
map[snake[SnakeSize - 1].X][snake[SnakeSize - 1].Y] = SPACE; //地图蛇尾所在地置空
for (int i = SnakeSize - 1; i > 0; i--) //蛇尾到蛇头整体移动一位
{
snake[i] = snake[i - 1];
map[snake[i].X][snake[i].Y] = SNAKE;
}
snake[0] = next; //将下一个位置赋予给头部
map[snake[0].X][snake[0].Y] = SNAKE; //设置头部
break;
case FOOD: //食物
for (int i = SnakeSize; i > 0; i--) //蛇尾到蛇头整体移动一位
{
snake[i] = snake[i - 1];
}
snake[0] = next; //设置蛇头位置
map[next.X][next.Y] = SNAKE; //设置头部
SnakeSize++; //蛇尺度加一
PlaySound(TEXT("D:\\22-音效\\1枪声.wav"), NULL, SND_FILENAME | SND_ASYNC); //播放音效//SND_ASYNC:播放音乐的同时结束程序;SND_SYNC:播放完音乐后才结束程序;
score++; //分数加一
//加快移动速度
if (score == 3 * times)
{
time1 -= 10;
times++;
if (time1 == 0)
{
time1 = 10;
}
}
yu//添加新的食物
for (int order = 0; order < number; order++)
{
if ((snake[0].X == food[order].X) && (snake[0].Y == food[order].Y))
{
addfood(order);
}
}
break;
default:
break;
}
}
void addfood(int order)
{
int row, col;
int flag = 0;
//判断是否有空位
for (int i = 1; i <= ROW - 2; i++)
{
for (int j = 1; j <= COL - 2; j++)
{
if (map[i][j] == SPACE)
{
flag = 1;
break;
}
}
if (flag == 1)
{
break;
}
}
//如果有空位,设置食物位置范围
if (flag == 1)
{
do
{
row = rand() % (ROW - 2) + 1;
col = rand() % (COL - 2) + 1;
} while (map[row][col] != SPACE);
map[row][col] = FOOD;
food[order].X = row;
food[order].Y = col;
}
}
代码运行效果如下:
上一篇: javascript实现贪吃蛇小游戏