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

855 lines
25 KiB
C++
Raw Normal View History

2026-05-21 16:27:53 +00:00
#include <fstream>
#include <iostream>
#include <string>
2026-05-22 18:28:16 +00:00
#include <stdexcept> /*для ошибок*/
2026-05-22 17:25:06 +00:00
#include <cmath>
2026-05-22 18:28:16 +00:00
#include <chrono> /*мерит время*/
2026-05-22 21:04:45 +00:00
#include <cstdlib> /*волшебная отрисовка*/
2026-05-21 16:27:53 +00:00
2026-05-19 22:17:12 +00:00
class cell{
private:
int x, y;
bool isWall;
bool isExit;
bool isStart;
public:
2026-05-22 18:28:16 +00:00
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;
}
2026-05-19 22:17:12 +00:00
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:
2026-05-21 16:27:53 +00:00
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);
}
}
2026-05-19 22:17:12 +00:00
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];
2026-05-21 16:27:53 +00:00
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);
}
}
2026-05-19 22:17:12 +00:00
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];
}
2026-05-21 16:27:53 +00:00
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];
}
2026-05-19 22:17:12 +00:00
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;}
2026-05-21 21:51:21 +00:00
int getWidth() {return width;}
int getHeight() {return height;}
2026-05-21 16:27:53 +00:00
};
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
}
};
2026-05-21 21:51:21 +00:00
class PathFindingStrategy {
public:
virtual cell** findPath(maze* m, cell* start, cell* exit) = 0;
2026-05-22 18:28:16 +00:00
virtual int getVisitedCells() = 0; /*для посещенных клеток*/
2026-05-21 21:51:21 +00:00
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 {
2026-05-22 18:28:16 +00:00
private:
int visitedCells;
2026-05-21 21:51:21 +00:00
public:
2026-05-22 18:28:16 +00:00
BFSStrategy() {visitedCells = 0;}
int getVisitedCells() override {return visitedCells;}
2026-05-21 21:51:21 +00:00
cell** findPath(maze* m, cell* start, cell* exit) override {
2026-05-22 18:28:16 +00:00
visitedCells = 0;
2026-05-21 21:51:21 +00:00
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++;
2026-05-22 18:28:16 +00:00
visitedCells++;
2026-05-21 21:51:21 +00:00
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 {
2026-05-22 18:28:16 +00:00
private:
int visitedCells;
2026-05-21 21:51:21 +00:00
public:
2026-05-22 18:28:16 +00:00
DFSStrategy() {visitedCells = 0;}
int getVisitedCells() override {return visitedCells;}
2026-05-21 21:51:21 +00:00
cell** findPath(maze* m, cell* start, cell* exit) override {
2026-05-22 18:28:16 +00:00
visitedCells = 0;
2026-05-21 21:51:21 +00:00
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;
}
}
2026-05-22 17:25:06 +00:00
cell** stack = new cell*[width * height];
int top = 0;
2026-05-21 21:51:21 +00:00
stack[top] = start;
top++;
visited[start->getX()][start->getY()] = true;
bool found = false;
while (top > 0) {
top--;
cell* current = stack[top];
2026-05-22 18:28:16 +00:00
visitedCells++;
2026-05-21 21:51:21 +00:00
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 {
2026-05-22 17:25:06 +00:00
private:
int heuristic(cell* current, cell* exit) {return std::abs(current->getX() - exit->getX()) + std::abs(current->getY() - exit->getY());}
2026-05-22 18:28:16 +00:00
int visitedCells;
2026-05-22 17:25:06 +00:00
2026-05-21 21:51:21 +00:00
public:
2026-05-22 18:28:16 +00:00
AStarStrategy() {visitedCells = 0;}
int getVisitedCells() override {return visitedCells;}
2026-05-21 21:51:21 +00:00
cell** findPath(maze* m, cell* start, cell* exit) override {
2026-05-22 18:28:16 +00:00
visitedCells = 0;
2026-05-22 17:25:06 +00:00
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] шагов*/
2026-05-22 18:28:16 +00:00
int** fScore = new int*[width]; /*f = h + g, где h - эвристика клетки[x][y]*/
2026-05-22 17:25:06 +00:00
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;
2026-05-22 18:28:16 +00:00
visitedCells++; /*чтоб выход засчитывался*/
2026-05-22 17:25:06 +00:00
break;
}
int cx = current->getX();
int cy = current->getY();
open[bestIndex] = open[openCount - 1];
openCount--;
inOpen[cx][cy] = false;
closed[cx][cy] = true;
2026-05-22 18:28:16 +00:00
visitedCells++;
2026-05-22 17:25:06 +00:00
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;
2026-05-21 21:51:21 +00:00
}
};
2026-05-22 18:28:16 +00:00
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:
2026-05-22 21:04:45 +00:00
MazeSolver(maze* labirint) {this->labirint = labirint; this->strategy = nullptr;}
2026-05-22 18:28:16 +00:00
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;
}
2026-05-22 21:04:45 +00:00
};
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);
}
}
2026-05-22 18:28:16 +00:00
2026-05-22 21:04:45 +00:00
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;
}
}
2026-05-22 18:28:16 +00:00
};
2026-05-21 21:51:21 +00:00
2026-05-22 21:04:45 +00:00
2026-05-21 16:27:53 +00:00
int main(){
}