forked from UNN/2026-rff_mp
855 lines
25 KiB
C++
855 lines
25 KiB
C++
#include <fstream>
|
||
#include <iostream>
|
||
#include <string>
|
||
#include <stdexcept> /*для ошибок*/
|
||
#include <cmath>
|
||
#include <chrono> /*мерит время*/
|
||
#include <cstdlib> /*волшебная отрисовка*/
|
||
|
||
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; 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;
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
int main(){
|
||
|
||
} |