一、主要功能
①迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;
②要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于 A*算法实现,输出走迷宫的最优路径并显示;
③设计交互友好的游戏图形界面。
二、系统设计
三、关键算法
算法 1:自动寻路
【1】算法功能
完成对双栈的操作,实现对初始栈和自动寻路栈的更新,符合递归次序的即为原点与终点的路径
【2】算法基本思想
重复以下步骤,直到遍历到终点:
1)使用双循环结构,选取当前列表中节点,将这个节点称为 A
2)将 A与 recordMatrix 列表中的值进行比较,若原有栈中不为空,则将其添加入栈
3)对于与 A 相邻的每一块可移动的相邻节点 B: 如果其非空且栈顶不相同则执行入栈操作。随后而需进行校验,倘若原有栈与自动寻路栈存储的内容一致则继续下行操作。
4)重复步骤,找到路径,然后回溯,计算出最终的路径
【3】代码展示
void Maze::autoFindPath()
{
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
recordMatrix[i][j].state= 1 ;//初始化recordMatrix的值(全1)
recordMatrix[i][j].i = matrix[i][j].i = i;
recordMatrix[i][j].j = matrix[i][j].j = j;
}
}
pathStack.clear();
recordMatrix[X][Y].state = 0; //出口位置标记为已经访问过,0为墙
point temp(X,Y,0);
pathStack.push_back(temp);
QList<point> stack;
int i , j , d ;
while(!pathStack.isEmpty())
{
point top = pathStack.back();
pathStack.pop_back();//将pathStack中的值出栈
if(!autoPath.isEmpty()&& !(top==autoPath.back()))
autoPath.push_back(top);//如果其非空且栈顶不相同则执行入栈操作
if(autoPath.isEmpty())
autoPath.push_back(top);
i = top.i;j=top.j;d=top.state;
//autoPath中存储的值即始末点的对应路径
while(d < 4)
{
temp.i = i+move[d].i;
temp.j = j+move[d].j;
if(temp.i == height-2 &&temp.j ==width-2)
{
pathStack.push_back(top);
pathStack.push_back(temp);
autoPath.push_back(temp);
for(int i=0 ; i<pathStack.size();i++)
{
qDebug()<<"("<<pathStack[i].i<<","<<pathStack[i].j<<")";
}
return;
}
//如果最开始的栈与后来记录路径的栈线路相同则其为原点到终点的路线
if(matrix[temp.i][temp.j].state == 1 && recordMatrix[temp.i][temp.j].state == 1)
{
recordMatrix[temp.i][temp.j].state = 0;
top.state=d;
pathStack.push_back(top);
temp.state=0;
pathStack.push_back(temp);
autoPath.push_back(temp);//自动寻路的路径对pathStack的值进行压栈操作
break;
}
d++;
}
if(d==4)
autoPath.push_back(pathStack.back());
}
}
算法 2:深度优先算法
【1】算法功能
【2】 算法基本思想
(1)把数组地图初始化为如下结构。选择一个靠近边缘的 1 作为起点,在它的周围随机找 另一个黄色的 1(这里的“周围”指的是上下左右 4 个方向)。找到就把他们联通,并且把 两个 1 之间的 0(灰色墙)也变成通路,这里用红色来表示。
(2)选择一个靠近边缘的 1 作为起点,在它的周围随机找另一个黄色的 1(这里的“周围” 指的是上下左右 4 个方向)。找到就把他们联通,并且把两个 1 之间的 0(灰色墙)也变成通路,这里用红色来表示。
(3)把上一步”终点”的格子作为新的一个“起点”格子,不断循环第2 步的过程直到找不到周围有黄色的 1,就回溯,回到之前的位置,看看周围是否有黄色的 1,如果有,就按照 2 步骤,不断将黄色 1 变联通,接下来就是不停地重复上面的步骤,找到就联通,找不到就往回走。
(4)遍历完所有的点即可生成一个迷宫,然后再选择出口与入口,一个完整的迷宫就形成了。
【3】代码展示
void Maze::dfs(int x,int y)
{
int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
if(x==height-2 && y==width-2)//成功---下面处理路径问题
{
while(!path.empty())//将path里面的点取出来,放在pa里面
{//path从栈顶-栈底的方向,路径是从终点-起点的顺序
point p1 = path.top();
path.pop();
pa.push(p1);
}
while(!pa.empty())
{//输出temp里面的路径,这样刚好是从起点到终点的顺序
point p1 = pa.top();
pa.pop();
path.push(p1);//将路径放回path里面,因为后面还要回溯!!!
point temp(p1.i,p1.j,0);
allpath.push_back(temp);
qDebug()<< "(" << p1.i << "," << p1.j << ")";
}
return;
}
if(x<=0 || x>=height-1 || y<=0 || y>=width-1)//越界
return;
//如果到了这一步,说明还没有成功,没有出界
for(int i=0;i<4;i++)//从4个方向探测
{
int nx = x + dir[i][0];
int ny = y + dir[i][1];//nx,ny:选择一个方向,前进一步之后,新的坐标
if(0<=nx && nx<height-1 && 0<=ny && ny<width-1 && matrix[nx][ny].state==1 && recordMatrix[nx][ny].state==1)
{//条件:nx,ny没有出界,maze[nx][ny]=0这个点不是障碍可以走,vis[nx][ny]=0说明(nx,ny)没有访问过,可以访问
recordMatrix[nx][ny].state=0;//设为访问过
point p(nx,ny,0);
path.push(p);//让当前点进栈
dfs(nx,ny);//进一步探测
recordMatrix[nx][ny].state=1;//回溯
path.pop();//由于是回溯,所以当前点属于退回去的点,需要出栈
}
}
}
四、主体代码
开始加载页面
#include "interface.h"
#include <QPalette>
#include <QPixmap>
#include <QFont>
#include <QThread>
int tim;
QString msg[] = {"加载中..","加载中...","加载中...."};
interface::interface(QWidget *parent) :
QWidget(parent)
{
setGeometry(0,0,720,456);
initWidgets();
label = new QLabel(this);
label->setGeometry(200,80,250,200);
label->setContentsMargins(0,0,0,0);
movie=new QMovie(":/new/image/playbutton.png");
label->setMovie(movie);
label->hide();
msgLabel = new QLabel(this);
msgLabel->setGeometry(320,250,100,75);
msgLabel->setFont(QFont("宋体",13));
msgLabel->hide();
}
void interface::paintEvent(QPaintEvent* event)
{
QPainter* painter = new QPainter(this);
QImage pixmap(":/new/image/ditu.jpg");
painter->drawImage(geometry(),pixmap);
}
void interface::initWidgets()
{
probar = new QProgressBar(this);
probar->setGeometry(120,300,480,10);
probar->setTextVisible(true);
probar->hide();
this->thread = new QThread(this);
timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(loading()));
button= new QPushButton(this);
button->setGeometry(400,200,200,100);
button->setStyleSheet("QPushButton{border-image: url(:/new/image/playbutton.png);}"
"QPushButton:hover{border-image: url(:/new/image/playbutton1.png);}");
connect(button,SIGNAL(clicked()),this,SLOT(startButton()));
}
void interface::startButton()
{
button->hide();
label->show();
msgLabel->show();
movie->start();
timer->start(100);
probar->show();
}
void interface::loading()
{
msgLabel->setText(msg[((tim++)/2)%3]);
probar->setValue(probar->value()+2);
if(probar->value() >= 99)
delete this;
}
游戏控件、ui、系统状态、角色移动等
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QStyleFactory>
#include <QMessageBox>
#include <QStack>
#include <QVector>
#include <QQueue>
#include <QTime>
#include <QTimer>
#include <QKeyEvent>
#include <QMovie>
#include <QPropertyAnimation>
#include <QSequentialAnimationGroup>
#include "maze.h"
#include <QMessageBox>
#include <QFileDialog>
#include <QTextStream>
#include <QDebug>
#include <fstream>
#include <string>
#define BASIC_LENGTH 40
using namespace std;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setFocusPolicy(Qt::StrongFocus);
initCharacter();
initWidget();
}
void MainWindow::refresh(int h,int w ,int (&map)[31][31])
{
//initWidget();
//initCharacter();
ui->spinBox->setValue(h);
ui->spinBox_2->setValue(w);
timer->stop();
group->stop();
time = 1;
step = 0;
isShowPath = false;
isPlayMode = false;
isPaintPath = false;
maze.refreshMaze(h,w,map);
if(maze.height*30+80 <=320)
this->setGeometry(40,40,240+maze.width*BASIC_LENGTH,320);
else
this->setGeometry(40,40,240+maze.width*BASIC_LENGTH,maze.height*BASIC_LENGTH+80);
ui->groupBox->move(width()-180,10);
//重新初始化mazeWidgets和_pMouse
_pMouse->hide();
for(int i=0;i<31;i++)
{
for(int j=0 ; j<31;j++)
{
mazeWidgets[i][j]->setStyleSheet("");
mazeWidgets[i][j]->hide();
}
}
//maze.autoFindPath();
xPos = 40 + maze.X*BASIC_LENGTH;
yPos = 40 + maze.Y*BASIC_LENGTH;
showMaze();
}
//初始化ui的widgets
void MainWindow::initWidget()
{
//初始化界面控件
ui->spinBox->setStyle(QStyleFactory::create("Macintosh"));
ui->spinBox_2->setStyle(QStyleFactory::create("Macintosh"));
ui->buttonGenerate->setStyle(QStyleFactory::create("fusion"));
ui->buttonPlay->setStyle(QStyleFactory::create("fusion"));
ui->buttonPath->setStyle(QStyleFactory::create("fusion"));
connect(ui->buttonGenerate,SIGNAL(clicked()),this,SLOT(createMazeBtn()));
connect(ui->buttonPath,SIGNAL(clicked()),this,SLOT(showAnimation()));
connect(ui->buttonPlay,SIGNAL(clicked()),this,SLOT(playMode()));
//connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(showPath()));
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(allpaths()));
connect(ui->pushButton_3,SIGNAL(clicked()),this,SLOT(shortpath()));
connect(ui->pushButton_2,SIGNAL(clicked()),this,SLOT(change()));
connect(ui->actionfilein,SIGNAL(triggered()),this,SLOT(filein()));
connect(ui->actionfileout,SIGNAL(triggered()),this,SLOT(fileout()));
ui->buttonPath->setStyleSheet("QPushButton{border-image: url(:/new/image/button1.png);}"
"QPushButton:hover{border-image: url(:/new/image/button4.png);}"
"QPushButton:pressed{border-image: url(:/new/image/button2.png);}");
ui->buttonPlay->setEnabled(false);
ui->buttonPath->setEnabled(false);
//游戏的状态
isPlayMode = false;
isShowPath = false;
//创建好迷宫砖块
for(int i=0; i <31 ;i++)
{
for(int j=0; j<31; j++)
{
mazeWidgets[i][j]=new QWidget(this);
mazeWidgets[i][j]->hide();
}
}
//人物
_pMouse = new QWidget(this);
_pMouse->setStyleSheet(character[0]);
_pMouse->hide();
//动画和时间相关的初始化
group = new QSequentialAnimationGroup();
isShowPath = false;
timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(moveCharacter()));
//初始化开始界面
surface = new interface(this);
}
bool MainWindow::checkIsOdd()
{
if(ui->spinBox->value()%2 == 0)
return false;
if(ui->spinBox_2->value()%2 == 0)
return false;
return true;
}
void MainWindow::createMazeBtn()
{
//停止之前可能进行的所有工作
timer->stop();
group->stop();
time = 1;
step = 0;
isShowPath = false;
isPlayMode = false;
isPaintPath = false;
if(!checkIsOdd())
{
QMessageBox::information(this,"警告","输入的数必须是奇数",QMessageBox::Yes);
return;
}
//窗口形状变形
maze.height = ui->spinBox->value();
maze.width = ui->spinBox_2->value();
if(maze.height*30+80 <=320)
this->setGeometry(40,40,240+maze.width*BASIC_LENGTH,320);
else
this->setGeometry(40,40,240+maze.width*BASIC_LENGTH,maze.height*BASIC_LENGTH+80);
ui->groupBox->move(width()-180,10);
//重新初始化mazeWidgets和_pMouse
_pMouse->hide();
for(int i=0;i<31;i++)
{
for(int j=0 ; j<31;j++)
{
mazeWidgets[i][j]->setStyleSheet("");
mazeWidgets[i][j]->hide();
}
}
//创建新的迷宫
maze.initMaze(ui->spinBox->value(),ui->spinBox_2->value());
maze.createMaze();
maze.autoFindPath();
xPos = 40 + maze.X*BASIC_LENGTH;
yPos = 40 + maze.Y*BASIC_LENGTH;
showMaze();
}
void MainWindow::showMaze()
{
_pMouse->setGeometry(40+maze.Y*BASIC_LENGTH,40+maze.X*BASIC_LENGTH,BASIC_LENGTH,BASIC_LENGTH);
_pMouse->show();
for(int i=0 ; i<maze.height ; i++)
{
for(int j=0 ; j<maze.width; j++)
{
mazeWidgets[i][j]->show();
mazeWidgets[i][j]->setGeometry(40+j*BASIC_LENGTH,40+i*BASIC_LENGTH,BASIC_LENGTH,BASIC_LENGTH);
if(maze.matrix[i][j].state == 0)
mazeWidgets[i][j]->setStyleSheet("border-image: url(:/new/image/Wall.png);");
if(maze.matrix[i][j].state == 1)
mazeWidgets[i][j]->setStyleSheet("border-image: url(:/new/image/Load.png);");
}
}
mazeWidgets[maze.height-2][maze.width-2]->setStyleSheet("border-image: url(:/new/image/liangc.gif);");
//更新按钮状态
ui->buttonPath->setEnabled(true);
ui->buttonPlay->setEnabled(true);
}
void MainWindow::showAnimation()
{
maze.autoFindPath();
if(isShowPath == true)
{
if(timer->isActive())
timer->stop();
group->stop();
isShowPath = false;
ui->buttonPlay->setEnabled(true);
ui->buttonPath->setStyleSheet("QPushButton{border-image: url(:/new/image/button1.png);}"
"QPushButton:hover{border-image: url(:/new/image/button4.png);}"
"QPushButton:pressed{border-image: url(:/new/image/button2.png);}");
return;
}
else
{
ui->buttonPath->setEnabled(true);
ui->buttonPath->setStyleSheet("QPushButton{border-image: url(:/new/image/button3.png);}"
"QPushButton:hover{border-image: url(:/new/image/button4.png);}"
"QPushButton:pressed{border-image: url(:/new/image/button2.png);}");
time = 1;
ui->buttonPlay->setEnabled(false);
isPlayMode = false;
isShowPath =true;
time = 1;
group->clear();
for(int i=0; i<maze.autoPath.size()-1;i++)
{
QPropertyAnimation* animation = new QPropertyAnimation(_pMouse, "pos");
animation->setDuration(400);
animation->setStartValue(QPoint(40+maze.autoPath[i].j*BASIC_LENGTH,40+maze.autoPath[i].i*BASIC_LENGTH));
animation->setEndValue(QPoint(40+maze.autoPath[i+1].j*BASIC_LENGTH,40+maze.autoPath[i+1].i*BASIC_LENGTH));
animation->setEasingCurve(QEasingCurve::Linear);
group->addAnimation(animation);
}
timer->start(400);
group->start();
}
ui->buttonPlay->setEnabled(true);
}
void MainWindow::warn()
{
//time1->stop();
isPlayMode = true;
QMessageBox::warning(this,"TimeOver","You haven't moved the character to the box at the specified time, the system will automatically generate the path for you!");
// QTimer *t;
//t=new QTimer(this);
//t->start(6000);
//connect(t,SIGNAL(timeout()),this,SLOT(showAnimation()));
//t->stop();
showAnimation();
}
bool MainWindow::checkadd()
{
if(xPos/40<=maze.height&&yPos/40<=maze.width&&xPos/40>=maze.height-1&&yPos/40>=maze.width-1)
{
QMessageBox::information(this,"Win","You win!");
isPlayMode = false;
showPath();
playMode();
return true;
}
return false;
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(isPlayMode == false)
return;
if(checkadd())
{
return;
}
else{
step++;
switch(event->key())
{
case Qt::Key_W:
if(maze.matrix[(xPos-8-40)/40][maze.Y].state == 0)
{
return;
}
xPos-=8;
maze.X=(xPos-40)/40;
_pMouse->move(yPos,xPos);
_pMouse->setStyleSheet(character[3+(step/5)%3]);
break;
case Qt::Key_S:
if(maze.matrix[(xPos)/40][maze.Y].state == 0)
{
return;
}
xPos+=8;
maze.X=(xPos-40)/40;
_pMouse->move(yPos,xPos);
_pMouse->setStyleSheet(character[0+(step/5)%3]);
break;
case Qt::Key_A:
if(maze.matrix[maze.X][(yPos-8-40)/40].state == 0)
{
return;
}
yPos-=8;
maze.Y=(yPos-40)/40;
_pMouse->move(yPos,xPos);
_pMouse->setStyleSheet(character[6+(step/5)%3]);
break;
case Qt::Key_D:
if(maze.matrix[maze.X][(yPos)/40].state == 0)
{
return;
}
yPos+=8;
maze.Y=(yPos-40)/40;
_pMouse->move(yPos,xPos);
_pMouse->setStyleSheet(character[9+(step/5)%3]);
break;}
}
}
void MainWindow::playMode()
{
time1=new QTimer();
connect(time1,SIGNAL(timeout()),this,SLOT(warn()));
time1->start(45000);
if(isPlayMode == false)
{
isPlayMode =true;
ui->buttonPath->setEnabled(false);
}
else
{
isPlayMode = false;
ui->buttonPath->setEnabled(true);
time1->stop();
}
}
void MainWindow::moveCharacter()
{
if(maze.autoPath[time+1].i < maze.autoPath[time].i)
{
_pMouse->setStyleSheet(character[3+time%3]);
}
if(maze.autoPath[time+1].i > maze.autoPath[time].i)
{
_pMouse->setStyleSheet(character[0+time%3]);
}
if(maze.autoPath[time+1].j > maze.autoPath[time].j)
{
_pMouse->setStyleSheet(character[9+time%3]);
}
if(maze.autoPath[time+1].j < maze.autoPath[time].j)
{
_pMouse->setStyleSheet(character[6+time%3]);
}
time++;
if(time == maze.autoPath.size()-1)
{
timer->stop();
time = 0;
}
}
void MainWindow::initCharacter()
{
character[0] = "border-image: url(:/new/image/down1.png);";
character[1] = "border-image: url(:/new/image/down2.png);";
character[2] = "border-image: url(:/new/image/down3.png);";
character[3] = "border-image: url(:/new/image/up1.png);";
character[4] = "border-image: url(:/new/image/up2.png);";
character[5] = "border-image: url(:/new/image/up3.png);";
character[6] = "border-image: url(:/new/image/left1.png);";
character[7] = "border-image: url(:/new/image/left2.png);";
character[8] = "border-image: url(:/new/image/left3.png);";
character[9] = "border-image: url(:/new/image/right1.png);";
character[10]= "border-image: url(:/new/image/right2.png);";
character[11]= "border-image: url(:/new/image/right3.png);";
}
void MainWindow::showPath()
{
//maze.autoFindPath();
for(int i=0; i<maze.pathStack.size();i++)
{
point temp = maze.pathStack[i];
int dir = maze.pathStack[i].state;
switch (dir) {
case 1:
dir = 3;
break;
case 2:
dir =9;
break;
case 3:
dir = 0;
break;
case 4:
dir = 6;
break;
default:
break;
}
if(!isPaintPath)
{
mazeWidgets[temp.i][temp.j]->setStyleSheet(character[dir+i%3]);
}
else
{
mazeWidgets[temp.i][temp.j]->setStyleSheet("border-image: url(:/new/image/Load.png);");
mazeWidgets[maze.height-2][maze.width-2]->setStyleSheet("border-image: url(:/new/image/box.png);");
}
}
isPaintPath = !isPaintPath;
}
void MainWindow::change()
{
int dx=ui->spinBox_3->value();
int dy=ui->spinBox_4->value();
maze.changePath(dx,dy);
isPlayMode = false;
isShowPath = false;
showMaze();
return;
}
void MainWindow::filein()
{
QString path=QFileDialog::getOpenFileName(this,"打开文件","C:\\");
string pa=path.toStdString();
char*p=(char*)pa.data();
ifstream in;
in.open(p, ios::in) ;
// QFile file(path);
//file.open(QIODevice::ReadOnly);
//QTextStream stream(&file);
int buf;
if (! in.is_open())
{ qDebug() << "Error opening file"; exit (1); }
in>>buf;
int h=buf;
in>>buf;
int w=buf;
//QStringList list=stream.readAll().split(' ');
//QListIterator<QString> li(list);
//buf=li.next().toInt();
//ui->spinBox->setValue(buf);
//buf=li.next().toInt();
//ui->spinBox_2->setValue(buf);
int i=0,j=0;
int map[31][31];
while (!in.eof() )
//while(li.hasNext())
{
// buf=li.next().toInt();
in>>buf;
map[i][j]=buf;
j++;
if(j==w)
{
j=0;
i++;
}
}
qDebug()<<h<<'z'<<w<<'z'<<i<<'z'<<j;
for (int i=0;i<h;i++)
{
for(int j=0;j<w;j++)
{
qDebug()<<map[i][j]<<' ';
}
}
in.close();
//initWidget();
refresh(h,w,map);
return;
}
void MainWindow::fileout()
{
//QString path=QFileDialog::getOpenFileName(this,"打开文件夹","C:\\Users\\ASUS\\Desktop");
QString path = QFileDialog::getExistingDirectory(this, tr("选择保存路径"), "/home", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
path+="\\data.txt";
QFile file(path);
file.open(QIODevice::WriteOnly);
QString d = QString::number(maze.height);
file.write(d.toLatin1().data());
file.write(" ");
d=QString::number(maze.width);
file.write(d.toLatin1().data());
file.write(" ");
for (int i=0;i<maze.height;i++)
{
for(int j=0;j<maze.width;j++)
{
d=QString::number(maze.matrix[i][j].state);
file.write(d.toLatin1().data());
file.write(" ");
}
}
file.close();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::allpaths()
{
qDebug()<<"suoyoulujing";
maze.allpath.clear();
for(int i=0;i<maze.height;i++)
{
for(int j=0;j<maze.width;j++)
{
maze.recordMatrix[i][j].state= 1 ;
maze.recordMatrix[i][j].i = maze.matrix[i][j].i = i;
maze.recordMatrix[i][j].j = maze.matrix[i][j].j = j;
}
}
maze.X=maze.height/2;
maze.Y=maze.width/2;
if(maze.X%2==0)
maze.X+=1;
if(maze.Y%2==0)
maze.Y+=1;
point d(maze.X,maze.Y,0);
maze.allpath.push_back(d);
maze.dfs(maze.X,maze.Y);
for(int i=0; i<maze.allpath.size();i++)
{
point temp = maze.allpath[i];
int dir = maze.allpath[i].state;
switch (dir) {
case 1:
dir = 3;
break;
case 2:
dir =9;
break;
case 3:
dir = 0;
break;
case 4:
dir = 6;
break;
default:
break;
}
if(!isPaintPath)
{
mazeWidgets[temp.i][temp.j]->setStyleSheet(character[dir+i%3]);
}
else
{
mazeWidgets[temp.i][temp.j]->setStyleSheet("border-image: url(:/new/image/Load.png);");
mazeWidgets[maze.height-2][maze.width-2]->setStyleSheet("border-image: url(:/new/image/box.png);");
}
}
isPaintPath = !isPaintPath;
}
void MainWindow::shortpath()
{
qDebug()<<"zuiduanlujing";
maze.shortpath.clear();
maze.X=maze.height/2;
maze.Y=maze.width/2;
if(maze.X%2==0)
maze.X+=1;
if(maze.Y%2==0)
maze.Y+=1;
maze.initbfs(maze.X,maze.Y);
for(int i=0; i<maze.shortpath.size();i++)
{
point temp = maze.shortpath[i];
int dir = maze.shortpath[i].state;
switch (dir) {
case 1:
dir = 3;
break;
case 2:
dir =9;
break;
case 3:
dir = 0;
break;
case 4:
dir = 6;
break;
default:
break;
}
if(!isPaintPath)
{
mazeWidgets[temp.i][temp.j]->setStyleSheet(character[dir+i%3]);
}
else
{
mazeWidgets[temp.i][temp.j]->setStyleSheet("border-image: url(:/new/image/Load.png);");
mazeWidgets[maze.height-2][maze.width-2]->setStyleSheet("border-image: url(:/new/image/box.png);");
}
}
isPaintPath = !isPaintPath;
}
迷宫生成、dfs算法、自动寻路
#include "maze.h"
#include <QTime>
#include <QDebug>
#include <queue>
point::point(){}
point::point(int i,int j, int state):i(i),j(j),state(state){}
bool point::operator==(const point& t)
{
if(this->i == t.i&&j == t.j && t.state == state)
return true;
else return false;
}
Maze::Maze()
{
matrix = NULL;
recordMatrix = NULL;
height = width = 0;
X = Y = 1;
//初始化随机数种子
QTime time=QTime::currentTime();
qsrand(time.msec()+time.second()*1000);
//创建两个栈
MazeStack= new QStack<point>;
//初始化方向Move[4]
move[0].i = -1; move[0].j=0;
move[1].i = 0 ; move[1].j=1;
move[2].i = 1 ; move[2].j=0;
move[3].i = 0 ; move[3].j=-1;
}
void Maze::initMaze(int h,int w) //创建一个大小为h*w的空白迷宫和record矩阵
{
//让使用到的两个栈清空
MazeStack->clear();
autoPath.clear();
//获取迷宫边界大小和人物的初始位置
height = h;
width = w;
X=height/2;
Y=width/2;
if(X%2==0)
X+=1;
if(Y%2==0)
Y+=1;
//收回之前分配的两个矩阵的空间
if(matrix != NULL)
{
for(int i=0;i<height;i++)
delete [] matrix[i];
delete [] matrix;
for(int i=0; i<height;i++)
delete [] recordMatrix[i];
delete [] recordMatrix;
}
//重新创建matrix数组和recordMaze数组, 因为height和width发生了改变
matrix=new point*[height];
recordMatrix = new point*[height];
for(int i=0;i<height;i++)
{
matrix[i]=new point[width];
recordMatrix[i] = new point[width];
}
//初始化matrix(全0)和recordMatrix的值(全1)
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
matrix[i][j].state=0;//所有节点设置为0
recordMatrix[i][j].state= 1 ;
recordMatrix[i][j].i = matrix[i][j].i = i;
recordMatrix[i][j].j = matrix[i][j].j = j;
}
}
}
void Maze::refreshMaze(int h,int w ,int (&map)[31][31]) //创建一个大小为h*w的迷宫和record矩阵
{
//让使用到的两个栈清空
MazeStack->clear();
autoPath.clear();
height = h;
width = w;
X=height/2;
Y=width/2;
if(X%2==0)
X+=1;
if(Y%2==0)
Y+=1;
//收回之前分配的两个矩阵的空间
if(matrix != NULL)
{
for(int i=0;i<height;i++)
delete [] matrix[i];
delete [] matrix;
for(int i=0; i<height;i++)
delete [] recordMatrix[i];
delete [] recordMatrix;
}
//重新创建matrix数组和recordMaze数组, 因为height和width发生了改变
matrix=new point*[height];
recordMatrix = new point*[height];
for(int i=0;i<height;i++)
{
matrix[i]=new point[width];
recordMatrix[i] = new point[width];
}
//初始化matrix()和recordMatrix的值(全1)
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
matrix[i][j].state=map[i][j];//所有节点设置
recordMatrix[i][j].state= 1 ;
recordMatrix[i][j].i = matrix[i][j].i = i;
recordMatrix[i][j].j = matrix[i][j].j = j;
}
}
}
void Maze::setDirFalse(bool& up,bool& down,bool& right,bool& left)
{
up = down = right = left = false;
}
void Maze::createMaze()
{
int i=3,j=3;
matrix[i][j].state=1;//创建矩阵存储变量
point temp;//临时变量
temp.i=i;
temp.j=j;
temp.state=1;
bool up=false, down=false, right=false, left=false;
while(true)
{
temp.i=i;
temp.j=j;
int randNum=qrand()%4;
switch(randNum)
{
case 0://上
if(!up&&i>2&&matrix[i-2][j].state==0)
{
MazeStack->push(temp);
matrix[i-2][j].state=1;
matrix[i-1][j].state=1;
i=i-2;
setDirFalse(up, down, right, left);
}
else
up=true;
break;
case 1://下
if(!down&&i<height-3&&matrix[i+2][j].state==0)
{
MazeStack->push(temp);
matrix[i+2][j].state=1;
matrix[i+1][j].state=1;
i=i+2;
setDirFalse(up, down, right, left);
}
else
down=true;
break;
case 2://左
if(!left&&j>2&&matrix[i][j-2].state==0)
{
MazeStack->push(temp);
matrix[i][j-2].state=1;
matrix[i][j-1].state=1;
j=j-2;
setDirFalse(up, down, right, left);
}
else
left=true;
break;
case 3://右
if(!right&&j<width-3&&matrix[i][j+2].state==0)
{
MazeStack->push(temp);
matrix[i][j+2].state=1;
matrix[i][j+1].state=1;
j=j+2;
setDirFalse(up, down, right, left);
}
else
right=true;
break;
}
if(up&&down&&right&&left)//如果当前访问节点四个方向都没有可拆的节点,回溯
{
if(!MazeStack->empty())
{
i=MazeStack->top().i;
j=MazeStack->top().j;
MazeStack->pop();
setDirFalse(up, down, right, left);
}
else//如果栈为空的话就返回,此时迷宫矩阵已经创建完毕
{
return;
}
}
}
}
void Maze::autoFindPath()
{
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
recordMatrix[i][j].state= 1 ;//初始化recordMatrix的值(全1)
recordMatrix[i][j].i = matrix[i][j].i = i;
recordMatrix[i][j].j = matrix[i][j].j = j;
}
}
pathStack.clear();
recordMatrix[X][Y].state = 0; //出口位置标记为已经访问过,0为墙
point temp(X,Y,0);
pathStack.push_back(temp);
QList<point> stack;
int i , j , d ;
while(!pathStack.isEmpty())
{
point top = pathStack.back();
pathStack.pop_back();//将pathStack中的值出栈
if(!autoPath.isEmpty()&& !(top==autoPath.back()))
autoPath.push_back(top);//如果其非空且栈顶不相同则执行入栈操作
if(autoPath.isEmpty())
autoPath.push_back(top);
i = top.i;j=top.j;d=top.state;
//autoPath中存储的值即始末点的对应路径
while(d < 4)
{
temp.i = i+move[d].i;
temp.j = j+move[d].j;
if(temp.i == height-2 &&temp.j ==width-2)
{
pathStack.push_back(top);
pathStack.push_back(temp);
autoPath.push_back(temp);
for(int i=0 ; i<pathStack.size();i++)
{
qDebug()<<"("<<pathStack[i].i<<","<<pathStack[i].j<<")";
}
return;
}
//如果最开始的栈与后来记录路径的栈线路相同则其为原点到终点的路线
if(matrix[temp.i][temp.j].state == 1 && recordMatrix[temp.i][temp.j].state == 1)
{
recordMatrix[temp.i][temp.j].state = 0;
top.state=d;
pathStack.push_back(top);
temp.state=0;
pathStack.push_back(temp);
autoPath.push_back(temp);//自动寻路的路径对pathStack的值进行压栈操作
break;
}
d++;
}
if(d==4)
autoPath.push_back(pathStack.back());
}
}
void Maze::dfs(int x,int y)
{
int dir[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
if(x==height-2 && y==width-2)//成功---下面处理路径问题
{
while(!path.empty())//将path里面的点取出来,放在pa里面
{//path从栈顶-栈底的方向,路径是从终点-起点的顺序
point p1 = path.top();
path.pop();
pa.push(p1);
}
while(!pa.empty())
{//输出temp里面的路径,这样刚好是从起点到终点的顺序
point p1 = pa.top();
pa.pop();
path.push(p1);//将路径放回path里面,因为后面还要回溯!!!
point temp(p1.i,p1.j,0);
allpath.push_back(temp);
qDebug()<< "(" << p1.i << "," << p1.j << ")";
}
return;
}
if(x<=0 || x>=height-1 || y<=0 || y>=width-1)//越界
return;
//如果到了这一步,说明还没有成功,没有出界
for(int i=0;i<4;i++)//从4个方向探测
{
int nx = x + dir[i][0];
int ny = y + dir[i][1];//nx,ny:选择一个方向,前进一步之后,新的坐标
if(0<=nx && nx<height-1 && 0<=ny && ny<width-1 && matrix[nx][ny].state==1 && recordMatrix[nx][ny].state==1)
{//条件:nx,ny没有出界,maze[nx][ny]=0这个点不是障碍可以走,vis[nx][ny]=0说明(nx,ny)没有访问过,可以访问
recordMatrix[nx][ny].state=0;//设为访问过
point p(nx,ny,0);
path.push(p);//让当前点进栈
dfs(nx,ny);//进一步探测
recordMatrix[nx][ny].state=1;//回溯
path.pop();//由于是回溯,所以当前点属于退回去的点,需要出栈
}
}
}
void Maze::initbfs(int x, int y)
{
int dx[] = { -1,0,1,0 }, dy[] = { 0,1,0,-1 };
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
dis[i][j]=1000;
}
}
dis[X][Y]=0;
bfs(x,y);
int m=height-2,n=width-2;
for(int i=0;i<dis[height-2][width-2];i++)
{
point p(m,n,1);
shortpath.push_front(p);
for(int j=0;j<4;j++)
{
if(dis[m+dx[j]][n+dy[j]]==dis[m][n]-1)
{
m+=dx[j];
n+=dy[j];
break;
}
}
}
point p(x,y,1);
shortpath.push_back(p);
}
void Maze::bfs(int x, int y)
{
int dx[] = { -1,0,1,0 }, dy[] = { 0,1,0,-1 };
queue<point>que;
int sx=x, sy=y;
point temp(sx,sy,1);
que.push(temp);
while (!que.empty())
{
point p=que.front();
que.pop();
int xx = p.i; int yy = p.j;
if (xx == height-2 && yy == width-2) break;
for (int i = 0; i < 4; i++)
{
int nx = xx + dx[i]; int ny = yy + dy[i];
if (nx > 0 && nx < height-1 && ny > 0 && ny < width-1 && matrix[nx][ny].state== 1 && dis[nx][ny] == 1000)
{
point temp(nx,ny,1);
que.push(temp);
dis[nx][ny] = dis[xx][yy] + 1;
}
}
}
return ;
}
void Maze::changePath(int i, int j)
{
if(i>=height&&j>=width)return;
if(matrix!=NULL)
{
if(matrix[i][j].state==0)
matrix[i][j].state=1;
else matrix[i][j].state=0;
}
}
五、运行结果