Imported existing sources
This commit is contained in:
24
DbPathFinder.pro
Normal file
24
DbPathFinder.pro
Normal file
@ -0,0 +1,24 @@
|
||||
QT += core gui widgets
|
||||
|
||||
DBLIBS +=
|
||||
|
||||
TARGET = pathfinder
|
||||
|
||||
PROJECT_ROOT = ..
|
||||
|
||||
SOURCES += main.cpp \
|
||||
mywidget.cpp \
|
||||
mainwindow.cpp
|
||||
|
||||
HEADERS += \
|
||||
mywidget.h \
|
||||
mainwindow.h
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
|
||||
RESOURCES +=
|
||||
|
||||
TRANSLATIONS +=
|
||||
|
||||
include($${PROJECT_ROOT}/app.pri)
|
17
main.cpp
Normal file
17
main.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include <QApplication>
|
||||
#include <qglobal.h>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "mainwindow.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch());
|
||||
|
||||
MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
114
mainwindow.cpp
Normal file
114
mainwindow.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
#include <QTimer>
|
||||
#include <QMessageBox>
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->widget->reset(ui->spinBoxColumns->value(), ui->spinBoxRows->value());
|
||||
|
||||
m_timer = new QTimer(this);
|
||||
m_timer->setInterval(1000/30);
|
||||
|
||||
connect(ui->pushButtonReset, &QAbstractButton::pressed, this, &MainWindow::reset);
|
||||
connect(ui->pushButtonStep, &QAbstractButton::pressed, this, &MainWindow::step);
|
||||
connect(ui->pushButtonAnimate, &QAbstractButton::pressed, this, &MainWindow::animate);
|
||||
connect(ui->pushButtonSolve, &QAbstractButton::pressed, this, &MainWindow::solve);
|
||||
connect(m_timer, &QTimer::timeout, this, &MainWindow::timeout);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::reset()
|
||||
{
|
||||
ui->widget->reset(ui->spinBoxColumns->value(), ui->spinBoxRows->value());
|
||||
ui->widget->repaint();
|
||||
|
||||
ui->pushButtonStep->setEnabled(true);
|
||||
ui->pushButtonAnimate->setEnabled(true);
|
||||
ui->pushButtonSolve->setEnabled(true);
|
||||
}
|
||||
|
||||
MyWidget::Result MainWindow::step()
|
||||
{
|
||||
auto result = ui->widget->compute();
|
||||
ui->widget->repaint();
|
||||
switch(result)
|
||||
{
|
||||
case MyWidget::Result::Solved:
|
||||
solved();
|
||||
break;
|
||||
case MyWidget::Result::NotSolvable:
|
||||
notSolvable();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MainWindow::animate()
|
||||
{
|
||||
ui->spinBoxColumns->setEnabled(false);
|
||||
ui->spinBoxRows->setEnabled(false);
|
||||
ui->pushButtonReset->setEnabled(false);
|
||||
ui->pushButtonStep->setEnabled(false);
|
||||
ui->pushButtonAnimate->setEnabled(false);
|
||||
ui->pushButtonSolve->setEnabled(false);
|
||||
|
||||
m_timer->start();
|
||||
}
|
||||
|
||||
void MainWindow::solve()
|
||||
{
|
||||
MyWidget::Result result;
|
||||
while((result = ui->widget->compute()) == MyWidget::Result::NotFinished);
|
||||
ui->widget->repaint();
|
||||
switch (result) {
|
||||
case MyWidget::Result::Solved:
|
||||
solved();
|
||||
break;
|
||||
case MyWidget::Result::NotSolvable:
|
||||
notSolvable();
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::timeout()
|
||||
{
|
||||
auto result = step();
|
||||
switch(result)
|
||||
{
|
||||
case MyWidget::Result::Solved:
|
||||
case MyWidget::Result::NotSolvable:
|
||||
m_timer->stop();
|
||||
ui->spinBoxColumns->setEnabled(true);
|
||||
ui->spinBoxRows->setEnabled(true);
|
||||
ui->pushButtonReset->setEnabled(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::solved()
|
||||
{
|
||||
QMessageBox::information(this, tr("Found the optimal solution"), tr("The optimal solution was found and will be drawn in darker green."));
|
||||
ui->pushButtonStep->setEnabled(false);
|
||||
ui->pushButtonAnimate->setEnabled(false);
|
||||
ui->pushButtonSolve->setEnabled(false);
|
||||
}
|
||||
|
||||
void MainWindow::notSolvable()
|
||||
{
|
||||
QMessageBox::warning(this, tr("No solution found"), tr("There is no solution!"));
|
||||
ui->pushButtonStep->setEnabled(false);
|
||||
ui->pushButtonAnimate->setEnabled(false);
|
||||
ui->pushButtonSolve->setEnabled(false);
|
||||
}
|
35
mainwindow.h
Normal file
35
mainwindow.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
|
||||
#include "mywidget.h"
|
||||
|
||||
class QTimer;
|
||||
|
||||
namespace Ui { class MainWindow; }
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
private Q_SLOTS:
|
||||
void reset();
|
||||
MyWidget::Result step();
|
||||
void animate();
|
||||
void solve();
|
||||
void timeout();
|
||||
|
||||
private:
|
||||
void solved();
|
||||
void notSolvable();
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
QTimer *m_timer;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
116
mainwindow.ui
Normal file
116
mainwindow.ui
Normal file
@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxColumns">
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>50</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinBoxRows">
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>500</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>50</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonReset">
|
||||
<property name="text">
|
||||
<string>Reset</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonStep">
|
||||
<property name="text">
|
||||
<string>Step</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonAnimate">
|
||||
<property name="text">
|
||||
<string>Animate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButtonSolve">
|
||||
<property name="text">
|
||||
<string>Solve</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="MyWidget" name="widget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>MyWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>mywidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
197
mywidget.cpp
Normal file
197
mywidget.cpp
Normal file
@ -0,0 +1,197 @@
|
||||
#include "mywidget.h"
|
||||
|
||||
//#define ACUTE_ANGLE
|
||||
|
||||
#include <QTimer>
|
||||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
#include <QLine>
|
||||
#include <QLineF>
|
||||
#include <QMessageBox>
|
||||
|
||||
MyWidget::MyWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
m_start(Q_NULLPTR),
|
||||
m_end(Q_NULLPTR)
|
||||
{
|
||||
}
|
||||
|
||||
void MyWidget::reset(int columns, int rows)
|
||||
{
|
||||
m_nodes.resize(rows);
|
||||
for(int y = 0; y < rows; y++)
|
||||
m_nodes[y].resize(columns);
|
||||
|
||||
for(int y = 0; y < m_nodes.count(); y++)
|
||||
for(int x = 0; x < m_nodes.at(y).count(); x++)
|
||||
{
|
||||
Node &node = m_nodes[y][x];
|
||||
node.wall = !(x == 0 && y == 0) &&
|
||||
!(x == m_nodes.at(y).count() -1 && y == m_nodes.count() - 1) &&
|
||||
qrand() < RAND_MAX / 3;
|
||||
node.x = x;
|
||||
node.y = y;
|
||||
node.connections.clear();
|
||||
node.f = 0;
|
||||
node.g = 0;
|
||||
node.h = 0;
|
||||
node.previous = 0;
|
||||
}
|
||||
|
||||
for(const auto &vector : m_nodes)
|
||||
for(const auto &node : vector)
|
||||
{
|
||||
if(node.wall)
|
||||
continue;
|
||||
|
||||
auto x = node.x;
|
||||
auto y = node.y;
|
||||
|
||||
if(x > 0 && !m_nodes.at(y).at(x - 1).wall)
|
||||
m_nodes[y][x].connections.append(&m_nodes[y][x - 1]);
|
||||
if(x < m_nodes.at(y).count() - 1 && !m_nodes.at(y).at(x + 1).wall)
|
||||
m_nodes[y][x].connections.append(&m_nodes[y][x + 1]);
|
||||
if(y > 0 && !m_nodes.at(y - 1).at(x).wall)
|
||||
m_nodes[y][x].connections.append(&m_nodes[y - 1][x]);
|
||||
if(y < m_nodes.count() - 1 && !m_nodes.at(y + 1).at(x).wall)
|
||||
m_nodes[y][x].connections.append(&m_nodes[y + 1][x]);
|
||||
|
||||
#ifdef ACUTE_ANGLE
|
||||
if(x > 0 && y > 0 && !m_nodes.at(y - 1).at(x - 1).wall && !m_nodes.at(y).at(x - 1).wall && !m_nodes.at(y - 1).at(x).wall)
|
||||
m_nodes[y][x].connections.append(&m_nodes[y - 1][x - 1]);
|
||||
if(x < m_nodes.at(y).count() - 1 && y > 0 && !m_nodes.at(y - 1).at(x + 1).wall && !m_nodes.at(y).at(x + 1).wall && !m_nodes.at(y - 1).at(x).wall)
|
||||
m_nodes[y][x].connections.append(&m_nodes[y - 1][x + 1]);
|
||||
if(x > 0 && y < m_nodes.count() - 1 && !m_nodes.at(y + 1).at(x - 1).wall && !m_nodes.at(y).at(x - 1).wall && !m_nodes.at(y + 1).at(x).wall)
|
||||
m_nodes[y][x].connections.append(&m_nodes[y + 1][x - 1]);
|
||||
if(x < m_nodes.at(y).count() - 1 && y < m_nodes.count() - 1 && !m_nodes.at(y + 1).at(x + 1).wall && !m_nodes.at(y).at(x + 1).wall && !m_nodes.at(y + 1).at(x).wall)
|
||||
m_nodes[y][x].connections.append(&m_nodes[y + 1][x + 1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_start = &m_nodes.constFirst().constFirst();
|
||||
m_end = &m_nodes.constLast().constLast();
|
||||
|
||||
m_openSet = { m_start };
|
||||
m_closedSet.clear();
|
||||
m_result.clear();
|
||||
}
|
||||
|
||||
MyWidget::Result MyWidget::compute()
|
||||
{
|
||||
if(m_openSet.count())
|
||||
{
|
||||
int lowestFIndex = 0;
|
||||
for(int i = 1; i < m_openSet.count(); i++)
|
||||
if(m_openSet.at(i)->f < m_openSet.at(lowestFIndex)->f)
|
||||
lowestFIndex = i;
|
||||
|
||||
auto current = m_openSet.at(lowestFIndex);
|
||||
|
||||
{
|
||||
auto test = current;
|
||||
m_result.clear();
|
||||
do
|
||||
{
|
||||
m_result.append(test);
|
||||
test = test->previous;
|
||||
}
|
||||
while(test);
|
||||
}
|
||||
|
||||
if(current != m_end)
|
||||
{
|
||||
m_openSet.removeAll(current);
|
||||
m_closedSet.append(current);
|
||||
|
||||
for(const auto &neighbour : current->connections)
|
||||
{
|
||||
if(!m_closedSet.contains(neighbour))
|
||||
{
|
||||
auto increasedG = current->g +
|
||||
#ifdef ACUTE_ANGLE
|
||||
QLineF(current->x, current->y, neighbour->x, neighbour->y).length();
|
||||
#else
|
||||
qAbs(current->x - neighbour->x) + qAbs(current->y - neighbour->y);
|
||||
#endif
|
||||
bool newPath = false;
|
||||
|
||||
if(m_openSet.contains(neighbour))
|
||||
{
|
||||
if(increasedG < neighbour->g)
|
||||
newPath = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
newPath = true;
|
||||
m_openSet.append(neighbour);
|
||||
}
|
||||
|
||||
if(newPath)
|
||||
{
|
||||
m_nodes[neighbour->y][neighbour->x].g = increasedG;
|
||||
m_nodes[neighbour->y][neighbour->x].h =
|
||||
#ifdef ACUTE_ANGLE
|
||||
QLineF(neighbour->x, neighbour->y, m_end->x, m_end->y).length();
|
||||
#else
|
||||
qAbs(neighbour->x - m_end->x) + qAbs(neighbour->y - m_end->y);
|
||||
#endif
|
||||
m_nodes[neighbour->y][neighbour->x].f = neighbour->g + neighbour->h;
|
||||
m_nodes[neighbour->y][neighbour->x].previous = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return Result::Solved;
|
||||
}
|
||||
else
|
||||
return Result::NotSolvable;
|
||||
|
||||
return Result::NotFinished;
|
||||
}
|
||||
|
||||
void MyWidget::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
|
||||
if(!m_nodes.count())
|
||||
return;
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
painter.setPen(Qt::NoPen);
|
||||
|
||||
auto boxHeight = height() / m_nodes.count();
|
||||
|
||||
for(const auto &vector : m_nodes)
|
||||
{
|
||||
auto boxWidth = width() / vector.count();
|
||||
|
||||
for(const auto &node : vector)
|
||||
{
|
||||
if(m_result.contains(&node))
|
||||
painter.setBrush(Qt::darkGreen);
|
||||
else if(m_closedSet.contains(&node))
|
||||
painter.setBrush(Qt::red);
|
||||
else if(m_openSet.contains(&node))
|
||||
painter.setBrush(Qt::green);
|
||||
else if(node.wall)
|
||||
painter.setBrush(Qt::black);
|
||||
else
|
||||
continue;
|
||||
|
||||
painter.drawRect(node.x * boxWidth, node.y * boxHeight, boxWidth, boxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
painter.setPen(Qt::blue);
|
||||
|
||||
for(const auto &vector : m_nodes)
|
||||
{
|
||||
auto boxWidth = width() / vector.count();
|
||||
|
||||
for(const auto &node : vector)
|
||||
for(const auto &otherNode : node.connections)
|
||||
painter.drawLine((node.x * boxWidth) + (boxWidth / 2), (node.y * boxHeight) + (boxHeight / 2), (otherNode->x * boxWidth) + (boxWidth / 2), (otherNode->y * boxHeight) + (boxHeight / 2));
|
||||
}
|
||||
}
|
51
mywidget.h
Normal file
51
mywidget.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef MYWIDGET_H
|
||||
#define MYWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVector>
|
||||
|
||||
class QTimer;
|
||||
|
||||
class MyWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class Result
|
||||
{
|
||||
NotFinished,
|
||||
Solved,
|
||||
NotSolvable
|
||||
};
|
||||
|
||||
explicit MyWidget(QWidget *parent = Q_NULLPTR);
|
||||
void reset(int columns, int rows);
|
||||
Result compute();
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
bool wall;
|
||||
int x;
|
||||
int y;
|
||||
QVector<const Node *> connections;
|
||||
|
||||
//algorithm specific
|
||||
qreal f;
|
||||
qreal g;
|
||||
qreal h;
|
||||
const Node *previous;
|
||||
};
|
||||
|
||||
QVector<QVector<Node> > m_nodes;
|
||||
|
||||
QVector<const Node *> m_openSet;
|
||||
QVector<const Node *> m_closedSet;
|
||||
const Node *m_start;
|
||||
const Node *m_end;
|
||||
QVector<const Node *> m_result;
|
||||
};
|
||||
|
||||
#endif // MYWIDGET_H
|
Reference in New Issue
Block a user