mazewidget.h
#ifndef MAZEWIDGET_H
#define MAZEWIDGET_H
#include <QMessageBox> //Qt信息窗口头文件
#include <QPainter> //Qt绘图头文件
#include <QDebug> //QtDebug头文件
#include <QKeyEvent> //Qt按键事件头文件
#include <QTimer> //Qt计时器头文件
#include <QInputDialog> //Qt输入对话框头文件
#include<widget.h> //迷宫类头文件
#include <QWidget>
#include<QList>
#include<vector>
#include<QMediaPlayer>
#include<QMediaPlaylist>
#include<QtConcurrent/QtConcurrent>
namespace Ui {
class mazewidget;
}
class mazewidget : public QWidget
{
Q_OBJECT
protected:
void paintEvent(QPaintEvent*); //绘图事件
void keyPressEvent(QKeyEvent*); //按键按下事件
private slots:
void on_start_btn_clicked(); //|
void on_stop_btn_clicked(); //|
void on_end_btn_clicked(); //|
void on_rule_btn_clicked(); //|各按钮点击槽函数
void on_setting_btn_clicked(); //|
void on_battle_btn_clicked(); //|
void on_about_btn_clicked(); //|
void on_roadfind_btn_clicked();
void DFS(int x, int y, bool& find);
void time_update();
void setStartEndPos(QPoint start, QPoint end);//时间更新槽函数
QList<QPoint> aStarPath(QPoint start, QPoint end);
void A_star();
public:
explicit mazewidget(QWidget *parent = nullptr);
~mazewidget();
QPushButton *start_btn; //开始游戏
QPushButton * stop_ptn;// 暂停
QPushButton *end_btn ;//终止游戏
QPushButton * setting_btn ;
QPushButton * roadfind_btn ;//设置QLabel:
QLabel* plaque ;//迷宫游戏
QLabel* plaque_time ;//时间:
QMediaPlayer *player;
QMediaPlaylist* playerList;
QLabel* plaque_grade ;//分数:
QList<int>open_list;
QList<int>close_list;
QPoint startPos;
QPoint endPos;
int row = 2 * level + 1;
int column = 2 * level + 1;
private:
Ui::mazewidget *ui;
//ui对象指针
Widget* map; //迷宫对象指针
private: struct point {
int x = -1;
int y = -1;
int cost = 0;
int fvalue = 0;
point* parent = nullptr;
bool operator<(const point& other)
const { return fvalue > other.fvalue; }
};
QVector<QPoint> path; // 用于存储搜索路径上的点的坐标
QVector<point*>open_set;
QVector<point*>close_set;
void A_star(int, int, int, int);
QVector<point*> get_surround_points(int, int, point*);
bool is_point_valid(int, int, point*); point* get_point_with_min_fvalue();
bool painting_switch; //绘图开关
bool timing_switch; //计时开关
bool keybord_switch; //键盘响应开关
bool stop_switch; //暂停按钮状态
bool find_switch;
int level;
int grade; //分数
int time; //时间
int p_x, p_y;
int end_x, end_y;
int next_x;
int next_y;
int current_x;
int current_y;
QTimer* timer; //计时器对象指针
};
#endif // MAZEWIDGET_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
private:
int level; //迷宫阶数
int** mapArray; //地图存储空间
int start_x, start_y; //起点
//生成基础地图(单元格)
void base();
//使点的周围设为待定(2)
void arround(int i, int j);
//设定迷宫开始延伸的起点
void start() ;
//循环停止判定(是否存在未判定的区域)
bool judge();
//操作(如果相邻空单元(1)则打通(变为4),如果不相邻空单元则为墙壁(0))
void op(int i, int j);
//随机选择一个待定墙壁判断并操作
void random2();
void DFS();
public:
//构造函数申请内存空间
Widget(int in_level);
~Widget();
//获取地图
int getlevel() ;
int** getmap();
int getside();
//生成地图
void makemap();
int p_x, p_y; //当前位置
int end_x, end_y;
//重置地图
void rebuildmap();
int* operator[](int index);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
mazewidget.cpp
#include "mazewidget.h"
#include "ui_mazewidget.h"
#include <QDateTime>
#include <queue>
#include <iostream>
mazewidget::mazewidget(QWidget *parent) :
QWidget(parent),ui(new Ui::mazewidget),map(new Widget(20)),painting_switch(false), timing_switch(false)
, keybord_switch(false), grade(0), time(0)
{
ui->setupUi(this);
ui->progressBar->setVisible(false); //初始隐藏进度条
ui->end_btn->setEnabled(false); //设置终止按钮禁用
ui->stop_btn->setEnabled(false); //设置暂停按钮禁用
ui->grade_value->setText(" "); //设置分数值显示为空\ ui->time_value->setText(" "); //设置时间值显示为空
map->makemap();
playerList = new QMediaPlaylist();
player = new QMediaPlayer();
playerList->addMedia(QMediaContent(QUrl("qrc:/C:/Users/MALING/Music/hmbb.mp3")));
playerList->setPlaybackMode(QMediaPlaylist::Loop);
player->setPlaylist(playerList);
player->setVolume(100);
player->play();
this->setWindowTitle("海绵宝宝回家");
//生成地图
timer = new QTimer(this); //初始化计时器
connect(timer, &QTimer::timeout, this, &mazewidget::time_update);
if (map == nullptr) {
map = new Widget(20);
}
}
mazewidget::~mazewidget()
{
delete ui;
delete map; //1.
delete timer; //2.
}
void mazewidget::paintEvent(QPaintEvent*) {
if(!painting_switch) return;
QPainter painter(this); //画笔对象
//绘图逻辑:
int perblock = (std::min(ui->frame->width(), ui->frame->height()) - 20) / (map->getside());
int start_x = ui->frame->x() + (ui->frame->width() - (ui->frame->x() + (map->getside()) * perblock)) / 2;
int strat_y = ui->frame->y() + (ui->frame->height() - (ui->frame->y() + (map->getside()) * perblock)) / 2;
for(int i = 0; i < map->getlevel() * 2 + 1; i++) {
for(int j = 0; j < map->getlevel() * 2 + 1; j++) {
if(i == map->p_x && j == map->p_y) {
QPixmap road;
road.load("C:\\Users\\MALING\\Pictures\\Saved Pictures\\starthmbb.png");
painter.drawPixmap(start_x + i * perblock, strat_y + j * perblock, perblock, perblock,road);
} else if(map->getmap()[i][j] == 7) {
QPixmap foot;
foot.load("C:\\Users\\MALING\\Pictures\\Saved Pictures\\jiaoyin.jpg");
painter.drawPixmap(start_x + i * perblock, strat_y + j * perblock, perblock, perblock,foot);
} else if(map->getmap()[i][j] == 3 || map->getmap()[i][j] == 4) {
painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::white));
} else if(map->getmap()[i][j] == 5) {
QPixmap start;
start.load("C:\\Users\\MALING\\Pictures\\Saved Pictures\\starthmbb.png");
painter.drawPixmap(start_x + i * perblock, strat_y + j * perblock, perblock, perblock,start);
} else if(map->getmap()[i][j] == 6) {
QPixmap end;
end.load("C:\\Users\\MALING\\Pictures\\Saved Pictures\\endhmbb.png");
painter.drawPixmap(start_x + i * perblock, strat_y + j * perblock, perblock, perblock,end);
}
else if(map->getmap()[i][j]==0 || map->getmap()[i][j] == -1) {
QPixmap wall;
wall.load("C:\\Users\\MALING\\Pictures\\Saved Pictures\\paopao.png");
painter.drawPixmap(start_x + i * perblock, strat_y + j * perblock, perblock, perblock,wall);
}else{
painter.fillRect(start_x + i * perblock, strat_y + j * perblock, perblock, perblock, QBrush(Qt::yellow));
}
}
}
}
void mazewidget::keyPressEvent(QKeyEvent* event) {
if(!keybord_switch) return;
// 角色坐标
int x = map->p_x;
int y = map->p_y;
//键盘移动逻辑:
if(event->key() == Qt::Key_I || event->key() == Qt::Key_W) {
if((*map)[x][y - 1] == 3 || (*map)[x][y - 1] == 4 || (*map)[x][y - 1] == 5 || (*map)[x][y - 1] == 6 || (*map)[x][y - 1] == 7) {
map->p_y--;
}
} else if(event->key() == Qt::Key_K || event->key() == Qt::Key_S) {
if((*map)[x][y + 1] == 3 || (*map)[x][y + 1] == 4 || (*map)[x][y + 1] == 5 || (*map)[x][y + 1] == 6 || (*map)[x][y + 1] == 7) {
map->p_y++;
}
} else if(event->key() == Qt::Key_J || event->key() == Qt::Key_A) {
if((*map)[x - 1][y] == 3 || (*map)[x - 1][y] == 4 || (*map)[x - 1][y] == 5 || (*map)[x - 1][y] == 6 || (*map)[x - 1][y] == 7) {
map->p_x--;
}
} else if(event->key() == Qt::Key_L || event->key() == Qt::Key_D) {
if((*map)[x + 1][y] == 3 || (*map)[x + 1][y] == 4 || (*map)[x + 1][y] == 5 || (*map)[x + 1][y] == 6 || (*map)[x + 1][y] == 7) {
map->p_x++;
}
}
//经过路径
if((*map)[map->p_x][map->p_y] != 5 && (*map)[map->p_x][map->p_y] != 6)(*map)[map->p_x][map->p_y] = 7;
repaint();
//到达终点
if((*map)[map->p_x][map->p_y] == 6) {
map->makemap();
repaint();
grade += (map->getlevel(), 2);
ui->grade_value->setText(QString::number(grade));
}
}
void mazewidget::time_update() {
if(time != 0) {
//计时中
time--;
ui->time_value->setText(QString::number(time));
ui->progressBar->setValue(time / 2);
} else {
timer->stop(); //停止计时器
ui->progressBar->setVisible(false); //隐藏进度条
keybord_switch = false; //设置键盘响应、
painting_switch = false; //绘图响应、
timing_switch = false; //计时响应为关闭状态
repaint(); //清除画布
ui->start_btn->setEnabled(true); //|
ui->time_value->setText(" "); //|
ui->grade_value->setText(" "); //|
ui->stop_btn->setEnabled(false); //|设置各按钮与标签状态
ui->end_btn->setEnabled(false); //|
ui->setting_btn->setEnabled(true); //|
//提示
QMessageBox outgrade(QMessageBox::NoIcon, "恭喜", "您得分:" + QString::number(grade), QMessageBox::Ok);
outgrade.exec();
//分数重置
grade = 0;
}
}
void mazewidget::on_start_btn_clicked() {
painting_switch = true;
timing_switch = true;
keybord_switch = true;
time = 200;
timer->start(1000);
ui->progressBar->setVisible(true);
ui->progressBar->setValue(100);
repaint();
ui->time_value->setText(QString::number(time));
ui->grade_value->setText(QString::number(grade));
ui->start_btn->setEnabled(false);
ui->stop_btn->setEnabled(true);
ui->end_btn->setEnabled(true);
ui->setting_btn->setEnabled(false);
}
void mazewidget::on_stop_btn_clicked() {
if(stop_switch) {
timing_switch = false;
keybord_switch = false;
timer->stop();
ui->stop_btn->setText("继续");
stop_switch = false;
} else {
timing_switch = true;
keybord_switch = true;
timer->start();
ui->stop_btn->setText("暂停");
stop_switch = true;
}
}
void mazewidget::on_end_btn_clicked() {
timing_switch = false;
painting_switch = false;
keybord_switch = false;
stop_switch = false;
timer->stop();
time = 0;
grade = 0;
ui->progressBar->setVisible(false);
ui->grade_value->setText(" ");
ui->time_value->setText(" ");
ui->stop_btn->setText("暂停");
ui->stop_btn->setEnabled(false);
ui->end_btn->setEnabled(false);
ui->start_btn->setEnabled(true);
ui->setting_btn->setEnabled(true);
map->rebuildmap();
repaint();
}
void mazewidget::on_setting_btn_clicked() {
QStringList difficultys;
difficultys << tr("启蒙难度(5阶迷宫)") << tr("简单难度(10阶迷宫)") << tr("普通难度(20阶迷宫)") << tr("困难难度(40阶迷宫)");
QString difficulty = QInputDialog::getItem(this, tr("选择难度"),
tr("请选择一个条目"), difficultys, 0, false);
if(difficulty == tr("启蒙难度(5阶迷宫)")) {
delete map;
map = new Widget(5);
map->makemap();
} else if(difficulty == tr("简单难度(10阶迷宫)")) {
delete map;
map = new Widget(10);
map->makemap();
} else if(difficulty == tr("普通难度(20阶迷宫)")) {
delete map;
map = new Widget(20);
map->makemap();
} else if(difficulty == tr("困难难度(40阶迷宫)")) {
delete map;
map = new Widget(40);
map->makemap();
}
find_switch = false;
}
bool mazewidget::is_point_valid(int x, int y, point* parent) {
// 如果是边界, 直接返回false
if (x < 0 || x >= map->getlevel() * 2 + 1 || y < 0 || y >= map->getlevel() * 2 + 1)
return false;
// 类型1 2 3 4 7都为不可达
if ((*map)[x][y] == 1 || (*map)[x][y] == 2 || (*map)[x][y] == 3 || (*map)[x][y] == 4 || (*map)[x][y] == 7)
return false;
// 如果在开放列表或者关闭列表中, 表示探索过
for (point *p : open_set) {
if (p->x == x && p->y == y)
return false;
}
for (point *p : close_set) {
if (p->x == x && p->y == y)
return false;
}
if (parent && parent->x == x && parent->y == y)
return false;
return true;
}
QVector<mazewidget::point*> mazewidget::get_surround_points(int x, int y, point* parent) {
// 相邻节点列表
QVector<point*> surround_points;
// 如果当前节点上面不是墙壁, 添加到相邻节点列表
if (is_point_valid(x - 1, y, parent)) {
point* p = new point;
p->x = x - 1; p->y = y;
p->cost = parent->cost + 1;
p->fvalue = p->cost + abs(x - 1 - map->p_x) + abs(y - map->p_y);
p->parent = parent; surround_points.push_back(p);
}
// 下面
if (is_point_valid(x + 1, y, parent)) {
point* p = new point;
p->x = x + 1;
p->y = y;
p->cost = parent->cost + 1;
p->fvalue = p->cost + abs(x + 1 - map->p_x) + abs(y - map->p_y);
p->parent = parent; surround_points.push_back(p);
}
// 左边
if (is_point_valid(x, y - 1, parent)) {
point* p = new point;
p->x = x; p->y = y - 1;
p->cost = parent->cost + 1;
p->fvalue = p->cost + abs(x - map->p_x) + abs(y - 1 - map->p_y);
p->parent = parent; surround_points.push_back(p);
}
// 右边
if (is_point_valid(x, y + 1, parent)) {
point* p = new point; p->x = x;
p->y = y + 1; p->cost = parent->cost + 1;
p->fvalue = p->cost + abs(x - map->p_x) + abs(y + 1 - map->p_y);
p->parent = parent;
surround_points.push_back(p);
}
return surround_points;
}
mazewidget::point* mazewidget::get_point_with_min_fvalue() {
if (open_set.empty()) {
qDebug() << "开放列表为空,没有节点可用";
return nullptr; // 开放列表为空,没有节点可用
}
int index = 0;
// 拿开放列表第一个的曼哈顿距离
int min_fvalue = open_set[0]->fvalue;
// 遍历整个开放列表, 判断是否有更小的距离, 如果有就更新最小距离
for (int i = 1; i < open_set.size(); i++) {
if (open_set[i]->fvalue < min_fvalue) {
index = i;
min_fvalue = open_set[i]->fvalue;
}
}
// 返回的节点
point* p = open_set[index];
// 从开放列表移除距离终点最近的节点
open_set.erase(open_set.begin() + index);
return p;
}
void mazewidget::A_star(int sx, int sy, int ex, int ey) {
qDebug() << "111";
open_set.clear(); // 开放列表(待探索节点)
close_set.clear(); // 闭合列表(探索过的节点)
path.clear();
// 每一个节点都有x,y坐标和相对于终点的曼哈顿距离
point* start = new point;
start->x = sx; start->y = sy;
start->cost = 0;
// 曼哈顿距离(这里是起点到终点的曼哈顿距离)
start->fvalue = abs(sx - ex) + abs(sy - ey);
// 将起点加入到开放列表
open_set.push_back(start);
// 终点
point* end = new point;
end->x = ex; end->y = ey;
int t = 1;
// 判断开放列表是否被探索完成
while (!open_set.empty()) {
// 获取距离最近的节点作为下一个节点(移动到下一个节点就变成了当前节点)
point* current = get_point_with_min_fvalue();
// qDebug() << "current:" << current;
t++;
qDebug() << "执行次数: " << t;
// 向关闭列表添加当前节点, 因为已经被探索
close_set.push_back(current);
// 判断当前位置是否等于终点位置
if (current->x == end->x && current->y == end->y) {
// 从终点回到起点, 以获得路径
while (current) {
qDebug() << "探索到终点";
// 将当前节点添加到路径的末尾
path.prepend(QPoint(current->x, current->y));
// ???
// (*map)[current->x][current->y] = 7;
// 得到当前节点的上一级节点
point* prev = current;
// 将当前节点设为上一级节点
current = current->parent;
// 删除上一级节点
delete prev;
}
return;
}
// 获取当前节点的相邻节点
// 需要在函数里面实现去除不可达节点(墙壁)
QVector<point*> surround_points = get_surround_points(current->x, current->y, current);
// 将相邻节点添加到开放列表(待探索节点)
for (point* p : surround_points) {
open_set.push_back(p);
delete p; // 释放内存
}
delete current;
}
}
widget.cpp
#include "widget.h"
void Widget::base() {
for (int i = 0; i < level * 2 + 1; i++) {
for (int j = 0; j < level * 2 + 1; j++) {
if (i == 0 || j == 0 || i == level * 2 + 1 - 1 ||
j == level * 2 + 1 - 1) {
mapArray[i][j] = -1;
} else if (i % 2 != 0 && j % 2 != 0) {
mapArray[i][j] = 1;
} else {
mapArray[i][j] = 0;
}
}
}
}
//使点的周围设为待定(2)
void Widget:: arround(int i, int j) {
if (mapArray[i - 1][j] == 0) {
mapArray[i - 1][j] = 2;
}
if (mapArray[i + 1][j] == 0) {
mapArray[i + 1][j] = 2;
}
if (mapArray[i][j - 1] == 0) {
mapArray[i][j - 1] = 2;
}
if (mapArray[i][j + 1] == 0) {
mapArray[i][j + 1] = 2;
}
}
//设定迷宫开始延伸的起点
void Widget:: start() {
mapArray[start_x][start_y] = 5;
arround(start_x, start_y);
}
//循环停止判定(是否存在未判定的区域)
bool Widget:: judge() {
bool flag = 0;
for (int i = 0; i < level * 2 + 1; i++) {
for (int j = 0; j < level * 2 + 1; j++) {
if (mapArray[i][j] == 2) {
flag = 1;
return flag;
}
}
}
return flag;
}
//操作(如果相邻空单元(1)则打通(变为4),如果不相邻空单元则为墙壁(0))
void Widget:: op(int i, int j) {
if ((mapArray[i - 1][j] == 3 || mapArray[i - 1][j] == 5) && mapArray[i + 1][j] == 1) {
mapArray[i][j] = 4;
mapArray[i + 1][j] = 3;
arround(i + 1, j);
start_x = i + 1;
start_y = j;
} else if ((mapArray[i][j - 1] == 3 || mapArray[i][j - 1] == 5) &&
mapArray[i][j + 1] == 1) {
mapArray[i][j] = 4;
mapArray[i][j + 1] = 3;
arround(i, j + 1);
start_x = i;
start_y = j + 1;
} else if ((mapArray[i + 1][j] == 3 || mapArray[i + 1][j] == 5) &&
mapArray[i - 1][j] == 1) {
mapArray[i][j] = 4;
mapArray[i - 1][j] = 3;
arround(i - 1, j);
start_x = i - 1;
start_y = j;
} else if ((mapArray[i][j + 1] == 3 || mapArray[i][j + 1] == 5) &&
mapArray[i][j - 1] == 1) {
mapArray[i][j] = 4;
mapArray[i][j - 1] = 3;
arround(i, j - 1);
start_x = i;
start_y = j - 1;
} else {
mapArray[i][j] = 0;
}
}
void Widget:: random2() {
int t = 0;
for (int i = 0; i < level * 2 + 1; i++) {
for (int j = 0; j < level * 2 + 1; j++) {
if (mapArray[i][j] == 2) {
t++;
}
}
}
int k = rand() % t + 1;
t = 0;
for (int i = 0; i < level * 2 + 1; i++) {
for (int j = 0; j < level * 2 + 1; j++) {
if (mapArray[i][j] == 2) {
t++;
if (t == k) {
op(i, j);
goto loopout;
}
}
}
}
loopout:
if (!judge()) {
mapArray[start_x][start_y] = 6;
}
}
//构造函数申请内存空间
Widget:: Widget(int in_level) : level(in_level) {
mapArray = new int* [level * 2 + 1];
for (int i = 0; i < level * 2 + 1; i++) {
mapArray[i] = new int[level * 2 + 1];
}
start_x = 1, start_y = 1; //起点设置为(1,1)
}
Widget::~Widget() {
for (int i = 0; i < level * 2 + 1; i++) {
delete [] mapArray[i];
}
delete [] mapArray;
}
//获取地图
int Widget:: getlevel() {
return Widget::level;
}
int** Widget::getmap() {
return mapArray;
}
int Widget::getside() {
return level * 2 + 1;
}
//生成地图
void Widget:: makemap() {
p_x = start_x;
p_y = start_y;
base();
start();
int a = 0;
while (judge()) {
a++;
random2();
// if (a % 30 == 0) {
// printarr(map, level);
// system("PAUSE");
// }
}
}
int* Widget::operator[](int index) {
return mapArray[index];
}
//重置地图
void Widget::rebuildmap(){
start_x=1;
start_y=1;
makemap();
}