【C / EasyX】随机迷宫生成和路径搜索(DFS)

1 篇文章 0 订阅
1 篇文章 0 订阅

==========================================说明=====================================
1.用了写游戏的基本流程所以界面是持续刷新的(可能会改成游戏)
2.迷宫生成算法是深度优先遍历(DFS),为了让路线不那么容易走通,是从终点向起点生成的。


PS1:迷宫难度不高,路线完全随机似乎不是太好,有待改进。

PS2:至于EasyX,是个C语言可用的不错的图形库,下载的话百度一下就有了。


#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <graphics.h>
//以下是 宏定义参数 //
#define WIN_WIDTH 1200                      // 窗体宽度
#define WIN_HEIGHT 680                      // 窗体高度
#define MAZE_WIDTH 15*34                    // 迷宫宽度
#define MAZE_HEIGHT 15*17                   // 迷宫高度
#define B_WIDTH 10                          // 格子宽度
#define X 51                                // 迷宫列数 
#define Y 26                                // 迷宫行数 
#define N X*2+1                             // 迷宫数组列数 
#define M Y*2+1                             // 迷宫数组行数 
#define L (N-1)*(M-1)                       // 迷宫路径长度
#define LX 80                               // 起点坐标X
#define LY 50                               // 起点坐标Y
#define C_BLOCK RGB(153,102,85)             // 墙壁颜色
#define C_GRAY RGB(123,72,55)               // 阴影颜色
#define C_GROUND RGB(50,50,50)              // 地面颜色
#define C_ROOT RGB(170,170,210)             // 路径颜色
#define C_LINE YELLOW           // 路径线颜色
//以下是  函数声明  //
float GetFPS();                             // 计算画面FPS(每秒帧数)
void Delay(DWORD ms);                       // 绝对延时
bool IsInRect(int x,int y,RECT r);          // 是否在矩形内
void InitMaze();                            // 初始化迷宫数组
void OutputMaze();                          // 输出迷宫
void CopyScreenBuffer();                    // 复制屏幕到缓存
void DrawScreenBuffer();                    // 绘制缓存到屏幕
void OutPutPath();                          // 输出路径连线
void DrawFPS();                             // 输出帧数
int Move(int d,int x,int y);                // 向指定方向移动,返回是否成功移动
void DFS_CreateMaze(int x,int y);           // 深度优先生成迷宫
void FindPath(int i,int j);                 // 深度优先搜索迷宫路径
void CreateMaze();                          // 创建迷宫
void DrawMenuItems();                       // 绘制菜单按钮
//以下是全局变量定义//
DWORD* p_Screen;                                // 显存指针
DWORD buff_Screen[WIN_WIDTH*WIN_HEIGHT];        // 显示缓存
int isShowFPS=1;                                // 是否显示帧数
int isShowMazePath=0;                           // 显示迷宫路径
int Maze[M][N];                                 // 迷宫数组   0 普通通路 1 墙 2 路径点 3 起点 4 终点
int path_x[1000][L],path_y[1000][L];            // 路径坐标
int length[L];                                  // 路径搜索长度
int lengtha,count;                              // 搜索计数
int mouseX;                                     // 鼠标位置坐标X
int mouseY;                                     // 鼠标位置坐标Y
bool isMouseDown;                               // 鼠标按下
int start_x,start_y,end_x,end_y;                // 起点坐标 终点坐标
int temp_d;                                     // 上一步走的方向
int Move_flag;                                  // 移动标记
//以下是辅助功能函数//
// 计算画面FPS(每秒帧数)
float GetFPS()
{
#define FPS_COUNT 8
    static int i = 0;
    static DWORD oldTime = GetTickCount();
    static float fps;
    if (i > FPS_COUNT)
    {
        i = 0;
        int newTime = GetTickCount();
        int elapsedTime = newTime - oldTime;
        fps = FPS_COUNT / (elapsedTime / 1000.0f);
        oldTime = newTime;
    }
    i++;
    return fps;
}
// 绝对延时
void Delay(DWORD ms)
{
    static DWORD oldTime = GetTickCount();
    while(GetTickCount() - oldTime < ms)
        Sleep(1);
    oldTime = GetTickCount();
}
// 是否在矩形内
bool IsInRect(int x,int y,RECT r)
{
    if((x>=r.left && x<=r.right) && (y>=r.top && y<=r.bottom)) return true;
    else return false;
}
// 初始化迷宫数组 
void InitMaze(){
    for(int i=0;i<M;i++){
        for(int j=0;j<N;j++){
            Maze[i][j]=1;
        }
    }
    // 设置起点和终点
    Maze[1][0]=3;
    Maze[M-2][N-1]=4;
}
// 输出迷宫 
void OutputMaze(){
    // 清屏
    setcolor(RGB(255,255,255));
    setfillcolor(RGB(255,255,255));
    fillrectangle(0,0,WIN_WIDTH,WIN_HEIGHT);
    for(int i=0;i<M;i++){
        for(int j=0;j<N;j++){
            int xx=LX+j*B_WIDTH;
            int yy=LY+i*B_WIDTH;
            switch(Maze[i][j]){
                case 0:
                    setcolor(C_GROUND);
                    setfillcolor(C_GROUND);
                    fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
                    break;
                case 1:
                    setcolor(C_BLOCK);
                    setfillcolor(C_BLOCK);
                    fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
                    if(Maze[i][j]==1 && (i==M-1 || Maze[i+1][j]!=1))
                    {
                        setcolor(C_GRAY);
                        setfillcolor(C_GRAY);
                        fillrectangle(xx,yy+B_WIDTH/2,xx+B_WIDTH,yy+B_WIDTH);
                    }
                    break;
                case 2:
                    setcolor(C_GROUND);
                    setfillcolor(C_GROUND);
                    fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
                    /*
                    setcolor(C_ROOT);
                    setfillcolor(C_ROOT);
                    fillellipse(xx+B_WIDTH/2-1,yy+B_WIDTH/2-1,xx+B_WIDTH/2+1,yy+B_WIDTH/2+1);
                    */
                    break;
                case 3:
                    setcolor(C_GROUND);
                    setfillcolor(C_GROUND);
                    fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
                    break;
                case 4:
                    setcolor(C_GROUND);
                    setfillcolor(C_GROUND);
                    fillrectangle(xx,yy,xx+B_WIDTH,yy+B_WIDTH);
                    break;
            }
        }
    }
    
    
}
// 复制屏幕到缓存
void CopyScreenBuffer()
{
    for(int i=0;i<WIN_WIDTH*WIN_HEIGHT;i++)
    {
        buff_Screen[i] = p_Screen[i];
    }
}
// 绘制缓存到屏幕
void DrawScreenBuffer()
{
    for(int i=0;i<WIN_WIDTH*WIN_HEIGHT;i++)
    {
         p_Screen[i]=buff_Screen[i];
    }
}
// 输出路径连线
void OutPutPath()
{
    if(isShowMazePath)
    {
        for(int i=0;i<length[0]-1;i++)
        {
            setcolor(C_LINE);
            int x1=path_x[0][i]*B_WIDTH + B_WIDTH/2 + LX;
            int x2=path_x[0][i+1]*B_WIDTH + B_WIDTH/2 + LX;
            int y1=path_y[0][i]*B_WIDTH + B_WIDTH/2 + LY;
            int y2=path_y[0][i+1]*B_WIDTH + B_WIDTH/2 + LY;
            line(x1,y1,x2,y2);
        }
    }
}
// 输出帧数
void DrawFPS()
{
    //输出帧数
    if(isShowFPS)
    {
        setcolor(RED);
        settextstyle(14, 0, _T("宋体"));
        TCHAR  s[5];
#if _MSC_VER > 1200
        _stprintf_s(s, _T("%.1f"), GetFPS());
#else
        _stprintf(s, _T("%.1f"), GetFPS());
#endif
        outtextxy(4, 2, _T("FPS:"));
        outtextxy(34, 2, s);
    }
}
// 向指定方向移动,返回是否成功移动
int Move(int d,int x,int y)
{
    switch(d)
    {
    // 向上
    case 0:{
        if(Maze[y-3][x]>0 && Maze[y-2][x-1]>0 && Maze[y-2][x+1]>0 && y-2>0)
        {
            Maze[y-2][x]=0;
            Maze[y-1][x]=0;
            Move_flag=1;
            temp_d=0;
            DFS_CreateMaze(x,y-2);
        }
        break;
           }
    // 向下
    case 1:{
        if(Maze[y+3][x]>0 && Maze[y+2][x-1]>0 && Maze[y+2][x+1]>0 && y+2<M)
        {
            Maze[y+2][x]=0;
            Maze[y+1][x]=0;
            Move_flag=1;
            temp_d=1;
            DFS_CreateMaze(x,y+2);
        }
        break;
           }
    // 向左
    case 2:{
        if(Maze[y][x-3]>0 && Maze[y-1][x-2]>0 && Maze[y+1][x-2]>0 && x-2>0)
        {
            Maze[y][x-1]=0;
            Maze[y][x-2]=0;
            Move_flag=1;
            temp_d=2;
            DFS_CreateMaze(x-2,y);
        }       
        break;
           }
    // 向右
    case 3:{
        if(Maze[y][x+3]>0 && Maze[y-1][x+2]>0 && Maze[y+1][x+2]>0 && x+2<N)
        {
            Maze[y][x+1]=0;
            Maze[y][x+2]=0;
            Move_flag=1;
            temp_d=3;
            DFS_CreateMaze(x+2,y);  
        }
        break;
           }
    }
    return Move_flag;
}// 深度优先生成迷宫
// 深度优先生成迷宫
void DFS_CreateMaze(int x,int y)
{
    //置通路
    Maze[y][x]=0;
    //随机方向
    int d=rand()%4;
    int direct=temp_d;
    int t[4]={0,0,0,0};// 已走方向标记
    Move_flag=0;// 重置移动标记
    // 保证随机性的同时走满地图
    while(t[0]!=1 || t[1]!=1 || t[2]!=1 || t[3]!=1)
    {
        int i=rand()%4;
        if(t[i]!=1) 
        {
            t[i]=1;
            Move(i,x,y);
        }
    }
}
// 深度优先搜索迷宫路径
void FindPath(int x,int y)
{
    // 置路径
    Maze[y][x]=2;
    path_x[count][lengtha]=x;
    path_y[count][lengtha]=y;
    lengtha++;
    // 搜索到目的地
    if(x==end_x && y==end_y)
    {
        //success=1;
        length[count] = lengtha;
        count++;
        if(count>0)
        {
            // 复制上一次的路径到下一次待搜索路径中
            for(int i=0;i<length[count-1];i++)
            {
                path_x[count][i]=path_x[count-1][i];
                path_y[count][i]=path_y[count-1][i];
            }
        }
    }
    else
    {
        // 搜索并回溯
        if(Maze[y][x+1]==0) {FindPath(x+1,y); Maze[y][x+1]=0;lengtha--;}
        if(Maze[y+1][x]==0) {FindPath(x,y+1); Maze[y+1][x]=0;lengtha--;}
        if(Maze[y][x-1]==0) {FindPath(x-1,y); Maze[y][x-1]=0;lengtha--;}
        if(Maze[y-1][x]==0) {FindPath(x,y-1); Maze[y-1][x]=0;lengtha--;}
    }
    //if(success!=1)
    //return success;
}
// 创建迷宫
void CreateMaze()
{
    // 初始化迷宫数组
    InitMaze();
    isShowMazePath = 0;
    // 从终点开始逆向生成迷宫
    start_x=1;
    start_y=1;
    end_x=N-2;
    end_y=M-2;
    DFS_CreateMaze(end_x,end_y);
    // 搜索路径
    lengtha=0;
    count=0;
    FindPath(start_x,start_y);
    // 结果路径复制到地图
    for(int i=0;i<length[0];i++)
    {
        Maze[path_y[0][i]][path_x[0][i]]=2;
    }
}
// 绘制菜单按钮
void DrawMenuItems()
{
    RECT r;
    r.left=860;
    r.top=610;
    r.right=r.left+90;
    r.bottom=r.top+38;
    setcolor(RGB(50,50,50));
    if(IsInRect(mouseX,mouseY,r)) 
    {
        setfillcolor(RGB(220,220,220));
        // 生成迷宫按键响应
        if(isMouseDown)
        {
            // 创建迷宫
            CreateMaze();
            // 绘制一次到屏幕并复制到缓存
            OutputMaze();
            CopyScreenBuffer();
            isMouseDown = 0;
        }
    }
    else setfillcolor(RGB(240,240,240));
    fillrectangle(r.left,r.top,r.right,r.bottom);
    settextstyle(25, 9, _T("Verdana"));
    setcolor(RGB(60,60,60));
    outtextxy(r.left+5, r.top+5,_T(" 生成迷宫"));
    r.left=1020;
    r.top=610;
    r.right=r.left+90;
    r.bottom=r.top+38;
    setcolor(RGB(50,50,50));
    if(IsInRect(mouseX,mouseY,r)) 
    {
        setfillcolor(RGB(220,220,220));
        // 显示/隐藏路径
        if(isMouseDown)
        {
            isShowMazePath = !isShowMazePath;
            isMouseDown = 0;
        }
    }
    else setfillcolor(RGB(240,240,240));
    fillrectangle(r.left,r.top,r.right,r.bottom);
    settextstyle(25, 9, _T("Verdana"));
    setcolor(RGB(60,60,60));
    if(isShowMazePath) outtextxy(r.left+5, r.top+5,_T(" 隐藏路径"));
    else outtextxy(r.left+5, r.top+5,_T(" 显示路径"));
}
// 主函数
void main(){
    // 置随机数种子 
    srand(unsigned(time(NULL)));
    // 初始化设备,加载图片
    initgraph(WIN_WIDTH, WIN_HEIGHT);
    // 设置窗口标题
    SetWindowText(GetHWnd(),_T("Maze v0.9 By:ls9512"));
    cleardevice();
    // 设置黑色背景
    setbkmode(TRANSPARENT);
    settextcolor(BLACK);
    // 开启双缓冲,防止闪屏
    BeginBatchDraw();
    // 鼠标消息变量
    MOUSEMSG mmsg;
    // 获取显存指针
    p_Screen = GetImageBuffer();
    // 创建迷宫
    CreateMaze();
    // 绘制一次到屏幕并复制到缓存
    OutputMaze();
    CopyScreenBuffer();
    // 主循环
    while(true)
    {
        //处理鼠标消息
        while(MouseHit())
        {
            mmsg = GetMouseMsg();
            switch(mmsg.uMsg)
            {
                case WM_MOUSEMOVE:  mouseX = mmsg.x; mouseY = mmsg.y; break;
                case WM_LBUTTONDOWN: isMouseDown=true ;break;
                case WM_LBUTTONUP: isMouseDown=false;break;
            }
        }
        // 绘制,将缓存数据复制到屏幕提速
        DrawScreenBuffer();
        OutPutPath();
        DrawMenuItems();
        DrawFPS();
        // 显示缓存的绘制内容
        FlushBatchDraw();
        // 延迟,帧数控制
        Delay(13);
    }
    // 关闭
    EndBatchDraw();
    closegraph();
} 



基于C++-EasyX编写的益智小游戏-迷宫.zip 一、开启c++ 游戏之门 对于许多初学者来说,c++ 可能是一门既神秘又令人畏惧的语言。但其实,c++ 也可以非常有趣!这次我们为您带来了一系列c++ 小游戏资源,旨在让您在轻松愉快的氛围中,逐步掌握c++ 的精髓。 二、资源亮点 由浅入深:我们为您提供了从入门级到进阶级的多种小游戏资源,满足您不同阶段的学习需求。 实践为王:这些资源不仅仅是理论,更有实际可运行的代码,让您亲身体验编程的乐趣。 模块化设计:每个游戏都按照功能模块进行划分,方便您学习和理解。 社区参与:我们鼓励您参与到社区中,与其他学习者分享经验,共同进步。 三、适用人群 无论您是初涉编程的新手,还是希望深入了解c++ 的进阶者,这些资源都能为您提供宝贵的实践机会。 四、使用建议 边学边做:建议您在学习过程中,积极动手实践,亲自感受c++ 的魅力。 不断挑战:尝试自行修改和优化游戏代码,培养独立思考和解决问题的能力。 交流与分享:加入我们的学习社群,与其他学习者交流心得,共同成长。 五、注意事项 尊重版权:请确保在使用这些资源时,遵循版权法规,尊重原创者的权益。 安全为先:在编写和运行代码时,请确保您的开发环境安全可靠,避免潜在风险。 持续学习:编程是一个不断进阶的过程,希望您能保持对知识的热情,持续深入学习。 感谢您选择我们的c++ 小游戏资源系列!让我们一起在探索中成长,用代码书写属于我们的精彩故事!
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ls9512

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值