2026-rff_mp/SolovevDS/docs/data/data_for_task2/task2.cpp

661 lines
20 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <fstream>
#include <iostream>
#include <string>
#include <stdexcept> /*для ошибок*/
#include <cmath>
#include <chrono> /*мерит время*/
class cell{
private:
int x, y;
bool isWall;
bool isExit;
bool isStart;
public:
cell() {x=0; y=0; isWall=false; isExit=false; isStart = false;}
cell(int x, int y, bool isWall, bool isExit, bool isStart)
{
this->x = x;
this->y = y;
this->isWall = isWall;
this->isExit = isExit;
this->isStart = isStart;
}
bool isPassable() {return !isWall;}
void setStart(bool value) {isStart = value;}
void setExit(bool value) {isExit = value;}
void setX(int x) {this->x = x;}
void setY(int y) {this->y = y;}
void setIsWall(bool isWall) {this->isWall = isWall;}
int getX() {return x;}
int getY() {return y;}
bool getIsWall() {return isWall;}
bool getIsExit() {return isExit;}
bool getIsStart() {return isStart;}
};
class maze{
private:
int width;
int height;
cell** matrix;
cell* start;
cell* exit;
public:
maze(int width, int height)
{
this->width = width;
this->height = height;
this->start = nullptr;
this->exit = nullptr;
matrix = new cell*[width];
for (int x = 0; x < width; ++x) {
matrix[x] = new cell[height];
for (int y = 0; y < height; ++y)
matrix[x][y] = cell(x, y, false, false, false);
}
}
maze(int width, int height, int startX, int startY, int exitX, int exitY)
{
this->width = width;
this->height = height;
this->start = nullptr;
this->exit = nullptr;
matrix = new cell*[width];
for (int x = 0; x < width; ++x) {
matrix[x] = new cell[height];
for (int y = 0; y < height; ++y) {
matrix[x][y] = cell(x, y, false, false, false);
}
}
matrix[startX][startY].setStart(true);
matrix[exitX][exitY].setExit(true);
start = &matrix[startX][startY];
exit = &matrix[exitX][exitY];
}
~maze()
{
for (int i = 0; i < width; ++i)
delete[] matrix[i];
delete[] matrix;
}
cell* getCell(int x, int y) {
if (x < 0 || x >= width || y < 0 || y >= height)
return nullptr;
return &matrix[x][y];
}
void setCell(int x, int y, cell newCell) {
if (x < 0 || x >= width || y < 0 || y >= height)
return;
matrix[x][y] = newCell;
if (matrix[x][y].getIsStart())
start = &matrix[x][y];
if (matrix[x][y].getIsExit())
exit = &matrix[x][y];
}
cell** getNeighbors(cell* current) { /*ДЕЛАТЬ delete[] neighbors; !!!!!!!!!!!!!!!*/
cell** neighbors = new cell*[5];
int count = 0;
int x = current->getX();
int y = current->getY();
cell* up = getCell(x, y - 1);
cell* down = getCell(x, y + 1);
cell* left = getCell(x - 1, y);
cell* right = getCell(x + 1, y);
if (up != nullptr && up->isPassable()) {
neighbors[count] = up;
count++;}
if (down != nullptr && down->isPassable()) {
neighbors[count] = down;
count++;}
if (left != nullptr && left->isPassable()) {
neighbors[count] = left;
count++;}
if (right != nullptr && right->isPassable()) {
neighbors[count] = right;
count++;}
neighbors[count] = nullptr;
return neighbors;
}
cell* getStart() {return start;}
cell* getExit() {return exit;}
int getWidth() {return width;}
int getHeight() {return height;}
};
class MazeBuilder {
public:
virtual maze* buildFromFile(const std::string& filename) = 0;
virtual ~MazeBuilder() {}
};
class TextFileMazeBuilder : public MazeBuilder {
public:
maze* buildFromFile(const std::string& filename) override
{
std::ifstream file(filename);
if (!file.is_open())
throw std::runtime_error("Ошибка: Не удалось открыть файл!");
std::string line;
int width = 0;
int height = 0;
// ДОБАВИЛ: первый проход по файлу.
// Здесь узнаём ширину и высоту лабиринта.
while (std::getline(file, line)) {
if (height == 0) {
width = line.length();
}
else {
if (line.length() != width)
throw std::runtime_error("Ошибка: строки лабиринта разной длины!");
}
height++;
}
if (width == 0 || height == 0) {
throw std::runtime_error("Ошибка: файл пустой!");
}
file.clear();
file.seekg(0);
maze* labirint = new maze(width, height);
bool hasStart = false;
bool hasExit = false;
int y = 0;
while (std::getline(file, line)) {
for (int x = 0; x < width; x++) {
char ch = line[x];
bool isWall = false;
bool isStart = false;
bool isExit = false;
switch(ch){
case '#':
isWall = true;
break;
case ' ':
isWall = false;
break;
case 'S':
isStart = true;
if (hasStart)
throw std::runtime_error("Ошибка: в лабиринте больше одного старта!");
hasStart = true;
break;
case 'E':
isExit = true;
if (hasExit)
throw std::runtime_error("Ошибка: в лабиринте больше одного выхода!");
hasExit = true;
break;
default:
throw std::runtime_error("Ошибка: неизвестный символ в файле!");
break;
}
cell current(x, y, isWall, isExit, isStart);
labirint->setCell(x, y, current);
}
y++;
}
file.close();
if (!hasStart)
throw std::runtime_error("Ошибка: в лабиринте нет старта!");
if (!hasExit)
throw std::runtime_error("Ошибка: в лабиринте нет выхода!");
return labirint;
}
// прочитать символы
// создать клетки
// вернуть готовый Maze
}
};
class PathFindingStrategy {
public:
virtual cell** findPath(maze* m, cell* start, cell* exit) = 0;
virtual int getVisitedCells() = 0; /*для посещенных клеток*/
virtual ~PathFindingStrategy() {}
};
class PathBuilder {
public:
static cell** buildPath(cell* start, cell* exit, cell*** parent) {
int length = 0;
cell* current = exit;
while (current != nullptr) {
length++;
if (current == start)
break;
current = parent[current->getX()][current->getY()];
}
cell** path = new cell*[length + 1];
current = exit;
for (int i = length - 1; i >= 0; i--) {
path[i] = current;
if (current == start)
break;
current = parent[current->getX()][current->getY()];
}
path[length] = nullptr;
return path;
}
};
class BFSStrategy : public PathFindingStrategy {
private:
int visitedCells;
public:
BFSStrategy() {visitedCells = 0;}
int getVisitedCells() override {return visitedCells;}
cell** findPath(maze* m, cell* start, cell* exit) override {
visitedCells = 0;
int width = m->getWidth();
int height = m->getHeight();
bool** visited = new bool*[width];
cell*** parent = new cell**[width];
for (int x = 0; x < width; x++) {
visited[x] = new bool[height];
parent[x] = new cell*[height];
for (int y = 0; y < height; y++) {
visited[x][y] = false;
parent[x][y] = nullptr;
}
}
cell** deque = new cell*[width * height];
int head = 0;
int tail = 0;
deque[tail] = start;
tail++;
visited[start->getX()][start->getY()] = true;
bool found = false;
while (head < tail) {
cell* current = deque[head];
head++;
visitedCells++;
if (current == exit) { //сравниваются указатели
found = true;
break;
}
cell** neighbors = m->getNeighbors(current);
for (int i = 0; neighbors[i] != nullptr; i++) {
cell* next = neighbors[i];
int nx = next->getX();
int ny = next->getY();
if (!visited[nx][ny]) {
visited[nx][ny] = true;
parent[nx][ny] = current;
deque[tail] = next;
tail++;
}
}
delete[] neighbors;
}
cell** path;
if (found) {
path = PathBuilder::buildPath(start, exit, parent);
}
else {
path = new cell*[1];
path[0] = nullptr;
}
delete[] deque;
for (int x = 0; x < width; x++) {
delete[] visited[x];
delete[] parent[x];
}
delete[] visited;
delete[] parent;
return path;
}
};
class DFSStrategy : public PathFindingStrategy {
private:
int visitedCells;
public:
DFSStrategy() {visitedCells = 0;}
int getVisitedCells() override {return visitedCells;}
cell** findPath(maze* m, cell* start, cell* exit) override {
visitedCells = 0;
int width = m->getWidth();
int height = m->getHeight();
bool** visited = new bool*[width];
cell*** parent = new cell**[width];
for (int x = 0; x < width; x++) {
visited[x] = new bool[height];
parent[x] = new cell*[height];
for (int y = 0; y < height; y++) {
visited[x][y] = false;
parent[x][y] = nullptr;
}
}
cell** stack = new cell*[width * height];
int top = 0;
stack[top] = start;
top++;
visited[start->getX()][start->getY()] = true;
bool found = false;
while (top > 0) {
top--;
cell* current = stack[top];
visitedCells++;
if (current == exit) { //сравниваются указатели
found = true;
break;
}
cell** neighbors = m->getNeighbors(current);
for (int i = 0; neighbors[i] != nullptr; i++) {
cell* next = neighbors[i];
int nx = next->getX();
int ny = next->getY();
if (!visited[nx][ny]) {
visited[nx][ny] = true;
parent[nx][ny] = current;
stack[top] = next;
top++;
}
}
delete[] neighbors;
}
cell** path;
if (found) {
path = PathBuilder::buildPath(start, exit, parent);
}
else {
path = new cell*[1];
path[0] = nullptr;
}
delete[] stack;
for (int x = 0; x < width; x++) {
delete[] visited[x];
delete[] parent[x];
}
delete[] visited;
delete[] parent;
return path;
}
};
class AStarStrategy : public PathFindingStrategy {
private:
int heuristic(cell* current, cell* exit) {return std::abs(current->getX() - exit->getX()) + std::abs(current->getY() - exit->getY());}
int visitedCells;
public:
AStarStrategy() {visitedCells = 0;}
int getVisitedCells() override {return visitedCells;}
cell** findPath(maze* m, cell* start, cell* exit) override {
visitedCells = 0;
int width = m->getWidth();
int height = m->getHeight();
bool** closed = new bool*[width]; /*клетка [x][y] посещена (да/нет)*/
bool** inOpen = new bool*[width]; /*клетка [x][y] имеет потенциал к посещению (да/нет)*/
int** gScore = new int*[width]; /* до клетка [x][y] от старта gSchore[x][y] шагов*/
int** fScore = new int*[width]; /*f = h + g, где h - эвристика клетки[x][y]*/
cell*** parent = new cell**[width];
for (int x = 0; x < width; x++) {
closed[x] = new bool[height];
inOpen[x] = new bool[height];
gScore[x] = new int[height];
fScore[x] = new int[height];
parent[x] = new cell*[height];
for (int y = 0; y < height; y++) {
closed[x][y] = false;
inOpen[x][y] = false;
gScore[x][y] = width * height + 100000; /*тупо большое число чтоб было больше чем клеток в лаберинте*/
fScore[x][y] = width * height + 100000;
parent[x][y] = nullptr;
}
}
cell** open = new cell*[width * height]; /*клетки с потенциалом на посещение*/
int openCount = 0; /*это количество потенц клеток, а также индекс следующего незанятого места*/
int sx = start->getX();
int sy = start->getY();
gScore[sx][sy] = 0;
fScore[sx][sy] = heuristic(start, exit);
open[openCount] = start;
openCount++;
inOpen[sx][sy] = true;
bool found = false;
while (openCount > 0) {
int bestIndex = 0;
for (int i = 1; i < openCount; i++) {
int ix = open[i]->getX();
int iy = open[i]->getY();
int bx = open[bestIndex]->getX();
int by = open[bestIndex]->getY();
if (fScore[ix][iy] < fScore[bx][by]) {
bestIndex = i; /*(fSchore наименьший в [bestIndex])*/
}
}
cell* current = open[bestIndex];
if (current == exit) {
found = true;
visitedCells++; /*чтоб выход засчитывался*/
break;
}
int cx = current->getX();
int cy = current->getY();
open[bestIndex] = open[openCount - 1];
openCount--;
inOpen[cx][cy] = false;
closed[cx][cy] = true;
visitedCells++;
cell** neighbors = m->getNeighbors(current);
for (int i = 0; neighbors[i] != nullptr; i++) {
cell* next = neighbors[i];
int nx = next->getX();
int ny = next->getY();
if (closed[nx][ny]) {
continue;
}
int tentativeG = gScore[cx][cy] + 1;
if (tentativeG < gScore[nx][ny]) {
parent[nx][ny] = current;
gScore[nx][ny] = tentativeG;
fScore[nx][ny] = gScore[nx][ny] + heuristic(next, exit);
if (!inOpen[nx][ny]) {
open[openCount] = next;
openCount++;
inOpen[nx][ny] = true;
}
}
}
delete[] neighbors;
}
cell** path;
if (found) {
path = PathBuilder::buildPath(start, exit, parent);
}
else {
path = new cell*[1];
path[0] = nullptr;
}
delete[] open;
for (int x = 0; x < width; x++) {
delete[] closed[x];
delete[] inOpen[x];
delete[] gScore[x];
delete[] fScore[x];
delete[] parent[x];
}
delete[] closed;
delete[] inOpen;
delete[] gScore;
delete[] fScore;
delete[] parent;
return path;
}
};
class SearchStats {
public:
double timeMs;
int visitedCells;
int pathLength;
SearchStats(double timeMs, int visitedCells, int pathLength) {
this->timeMs = timeMs;
this->visitedCells = visitedCells;
this->pathLength = pathLength;
}
};
class MazeSolver{
private:
maze* labirint;
PathFindingStrategy* strategy;
public:
MazeSolver(maze* labirint) {this->labirint = labirint;}
void setStrategy(PathFindingStrategy* strategy){
this->strategy = strategy;
}
SearchStats solve(){
auto start = std::chrono::high_resolution_clock::now();
cell** path = strategy->findPath(labirint,labirint->getStart(),labirint->getExit());
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> duration = end - start;
int pathLength = 0;
if (path[0] != nullptr)
while (path[pathLength] != nullptr) {pathLength++;}
int visitedCells = 0;
visitedCells = strategy->getVisitedCells();
delete[] path;
SearchStats stats(duration.count(), visitedCells, pathLength);
return stats;
}
};
int main(){
}