Merge branch 'SolovevDS-task2' into SolovevDS-task1

This commit is contained in:
solovevds 2026-05-23 19:38:43 +03:00
commit 63e6158583
12 changed files with 5894 additions and 0 deletions

View File

@ -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"
)

View File

@ -0,0 +1,10 @@
##########
#S ###
###### ###
# ###
# #### ###
# # ###
# # ######
# # #
# ######E#
##########

View File

@ -0,0 +1,100 @@
####################################################################################################
#S # # # # # # # # # ##
## ############### # ## ## ###### # ### ### ### ########### ### # ## # ####### # ### # # # ##
# # # # # # # # # # # # # # # # # # # ##
# ### # ##### # #### ##### ######### # ##### # ##### # ### ##### ### # # ### # ####### ##### # ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# ##### ### ##### ##### # # ### # ##### # ##### # ####### ######### ##### # # # # ### ### # # ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# # # ### # # # ### # ##### ### ### # # ##### # # # ### # # ##### ### ##### ### ########### ### ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# # ### ########### # # ### # ### # # ### # ### # ##### # ####### #### ## # ### ######### ####### ##
# # # # # # # # # # # # # # # # # # ##
# ####### ### # # # ### # ### ################# ############# # ### ## ### ## # ### ### # # # # ####
# # # # # # # # # # # # # # # # # # # # # # ##
####### ### ### ##### ### # # ########### # ####### ### # ### # ##### # # ##### ### # ### ##### ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# ### # # ### ####### # # ##### ### # ### ### # ### # # # # ##### # # ########### # ##### # # ######
# # # # # # # # # # # # # # # # # # # # # # # # # # ##
# # ##### #### # # # # # # ## ## # # ### ##### ### # ####### ### ####### # # # # ### # # ##### ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# # ## # # # ### ####### # # # ##### # # ## # # # ######### ### ####### # ### ### # # # ### # # ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
# ##### ### # # ##### # ### ##### # ### ### ### ##### # # # ### ###### ##### # # # # ##### ### # ##
# # # # # # # # # # # # # # # # # # # # # ##
# ##### # ######### # ######### ### # ## #### ### ############### # ########### ######### ## # # ##
# # # # # # # # # # # # # # # # ##
# ####### # # ##### ### # #### # ### # ### # # # ############# # # # # # # # ####### ### # # ### ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
####### ### ### # # ##### # # ### # # ### ##### ########### # ### ####### ####### # # ### # # # ##
# # # # # # # # # # # # # # # # # # # # # ##
# ### ## ##### ###### ## # ##### ### ### ##### ### ############# # ### # ##### ####### ### ##### ##
# # # # # # # # # # # # # # # # # # # # # ##
# # ### ## ###### # # # #### ### ### # # # ####### # ##### # ### # ####### ### ### ### ### ######
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# ### # # ##### # ###### #### # # ### # # # # ### # # # ######### # ### ### # # ## ## # ### # ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
# # ### ### # ##### # ## # # # ### ##### # # # # # # # ### #### # ### # # ####### # # # ### # ####
# # # # # # # # # # # # # # # # # # # # # # # # # # # ##
# ### ### ### # ### # ####### # ### # # ##### # ### # ### # ##### ### ### # ##### # ### # ### ##
# # # # # # # # # # # # # # # # # # # # # # # # # # ##
# ##### ### # # ### # # # #### ## # # # ### # # ### # ### ### ### # # # # # ####### ######### # ####
# # # # # # # # # # # # # # # # # # # # # # # # # # ##
# # # # # ##### # # ### # ### # # ### # ### # # # ### ### ############# # ### # ######### # ### ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
# ### # # # # # ##### ### # # # # # # ##### # # # ##### # ##### # # ## # ### # # # ### ##### ####
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
### ##### ### ### # # # ### # ### # # ### # ### # # # # ### ### # # # ### ####### ## ## ### ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # ##
# # # # ## ### # ##### # # ### # # ##### # ### ### # # ### ### ##### ### # ##### ## ### ####### ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# ####### ### ### # ####### ####### # # ### # # ####### ### ### # # # ##### ##### ### ### # # ####
# # # # # # # # # # # # # # # # # # # # # ##
# # ####### ####### ### # ### ### # # # ##### ########## # # # ####### # # ######### # # ### # # ##
# # # # # # # # # # # # # # # # # # # # # # # # # # ##
# # # # # ##### # ### ##### ### ##### # ##### ### # # # ### # ### # ### # # #### ## ### ### # # # ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # ##
# # # ##### ### ####### ##### ### # ### ## ### # # # ####### # # # # # ##### ##### # ### # ##### ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
##### # # ### ### # # # ##### # # # ### # # # # ##### ### # ### # # # # ##### # ### # ### # # # ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # # ##
# # ### # # ##### # ############ # ##### # #### ## # ##### # # ######### # ####### ## # # # # # ##
# # # # # # # # # # # # # # # # # # # ##
# ##### # ##### # ### ### # # ##### # ############### # # ### ######### # ### # ##### ### # ##### ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# # # # ### # # # # ## ### ##### # ### ## ######## ### ########## # # ### # # ####### ### ##### ##
# # # # # # # # # # # # # # # # # # # ##
# ### # # # ### ##### ##### # # # ############### ######### ### # # ################ # # ### ######
# # # # # # # # # # # # # # # # # # # ##
# # # ### ### ######### # # # ####### ### #### # ### ##### ######### # # # ##### # ##### # ########
# # # # # # # # # # # # # # # # # # # # # # # # # # ##
### # # # ##### # # # # # # # ### # # ### # ### # ## # ## # ####### ######### ##### # # # ### ##
# # # # # # # # # # # # # # # # # # # # # # # ##
# ##### ##### # # ### ### ### # ##### # # # ### # ############ ## ######### ### # ### ### # # # # ##
# # # # # # # # # # # # # # # # # # # # # # # # ##
# # ##### # # ##### # ### # #### ### # ######### ### # # ##### # ### ### # ### ### #### ## # ##
# # # # # # # # # # # # # # # # # # # # # # # # ##
# ##### ### #### # # ########### ### # # # # # # # ### # # # # # # ### # ### ##### ### # ### # # ##
# # # # # # # # # # # # # # # # # # # # # # # # # ##
# # # # # ### # ### # # ####### #### ######### ####### ### ####### # # ####### ### ### # ##### ##
# # # # # # # # # # # # # # # # # # # # # # # ##
# # ##### ##### # ####### ### ################# # # ### # # ### # # ### # # ### # ######### ##### ##
# # # # # # # # # # # # # # # # # # # # # # # # ##
### # ### # # ##### # # # # ### ### # ##### ####### ### ### # # ### ####### ####### ### ##########
# # # # # # # # # # # # # # # # # # # # # ##
# ######### # ##### ### ############# # ####### ##### ### ### # # ### # ### ##### # # ### ### # ##
# # # # # # # # # # # # # # # # # # # # # ##
# # # # # # ##### ##### # ######### # ### ### ### # ####### # # ### # ####### ##### ### # ### ##
# # # # # # # # # # # # # # # # # # # # ##
# ### # # # ##### # ###### #### # ##### # ### # # ### ######### ### ####### # ### ### ### ####### ##
# # # # # # # # # # # # # # # # # # # # # # # ##
# # ########### ### # ##### # # # # # ### # ### ### ### ##### ### ##### # ####### # ###### # # ####
# # # # # # # # # # # # # # # # # # # # # # # ##
# # # ###### # ### # # ########### ## ### ##### # # # # # # # # # ########### ### ######### ### ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # ##
# ### ### # ### # ### # # ### # # ### # ### # ### # ##### # # # # ### # # ###### # ### # # ### # ##
# # # # # # # # # # # # # # # # # # # # # # # # # # # ##
### # # ##### ##### ### # # ####### ##### # # # ########### ######### ### # # ### ##### # # # # ##
# # # # # # # # E##
####################################################################################################
####################################################################################################

View File

@ -0,0 +1,50 @@
##################################################
#S # # # # # ##
##### ##### ### ######### ### ### # ### # ### # ##
# # # # # # # # # # # # ##
### ######### ############# ### # # # ##### ### ##
# # # # # # # # # # ##
# ########### # ### # # ##### # ##### # ### # ##
# # # # # # # # # # # ##
### # # ### ### # ### # ######### # ####### ### ##
# # # # # # # # # # ##
# ##### # ### ##### # ##### ##### ########### ####
# # # # # # # # # # ##
##### # ### ##### # ########### ##### ##### ### ##
# # # # # # # # # # ##
# ####### # ####### # ####### ### # ### # ### # ##
# # # # # # # # # # # # ##
# # # # ################### # # ### # # # ### # ##
# # # # # # # # # # # # # ##
### # ### ### # # ########### ### # # # ### ### ##
# # # # # # # # # # # # # # ##
# ##### ### ######### ##### ### # ### # # ### # ##
# # # # # # # # # # # # # ##
# # # ####### ### ### # ### # # ### # # # # # ####
# # # # # # # # # # # # # # ##
# # ########### ####### # ### # # ######### ### ##
# # # # # # # # ##
# ### ####### # ##### ##### # ####### ### ### ####
# # # # # # # # # # # # ##
# ######### ####### # # # # ### # ####### # ### ##
# # # # # # # # # # # # ##
### # ### ##### ####### # # # # ### # # ####### ##
# # # # # # # # # # # # # # ##
# # # # ### # ####### # # # ##### # # ### ### # ##
# # # # # # # # # # # # # # # # ##
# # # ######### # # # ### ### ### ### # ### # # ##
# # # # # # # # # # # # # ##
# ### # ####### # ######### # ####### ### # ### ##
# # # # # # # # # # # # # ##
# ##### # # # ##### # # ####### ### # # ### # ####
# # # # # # # # # # # # # # ##
##### ### # # # ##### ########### # # # # # ### ##
# # # # # # # # # # # ##
# ############# # ### ##### ##### # ### # ##### ##
# # # # # # # # # # # ##
# # ####### ### # # ### # ### ### ### # ##### # ##
# # # # # # # # # # # # # ##
# ##### ##### ### # # ##### ### ### ##### ##### ##
# # # # # # E##
##################################################
##################################################

View File

@ -0,0 +1,50 @@
S
E

View File

@ -0,0 +1,10 @@
##########
#S #
# ###### #
# # # #
# # ## # #
# # ## # #
# # # #
# ########
# #E#
##########

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -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
1 maze strategy time_ms cells_visited way_len
2 small_10 BFS 0.00724 31.00000 21.00000
3 small_10 DFS 0.00360 31.00000 21.00000
4 small_10 AStar 0.00519 24.00000 21.00000
5 medium_50 BFS 0.04465 505.00000 145.00000
6 medium_50 DFS 0.03666 385.00000 361.00000
7 medium_50 AStar 0.05370 319.00000 145.00000
8 large_100 BFS 0.44010 4534.00000 245.00000
9 large_100 DFS 0.09760 816.00000 703.00000
10 large_100 AStar 0.37331 1298.00000 245.00000
11 empty BFS 0.15303 2500.00000 99.00000
12 empty DFS 0.09335 1275.00000 1275.00000
13 empty AStar 0.17047 341.00000 99.00000
14 no_path BFS 0.00259 25.00000 0.00000
15 no_path DFS 0.00244 25.00000 0.00000
16 no_path AStar 0.00494 25.00000 0.00000

View File

@ -0,0 +1,934 @@
#include <fstream>
#include <iostream>
#include <string>
#include <stdexcept> /*для ошибок*/
#include <cmath>
#include <chrono> /*мерит время*/
#include <cstdlib> /*волшебная отрисовка*/
#include <iomanip>
#include <windows.h>
#include <clocale>
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<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;
}
};
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;
}

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.