欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言#贪吃蛇#自动寻路

程序员文章站 2024-03-19 08:17:04
...

程序小白的自学编程之路,看完了基础的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;
    }
}

代码运行效果如下:
C语言#贪吃蛇#自动寻路

相关标签: 思想