迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;
要求查找并理解迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫。
要求迷宫游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统提示迷宫路径要求基于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,用于在迷宫中显示路径。
运行结果