基于A*搜索算法迷宫游戏开发

迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;

 要求查找并理解迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫。

 要求迷宫游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统提示迷宫路径要求基于A*算法实现,输出玩家当前位置到迷宫出口的最优路径。设计交互友好的游戏图形界面

#include <iostream>
#include <iomanip>
#include <graphics.h>
#include <time.h>
#include <easyx.h>
#include <algorithm>
#include <vector>
#include <conio.h>
using namespace std;
#define SIZE 10
bool flag = 0;
enum dirct {
    Up,
    Down,
    Left,
    Right,
};
struct AStar
{
    int M;                       
    int** maze;                    
    int O_row;                    
    int O_col;                    	
    int E_row;                   
    int E_col;                    
}Astar{ 0,NULL,1,1,0,0 };
struct position {
    int row, col;
    int g;                        
    int h;                        
    int F = h + g;               
};
struct treeNode
{
    position pos;
    vector<treeNode*> child;
    treeNode* parent;
};
treeNode* creatTreeNode(int row, int col);                        
int getH(position pos, AStar a);                                
void Init_Maze(AStar& Astar);                                    
void show(AStar& Astar);                                       
void creatMaze(AStar& Astar, int x_index, int y_index);            
void makeMaze(AStar& Astar);                                    
bool findneighbor(AStar& Astar, int x_index, int y_index);        
int main()
{
    treeNode* pRoot = NULL;     
    vector<treeNode*> open;  
    vector<treeNode*>::iterator it;    
    vector<treeNode*>::iterator itmin;    
    Astar.M = 0;
    cout << "请输入迷宫是几乘几的" << endl;
    cin >> Astar.M;
    if (Astar.M % 2 == 0)
        Astar.M++;
    Astar.E_col = Astar.M;
    Astar.E_row = Astar.M;
    pRoot = creatTreeNode(Astar.O_row, Astar.O_col); 
    srand((unsigned)time(0));
    initgraph(((Astar.M + 2) * 2 + 1) * SIZE, (Astar.M + 2) * SIZE, SHOWCONSOLE);
    Astar.maze = new int* [Astar.M + 2];
    for (int i = 0; i < Astar.M + 2; i++)
        Astar.maze[i] = new int[Astar.M + 2];
    Init_Maze(Astar);
    creatMaze(Astar, 1, 1);
    makeMaze(Astar);
    show(Astar);
    treeNode* Now = pRoot;
    pRoot->pos.F = Astar.M * Astar.M;
    open.push_back(pRoot);
    int playerRow = Astar.O_row;  
    int playerCol = Astar.O_col;  
    bool autoMove = false; 
    bool autoComplete = false;  
    char key; 
    while (1)
    {
        key = _getch();  
        switch (key)
        {
        case 'w':
        case 'W':
            if (!autoMove && playerRow > 1 && Astar.maze[playerRow - 1][playerCol] != 1)
                playerRow--;
            break;
        case 's':
        case 'S':
            if (!autoMove && playerRow < Astar.M && Astar.maze[playerRow + 1][playerCol] != 1)
                playerRow++;
            break;
        case 'a':
        case 'A':
            if (!autoMove && playerCol > 1 && Astar.maze[playerRow][playerCol - 1] != 1)
                playerCol--;
            break;
        case 'd':
        case 'D':
            if (!autoMove && playerCol < Astar.M && Astar.maze[playerRow][playerCol + 1] != 1)
                playerCol++;
            break;
        case ' ': 
           
            break; if (!autoMove)
            {
                autoMove = true;
                autoComplete = false;
               
                treeNode* pNode = pRoot;
                while (pNode)
                {
                    Astar.maze[pNode->pos.row][pNode->pos.col] = 0;
                    treeNode* pParent = pNode->parent;
                    delete pNode;
                    pNode = pParent;
                }
                pRoot = creatTreeNode(Astar.O_row, Astar.O_col);
                Now = pRoot;
                Now->pos.F = Astar.M * Astar.M;
                open.clear();
                open.push_back(pRoot);
            }
        default:
            break;
        }

        if (autoMove)
        {
            while (!autoComplete)
            {
            for (int i = 0; i < 4; i++)
            {
                treeNode* pChild = creatTreeNode(Now->pos.row, Now->pos.col);
                switch (i)
                {
                case Up:
                    pChild->pos.row--;
                    pChild->pos.g = Now->pos.g + 1;
                    break;
                case Down:
                    pChild->pos.row++;
                    pChild->pos.g = Now->pos.g + 1;
                    break;
                case Left:
                    pChild->pos.col--;
                    pChild->pos.g = Now->pos.g + 1;
                    break;
                case Right:
                    pChild->pos.col++;
                    pChild->pos.g = Now->pos.g + 1;
                    break;
                default:
                    break;
                }
                if (Astar.maze[pChild->pos.row][pChild->pos.col] == 0 || Astar.maze[pChild->pos.row][pChild->pos.col] == 8
                    || Astar.maze[pChild->pos.row][pChild->pos.col] == 2)    
                {
                    Astar.maze[pChild->pos.row][pChild->pos.col] = 4;
                    pChild->pos.h = getH(pChild->pos, Astar);
                    pChild->pos.F = pChild->pos.g + pChild->pos.h;
                    Now->child.push_back(pChild);
                    pChild->parent = Now;
                    open.push_back(pChild);
                }
                else {
                    delete pChild;
                }
            }
            itmin = open.begin();
            for (it = open.begin(); it != open.end(); it++)
            {
                itmin = ((*it)->pos.F <= (*itmin)->pos.F) ? it : itmin;    
            }
            Now = *itmin;
            show(Astar);
            open.erase(itmin);

            if (Now->pos.row == Astar.E_row && Now->pos.col == Astar.E_col)
            {
                flag = 1;
                autoComplete = true;  
                break;
            }

            if (open.empty())
            {
                autoComplete = true;  
                break;
            }
            }
        }

        Astar.maze[playerRow][playerCol] = 8;
        show(Astar);
        if (autoComplete)
            break;
    }
    if (flag)
    {
        cout << "success" << endl;
        while (Now)
        {
            cout << "(" << Now->pos.row << "," << Now->pos.col << ")";
            printf("(g:%d,h:%d,f:%d)\n", Now->pos.g, Now->pos.h, Now->pos.F);
            Astar.maze[Now->pos.row][Now->pos.col] = 3;
            Now = Now->parent;
        }
    }
    else
    {
        cout << "not found" << endl;
    }
    while (1)
    {
        BeginBatchDraw();
        show(Astar);
        FlushBatchDraw();
    }
    closegraph();
}
void Init_Maze(AStar& Astar)
{
    for (int i = 0; i < Astar.M + 2; i++)
        for (int j = 0; j < Astar.M + 2; j++)
        {
            if (i == 0 || i == Astar.M + 1)
                Astar.maze[i][j] = 1;
            if ((i % 2 == 1) && (j % 2 == 1))
                Astar.maze[i][j] = 0;
            else Astar.maze[i][j] = 1;
        }
}
void show(AStar& Astar)
{
    IMAGE star, wall, ball, ly;
    loadimage(&star, "./p/球.png", SIZE, SIZE);
    loadimage(&wall, "./p/墙.png", SIZE, SIZE);
    loadimage(&ball, "./p/星.png", SIZE, SIZE);
    loadimage(&ly, "./p/路.png", SIZE, SIZE);
    for (int i = 0; i < Astar.M + 2; i++)
    {
        for (int j = 0; j < Astar.M + 2; j++)
        {
            if (Astar.maze[i][j] == 1)
            {
                putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &wall);
                putimage(j * SIZE, i * SIZE, &wall);
            }
            else if (Astar.maze[i][j] == 4)
            {
                putimage((j + Astar.M + 2 + 1) * SIZE, (i)*SIZE, &ball);
            }
            else if (Astar.maze[i][j] == 3)
            {
                putimage(j * SIZE, (i)*SIZE, &ly);
            }
            else if (Astar.maze[i][j] == 8)
            {
                putimage(j * SIZE, (i)*SIZE, &star);
            }
        }
    }
    putimage((Astar.M + 2 + 1 + 1) * SIZE, (SIZE), &star);
    putimage((Astar.M + Astar.M + 2 + 1) * SIZE, (Astar.M) * SIZE, &star);
    putimage(SIZE, SIZE, &star);
    putimage(Astar.M * SIZE, Astar.M * SIZE, &star);
}
bool findneighbor(AStar& Astar, int x_index, int y_index)
{
    if ((x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0) || (x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)
        || (y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0) || (y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0))
        return 1;
    else
        return 0;
}
void creatMaze(AStar& Astar, int x_index, int y_index)
{
    int pos, x, y, flag = 0;
    x = x_index;
    y = y_index;
    while (1)
    {
        flag = 0;
        flag = findneighbor(Astar, x, y);
        if (!flag)
            return;
        else {
            Astar.maze[x_index][y_index] = 5;
            x = x_index;
            y = y_index;
            while (1)
            {
                pos = rand() % (4 - 1 + 1) + 1;
                if (pos == 1 && x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0)
                {
                    x_index -= 2;
                }
                else if (pos == 2 && x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)
                {
                    x_index += 2;
                }
                else if (pos == 3 && y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0)
                {
                    y_index += 2;
                }
                else if (pos == 4 && y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0)
                {
                    y_index -= 2;
                }
                Astar.maze[(x + x_index) / 2][(y + y_index) / 2] = 5;
                Astar.maze[x_index][y_index] = 5;
                creatMaze(Astar, x_index, y_index);
                break;
            }
        }
    }
}
void makeMaze(AStar& Astar)
{
    for (int i = 0; i < Astar.M + 2; i++)
        for (int j = 0; j < Astar.M + 2; j++)
        {
            if (Astar.maze[i][j] == 5)
            {
                Astar.maze[i][j] = 0;
            }
        }
    Astar.maze[1][1] = 8;
    Astar.maze[Astar.M][Astar.M] = 2;
}
int getH(position pos, AStar a) {
    return (a.E_row - pos.row) + (a.E_col - pos.col);
}
treeNode* creatTreeNode(int row, int col)
{
    treeNode* pNew = new treeNode;
    memset(pNew, 0, sizeof(treeNode));
    pNew->pos.col = col;
    pNew->pos.row = row;
}

算法思想

通过递归回溯算法来生成迷宫

  • findneighbor(Astar, x, y) 函数用于判断当前位置的上、下、左、右四个相邻位置是否为空,如果存在空位置则返回 1,否则返回 0。
  • 如果当前位置没有空相邻位置,即 !flag,则函数直接返回,结束递归。
  • 否则,会在当前位置设置为通路(值为 5),然后随机选择一个空相邻位置进行下一步的递归调用。
  • 随机选择下一步的相邻位置时,使用 rand() 函数生成一个随机数 pos,在 1 到 4 之间,分别代表上、下、左、右四个方向。根据选择的方向和条件判断,更新下一步的坐标位置。
  • 在更新下一步坐标位置前,还会将当前位置和下一步位置之间的中间位置标记为通路。
  • 然后,对更新后的下一步位置进行递归调用 creatMaze(Astar, x_index, y_index),进一步生成迷宫。
  • 递归继续进行,直到所有位置都被处理完或无可用的相邻位置时,迷宫生成过程结束。
void creatMaze(AStar& Astar, int x_index, int y_index)
{
    int pos, x, y, flag = 0;
    x = x_index;
    y = y_index;
    while (1)
    {
        flag = 0;
        flag = findneighbor(Astar, x, y);
        if (!flag)
            return;
        else {
            Astar.maze[x_index][y_index] = 5;
            x = x_index;
            y = y_index;
            while (1)
            {
                pos = rand() % (4 - 1 + 1) + 1;
                if (pos == 1 && x_index >= 3 && Astar.maze[x_index - 2][y_index] == 0)
                {
                    x_index -= 2;
                }
                else if (pos == 2 && x_index <= Astar.M - 1 && Astar.maze[x_index + 2][y_index] == 0)
                {
                    x_index += 2;
                }
                else if (pos == 3 && y_index <= Astar.M - 1 && Astar.maze[x_index][y_index + 2] == 0)
                {
                    y_index += 2;
                }
                else if (pos == 4 && y_index >= 3 && Astar.maze[x_index][y_index - 2] == 0)
                {
                    y_index -= 2;
                }
                Astar.maze[(x + x_index) / 2][(y + y_index) / 2] = 5;
                Astar.maze[x_index][y_index] = 5;
                creatMaze(Astar, x_index, y_index);
                break;
            }
        }
    }
}

A*算法的体现

根据当前位置,分别向上、下、左、右四个方向搜索相邻节点,并计算每个节点的F值(包括G值和H值),然后将所有的相邻节点加入开启列表中。然后遍历开启列表,通过比较F值找到F值最小的节点作为当前节点,并将该节点加入关闭列表中。如果当前节点就是终点,则表明寻路成功;否则,继续搜索直到开启列表为空。最后,通过回溯查找到从起点到终点的最短路径,同时在地图上标记出路径经过的位置。

1. AStar结构体中定义了起点和终点的位置,以及迷宫的大小和数据结构。

2. getH函数计算当前节点到终点的曼哈顿距离,作为启发式函数。

3. creatTreeNode函数创建一个新的节点,并将其位置初始化为给定的行和列。

4. 执行自动移动时,根据当前位置生成四个子节点,计算它们的F值(g+h),将其加入到open列表中。

5. 在每次循环中,选择F值最小的节点,将其作为当前节点,并从open列表中删除。

6. 如果当前节点为终点,则找到一条路径;如果open列表为空,则说明找不到路径。

7. 在找到路径后,将路径上的节点标记为3,用于在迷宫中显示路径。

运行结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值