diff --git a/SolovevDS/docs/data/data_for_task2/diagrams.py b/SolovevDS/docs/data/data_for_task2/diagrams.py new file mode 100644 index 0000000..7ff2bde --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/diagrams.py @@ -0,0 +1,76 @@ +import pandas as pd +import matplotlib.pyplot as plt +import numpy as np + +df = pd.read_csv("results.csv") + +maze_order = ["small_10", "medium_50", "large_100", "empty", "no_path"] +strategy_order = ["BFS", "DFS", "AStar"] + +maze_labels = { + "small_10": "10×10", + "medium_50": "50×50", + "large_100": "100×100", + "empty": "Пустой", + "no_path": "Без выхода" +} + +df["maze"] = pd.Categorical(df["maze"], categories=maze_order, ordered=True) +df["strategy"] = pd.Categorical(df["strategy"], categories=strategy_order, ordered=True) +df = df.sort_values(["maze", "strategy"]) + + +def plot_grouped_bar(df, value_col, ylabel, title, filename): + mazes = maze_order + strategies = strategy_order + + x = np.arange(len(mazes)) + width = 0.25 + + plt.figure(figsize=(11, 6)) + + for i, strategy in enumerate(strategies): + values = [] + + for maze in mazes: + row = df[(df["maze"] == maze) & (df["strategy"] == strategy)] + values.append(row[value_col].values[0]) + + plt.bar(x + (i - 1) * width, values, width, label=strategy) + + plt.xlabel("Лабиринт") + plt.ylabel(ylabel) + plt.title(title) + + plt.xticks(x, [maze_labels[m] for m in mazes], rotation=20) + plt.legend(title="Стратегия") + plt.grid(axis="y", alpha=0.3) + + plt.tight_layout() + plt.savefig(filename, format="svg") + plt.show() + + +plot_grouped_bar( + df, + value_col="time_ms", + ylabel="Время, мс", + title="Сравнение времени выполнения BFS, DFS и A*", + filename="time_comparison.svg" +) + +plot_grouped_bar( + df, + value_col="cells_visited", + ylabel="Количество посещённых клеток", + title="Сравнение количества посещённых клеток", + filename="visited_cells_comparison.svg" +) + +plot_grouped_bar( + df, + value_col="way_len", + ylabel="Длина пути, клеток", + title="Сравнение длины найденного пути", + filename="path_length_comparison.svg" +) \ No newline at end of file diff --git a/SolovevDS/docs/data/data_for_task2/maze10.txt b/SolovevDS/docs/data/data_for_task2/maze10.txt new file mode 100644 index 0000000..8ffe41a --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/maze10.txt @@ -0,0 +1,10 @@ +########## +#S ### +###### ### +# ### +# #### ### +# # ### +# # ###### +# # # +# ######E# +########## diff --git a/SolovevDS/docs/data/data_for_task2/maze100.txt b/SolovevDS/docs/data/data_for_task2/maze100.txt new file mode 100644 index 0000000..2bd6ce0 --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/maze100.txt @@ -0,0 +1,100 @@ +#################################################################################################### +#S # # # # # # # # # ## +## ############### # ## ## ###### # ### ### ### ########### ### # ## # ####### # ### # # # ## +# # # # # # # # # # # # # # # # # # # ## +# ### # ##### # #### ##### ######### # ##### # ##### # ### ##### ### # # ### # ####### ##### # ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ### ##### ##### # # ### # ##### # ##### # ####### ######### ##### # # # # ### ### # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # ### # # # ### # ##### ### ### # # ##### # # # ### # # ##### ### ##### ### ########### ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ### ########### # # ### # ### # # ### # ### # ##### # ####### #### ## # ### ######### ####### ## +# # # # # # # # # # # # # # # # # # ## +# ####### ### # # # ### # ### ################# ############# # ### ## ### ## # ### ### # # # # #### +# # # # # # # # # # # # # # # # # # # # # # ## +####### ### ### ##### ### # # ########### # ####### ### # ### # ##### # # ##### ### # ### ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### # # ### ####### # # ##### ### # ### ### # ### # # # # ##### # # ########### # ##### # # ###### +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ##### #### # # # # # # ## ## # # ### ##### ### # ####### ### ####### # # # # ### # # ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ## # # # ### ####### # # # ##### # # ## # # # ######### ### ####### # ### ### # # # ### # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ### # # ##### # ### ##### # ### ### ### ##### # # # ### ###### ##### # # # # ##### ### # ## +# # # # # # # # # # # # # # # # # # # # # ## +# ##### # ######### # ######### ### # ## #### ### ############### # ########### ######### ## # # ## +# # # # # # # # # # # # # # # # ## +# ####### # # ##### ### # #### # ### # ### # # # ############# # # # # # # # ####### ### # # ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +####### ### ### # # ##### # # ### # # ### ##### ########### # ### ####### ####### # # ### # # # ## +# # # # # # # # # # # # # # # # # # # # # ## +# ### ## ##### ###### ## # ##### ### ### ##### ### ############# # ### # ##### ####### ### ##### ## +# # # # # # # # # # # # # # # # # # # # # ## +# # ### ## ###### # # # #### ### ### # # # ####### # ##### # ### # ####### ### ### ### ### ###### +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### # # ##### # ###### #### # # ### # # # # ### # # # ######### # ### ### # # ## ## # ### # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ### ### # ##### # ## # # # ### ##### # # # # # # # ### #### # ### # # ####### # # # ### # #### +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### ### ### # ### # ####### # ### # # ##### # ### # ### # ##### ### ### # ##### # ### # ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ### # # ### # # # #### ## # # # ### # # ### # ### ### ### # # # # # ####### ######### # #### +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # # # ##### # # ### # ### # # ### # ### # # # ### ### ############# # ### # ######### # ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### # # # # # ##### ### # # # # # # ##### # # # ##### # ##### # # ## # ### # # # ### ##### #### +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +### ##### ### ### # # # ### # ### # # ### # ### # # # # ### ### # # # ### ####### ## ## ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # # ## ### # ##### # # ### # # ##### # ### ### # # ### ### ##### ### # ##### ## ### ####### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# ####### ### ### # ####### ####### # # ### # # ####### ### ### # # # ##### ##### ### ### # # #### +# # # # # # # # # # # # # # # # # # # # # ## +# # ####### ####### ### # ### ### # # # ##### ########## # # # ####### # # ######### # # ### # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # # # ##### # ### ##### ### ##### # ##### ### # # # ### # ### # ### # # #### ## ### ### # # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # ##### ### ####### ##### ### # ### ## ### # # # ####### # # # # # ##### ##### # ### # ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +##### # # ### ### # # # ##### # # # ### # # # # ##### ### # ### # # # # ##### # ### # ### # # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# # ### # # ##### # ############ # ##### # #### ## # ##### # # ######### # ####### ## # # # # # ## +# # # # # # # # # # # # # # # # # # # ## +# ##### # ##### # ### ### # # ##### # ############### # # ### ######### # ### # ##### ### # ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # # ### # # # # ## ### ##### # ### ## ######## ### ########## # # ### # # ####### ### ##### ## +# # # # # # # # # # # # # # # # # # # ## +# ### # # # ### ##### ##### # # # ############### ######### ### # # ################ # # ### ###### +# # # # # # # # # # # # # # # # # # # ## +# # # ### ### ######### # # # ####### ### #### # ### ##### ######### # # # ##### # ##### # ######## +# # # # # # # # # # # # # # # # # # # # # # # # # # ## +### # # # ##### # # # # # # # ### # # ### # ### # ## # ## # ####### ######### ##### # # # ### ## +# # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ##### # # ### ### ### # ##### # # # ### # ############ ## ######### ### # ### ### # # # # ## +# # # # # # # # # # # # # # # # # # # # # # # # ## +# # ##### # # ##### # ### # #### ### # ######### ### # # ##### # ### ### # ### ### #### ## # ## +# # # # # # # # # # # # # # # # # # # # # # # # ## +# ##### ### #### # # ########### ### # # # # # # # ### # # # # # # ### # ### ##### ### # ### # # ## +# # # # # # # # # # # # # # # # # # # # # # # # # ## +# # # # # ### # ### # # ####### #### ######### ####### ### ####### # # ####### ### ### # ##### ## +# # # # # # # # # # # # # # # # # # # # # # # ## +# # ##### ##### # ####### ### ################# # # ### # # ### # # ### # # ### # ######### ##### ## +# # # # # # # # # # # # # # # # # # # # # # # # ## +### # ### # # ##### # # # # ### ### # ##### ####### ### ### # # ### ####### ####### ### ########## +# # # # # # # # # # # # # # # # # # # # # ## +# ######### # ##### ### ############# # ####### ##### ### ### # # ### # ### ##### # # ### ### # ## +# # # # # # # # # # # # # # # # # # # # # ## +# # # # # # ##### ##### # ######### # ### ### ### # ####### # # ### # ####### ##### ### # ### ## +# # # # # # # # # # # # # # # # # # # # ## +# ### # # # ##### # ###### #### # ##### # ### # # ### ######### ### ####### # ### ### ### ####### ## +# # # # # # # # # # # # # # # # # # # # # # # ## +# # ########### ### # ##### # # # # # ### # ### ### ### ##### ### ##### # ####### # ###### # # #### +# # # # # # # # # # # # # # # # # # # # # # # ## +# # # ###### # ### # # ########### ## ### ##### # # # # # # # # # ########### ### ######### ### ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +# ### ### # ### # ### # # ### # # ### # ### # ### # ##### # # # # ### # # ###### # ### # # ### # ## +# # # # # # # # # # # # # # # # # # # # # # # # # # # ## +### # # ##### ##### ### # # ####### ##### # # # ########### ######### ### # # ### ##### # # # # ## +# # # # # # # # E## +#################################################################################################### +#################################################################################################### diff --git a/SolovevDS/docs/data/data_for_task2/maze50.txt b/SolovevDS/docs/data/data_for_task2/maze50.txt new file mode 100644 index 0000000..b574296 --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/maze50.txt @@ -0,0 +1,50 @@ +################################################## +#S # # # # # ## +##### ##### ### ######### ### ### # ### # ### # ## +# # # # # # # # # # # # ## +### ######### ############# ### # # # ##### ### ## +# # # # # # # # # # ## +# ########### # ### # # ##### # ##### # ### # ## +# # # # # # # # # # # ## +### # # ### ### # ### # ######### # ####### ### ## +# # # # # # # # # # ## +# ##### # ### ##### # ##### ##### ########### #### +# # # # # # # # # # ## +##### # ### ##### # ########### ##### ##### ### ## +# # # # # # # # # # ## +# ####### # ####### # ####### ### # ### # ### # ## +# # # # # # # # # # # # ## +# # # # ################### # # ### # # # ### # ## +# # # # # # # # # # # # # ## +### # ### ### # # ########### ### # # # ### ### ## +# # # # # # # # # # # # # # ## +# ##### ### ######### ##### ### # ### # # ### # ## +# # # # # # # # # # # # # ## +# # # ####### ### ### # ### # # ### # # # # # #### +# # # # # # # # # # # # # # ## +# # ########### ####### # ### # # ######### ### ## +# # # # # # # # ## +# ### ####### # ##### ##### # ####### ### ### #### +# # # # # # # # # # # # ## +# ######### ####### # # # # ### # ####### # ### ## +# # # # # # # # # # # # ## +### # ### ##### ####### # # # # ### # # ####### ## +# # # # # # # # # # # # # # ## +# # # # ### # ####### # # # ##### # # ### ### # ## +# # # # # # # # # # # # # # # # ## +# # # ######### # # # ### ### ### ### # ### # # ## +# # # # # # # # # # # # # ## +# ### # ####### # ######### # ####### ### # ### ## +# # # # # # # # # # # # # ## +# ##### # # # ##### # # ####### ### # # ### # #### +# # # # # # # # # # # # # # ## +##### ### # # # ##### ########### # # # # # ### ## +# # # # # # # # # # # ## +# ############# # ### ##### ##### # ### # ##### ## +# # # # # # # # # # # ## +# # ####### ### # # ### # ### ### ### # ##### # ## +# # # # # # # # # # # # # ## +# ##### ##### ### # # ##### ### ### ##### ##### ## +# # # # # # E## +################################################## +################################################## diff --git a/SolovevDS/docs/data/data_for_task2/maze_empty.txt b/SolovevDS/docs/data/data_for_task2/maze_empty.txt new file mode 100644 index 0000000..a92cf1d --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/maze_empty.txt @@ -0,0 +1,50 @@ +S + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + E diff --git a/SolovevDS/docs/data/data_for_task2/maze_no_path.txt b/SolovevDS/docs/data/data_for_task2/maze_no_path.txt new file mode 100644 index 0000000..03906b4 --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/maze_no_path.txt @@ -0,0 +1,10 @@ +########## +#S # +# ###### # +# # # # +# # ## # # +# # ## # # +# # # # +# ######## +# #E# +########## diff --git a/SolovevDS/docs/data/data_for_task2/path_length_comparison.svg b/SolovevDS/docs/data/data_for_task2/path_length_comparison.svg new file mode 100644 index 0000000..f3af08a --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/path_length_comparison.svg @@ -0,0 +1,1536 @@ + + + + + + + + 2026-05-23T13:53:58.910555 + image/svg+xml + + + Matplotlib v3.10.5, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SolovevDS/docs/data/data_for_task2/results.csv b/SolovevDS/docs/data/data_for_task2/results.csv new file mode 100644 index 0000000..8bc14f3 --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/results.csv @@ -0,0 +1,16 @@ +maze,strategy,time_ms,cells_visited,way_len +small_10,BFS,0.00724,31.00000,21.00000 +small_10,DFS,0.00360,31.00000,21.00000 +small_10,AStar,0.00519,24.00000,21.00000 +medium_50,BFS,0.04465,505.00000,145.00000 +medium_50,DFS,0.03666,385.00000,361.00000 +medium_50,AStar,0.05370,319.00000,145.00000 +large_100,BFS,0.44010,4534.00000,245.00000 +large_100,DFS,0.09760,816.00000,703.00000 +large_100,AStar,0.37331,1298.00000,245.00000 +empty,BFS,0.15303,2500.00000,99.00000 +empty,DFS,0.09335,1275.00000,1275.00000 +empty,AStar,0.17047,341.00000,99.00000 +no_path,BFS,0.00259,25.00000,0.00000 +no_path,DFS,0.00244,25.00000,0.00000 +no_path,AStar,0.00494,25.00000,0.00000 diff --git a/SolovevDS/docs/data/data_for_task2/task2.cpp b/SolovevDS/docs/data/data_for_task2/task2.cpp new file mode 100644 index 0000000..07f957c --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/task2.cpp @@ -0,0 +1,934 @@ +#include +#include +#include +#include /*для ошибок*/ +#include +#include /*мерит время*/ +#include /*волшебная отрисовка*/ +#include +#include +#include + +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; + } +}; + + +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; this->strategy = nullptr;} + + 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 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; + } +}; + +class Player{ + private: + cell* current; + public: + Player(cell* current) {this->current = current;} + + cell* getCurrent() {return current;} + void moveTo(cell* cell) {this->current = cell;} +}; + +class Direction{ + private: + int dx; + int dy; + + public: + Direction(int dx, int dy) { + this->dx = dx; + this->dy = dy; + } + + int getDx() {return dx;} + int getDy() {return dy;} +}; + +class Command { + public: + virtual void execute() = 0; + virtual void undo() = 0; + virtual ~Command() {} +}; + +class MoveCommand : public Command{ + private: + Player* player; + Direction dir; + cell* previousCell; + maze* labirint; + public: + MoveCommand(Player* player, maze* labirint, Direction dir) : dir(dir) { + this->player = player; + this->labirint = labirint; + this->previousCell = nullptr; + } + + void execute() override { + cell* currentCell = player->getCurrent(); + + int newX = currentCell->getX() + dir.getDx(); + int newY = currentCell->getY() + dir.getDy(); + + cell* nextCell = labirint->getCell(newX, newY); + + if (nextCell != nullptr && nextCell->isPassable()) { + previousCell = currentCell; + player->moveTo(nextCell); + } + else { + std::cout << "Нельзя сделать ход!" << std::endl; + } + } + + void undo() override { + if (previousCell != nullptr) { + player->moveTo(previousCell); + previousCell = nullptr; + } + } + +}; + +class ConsolController{ + private: + maze* labirint; + Player* player; + Command* lastCommand; /*указатель на последнюю команду(на объект класса command)*/ + bool running; + public: + ConsolController(maze* labirint, Player* player) { + this->labirint = labirint; + this->player = player; + this->lastCommand = nullptr; + this->running = false; + } + + ~ConsolController() { + if (lastCommand != nullptr) { + delete lastCommand; + } + } + + void run() { + running = true; + + while (running) { + clearConsole(); + drawMaze(); + + std::cout << "W/A/S/D - ход, Z - отмена, Q - выход" << std::endl; + char ch; + std::cin >> ch; + + handleInput(ch); + } + } + + void handleInput(char ch) { + switch(ch){ + case 'q': + case 'Q': + running = false; + break; + case 'z': + case 'Z': + undoLastMove(); + break; + default: + handleMove(ch); + break; + } + } + + void handleMove(char ch) { + Direction dir(0, 0); + + switch (ch) { + case 'w': + case 'W': + dir = Direction(0, -1); + break; + case 's': + case 'S': + dir = Direction(0, 1); + break; + case 'a': + case 'A': + dir = Direction(-1, 0); + break; + case 'd': + case 'D': + dir = Direction(1, 0); + break; + default: + return; + } + + if (lastCommand != nullptr) { + delete lastCommand; + lastCommand = nullptr; + } + + lastCommand = new MoveCommand(player, labirint, dir); + lastCommand->execute(); + } + + void undoLastMove() { + if (lastCommand != nullptr) { + lastCommand->undo(); + delete lastCommand; + lastCommand = nullptr; /*можно отменить только одну команду назад, указатель делаем 0, чтобы не долбится в отмену уже отмененной команды*/ + } + } + + void clearConsole() { + #ifdef _WIN32 + system("cls"); + #else + system("clear"); + #endif + } + + void drawMaze() { + for (int y = 0; y < labirint->getHeight(); y++) { + for (int x = 0; x < labirint->getWidth(); x++) { + cell* currentCell = labirint->getCell(x, y); + + if (currentCell == player->getCurrent()) + std::cout << "P"; + else if (currentCell->getIsWall()) + std::cout << "#"; + else if (currentCell->getIsStart()) + std::cout << "S"; + else if (currentCell->getIsExit()) + std::cout << "E"; + else + std::cout << " "; + } + std::cout << std::endl; + } + } +}; + +class Benchmark { + private: + int RUNS = 10; + public: + Benchmark(int runs) {this->RUNS = runs;} + void benchmark(){ + std::string mazeFiles[] = { + "SolovevDS/docs/data/data_for_task2/maze10.txt", + "SolovevDS/docs/data/data_for_task2/maze50.txt", + "SolovevDS/docs/data/data_for_task2/maze100.txt", + "SolovevDS/docs/data/data_for_task2/maze_empty.txt", + "SolovevDS/docs/data/data_for_task2/maze_no_path.txt" + }; + + std::string mazeNames[] = { + "small_10", + "medium_50", + "large_100", + "empty", + "no_path" + }; + + std::ofstream csv("SolovevDS/docs/data/data_for_task2/results.csv"); + + if (!csv.is_open()) + throw std::runtime_error("Ошибка: не удалось создать results.csv!"); + + csv << "maze,strategy,time_ms,cells_visited,way_len\n"; + + TextFileMazeBuilder builder; + + for (int i = 0; i < 5; i++) { + maze* labirint = builder.buildFromFile(mazeFiles[i]); + + MazeSolver solver(labirint); + + BFSStrategy bfs; + DFSStrategy dfs; + AStarStrategy astar; + + PathFindingStrategy* strategies[] = {&bfs, &dfs, &astar}; + std::string strategyNames[] = {"BFS", "DFS", "AStar"}; + + for (int s = 0; s < 3; s++) { + double sumTime = 0; + double sumVisited = 0; + double sumPathLength = 0; + + for (int run = 0; run < RUNS; run++) { + solver.setStrategy(strategies[s]); + + SearchStats stats = solver.solve(); + + sumTime += stats.timeMs; + sumVisited += stats.visitedCells; + sumPathLength += stats.pathLength; + } + + double avgTime = sumTime / RUNS; + double avgVisited = sumVisited / RUNS; + double avgPathLength = sumPathLength / RUNS; + + csv << mazeNames[i] << "," + << strategyNames[s] << "," + << std::fixed << std::setprecision(5) << avgTime << "," + << avgVisited << "," + << avgPathLength << "\n"; + } + delete labirint; + } + + csv.close(); + } +}; + + +int main(){ + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + setlocale(LC_ALL, ".UTF-8"); + Benchmark ben(10); + ben.benchmark(); + + TextFileMazeBuilder builder; + maze* labirint = builder.buildFromFile("SolovevDS/docs/data/data_for_task2/maze10.txt"); + Player player(labirint->getStart()); + ConsolController controller(labirint, &player); + controller.run(); + delete labirint; + + return 0; +} diff --git a/SolovevDS/docs/data/data_for_task2/time_comparison.svg b/SolovevDS/docs/data/data_for_task2/time_comparison.svg new file mode 100644 index 0000000..d50a3e3 --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/time_comparison.svg @@ -0,0 +1,1582 @@ + + + + + + + + 2026-05-23T13:53:58.486270 + image/svg+xml + + + Matplotlib v3.10.5, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SolovevDS/docs/data/data_for_task2/visited_cells_comparison.svg b/SolovevDS/docs/data/data_for_task2/visited_cells_comparison.svg new file mode 100644 index 0000000..05def5c --- /dev/null +++ b/SolovevDS/docs/data/data_for_task2/visited_cells_comparison.svg @@ -0,0 +1,1530 @@ + + + + + + + + 2026-05-23T13:53:58.677345 + image/svg+xml + + + Matplotlib v3.10.5, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SolovevDS/docs/laba_2_report.pdf b/SolovevDS/docs/laba_2_report.pdf new file mode 100644 index 0000000..005e523 Binary files /dev/null and b/SolovevDS/docs/laba_2_report.pdf differ