# Отчет по заданию: поиск выхода из лабиринта

## 1. Описание задачи и выбранных паттернов

Цель работы - реализовать расширяемую программу для загрузки лабиринта из файла,
поиска пути от старта `S` до выхода `E`, визуализации результата и сравнения
алгоритмов на лабиринтах разной сложности.

В проекте реализованы четыре паттерна GoF:

| Паттерн | Где реализован | Зачем нужен |
|---|---|---|
| Builder | `MazeBuilder`, `TextFileMazeBuilder` | Изолирует парсинг и валидацию файла от остального приложения. |
| Strategy | `PathFindingStrategy`, `BFSStrategy`, `DFSStrategy`, `AStarStrategy`, `DijkstraStrategy` | Позволяет менять алгоритм поиска без изменения `MazeSolver`. |
| Observer | `Observer`, `ConsoleView`, события `search_started`, `path_found`, `path_not_found` | Отделяет вычисления от отображения в консоли. |
| Command | `Command`, `MoveCommand`, `Player` | Инкапсулирует ход игрока и поддерживает отмену хода. |

Диаграмма классов:

```mermaid
classDiagram
    class Cell {
        +int x
        +int y
        +bool is_wall
        +bool is_start
        +bool is_exit
        +int weight
        +is_passable() bool
    }

    class Maze {
        +list cells
        +int width
        +int height
        +Cell start
        +Cell exit
        +get_cell(x, y) Cell
        +get_neighbors(cell) list
        +to_text(path, player) str
    }

    class MazeBuilder {
        <<interface>>
        +build_from_file(filename) Maze
    }

    class TextFileMazeBuilder {
        +build_from_file(filename) Maze
    }

    class PathFindingStrategy {
        <<interface>>
        +find_path(maze, start, exit) PathResult
    }

    class BFSStrategy
    class DFSStrategy
    class AStarStrategy
    class DijkstraStrategy

    class SearchStats {
        +str strategy_name
        +float time_ms
        +int visited_cells
        +int path_length
        +list path
    }

    class MazeSolver {
        +set_strategy(strategy)
        +add_observer(observer)
        +solve() SearchStats
    }

    class Observer {
        <<interface>>
        +update(event)
    }

    class ConsoleView {
        +update(event)
        +render(maze, player_position, path) str
    }

    class Command {
        <<interface>>
        +execute() bool
        +undo() bool
    }

    class MoveCommand
    class Player

    MazeBuilder <|.. TextFileMazeBuilder
    MazeBuilder --> Maze : creates
    PathFindingStrategy <|.. BFSStrategy
    PathFindingStrategy <|.. DFSStrategy
    PathFindingStrategy <|.. AStarStrategy
    PathFindingStrategy <|.. DijkstraStrategy
    MazeSolver --> PathFindingStrategy : uses
    MazeSolver --> Maze : uses
    MazeSolver --> Observer : notifies
    Observer <|.. ConsoleView
    Command <|.. MoveCommand
    MoveCommand --> Player
    Player --> Cell
```

## 2. Ключевые классы

Основные файлы проекта:

| Файл | Назначение |
|---|---|
| `maze_solver/models.py` | Классы `Cell` и `Maze`, поиск соседей, текстовая отрисовка. |
| `maze_solver/builders.py` | Интерфейс Builder и загрузка лабиринта из `.txt`. |
| `maze_solver/strategies.py` | BFS, DFS, A* и Дейкстра. |
| `maze_solver/solver.py` | Оркестратор поиска и сбор статистики. |
| `maze_solver/observers.py` | Observer и консольное представление. |
| `maze_solver/commands.py` | Command, игрок и undo перемещения. |
| `main.py` | CLI для запуска поиска и ручного режима. |
| `scripts/run_experiments.py` | Замеры и построение SVG-графиков. |

Пример запуска:

```bash
python3 main.py --maze data/mazes/small.txt --strategy astar --render
```

## 3. Результаты экспериментов

Для каждого лабиринта и каждой стратегии выполнено 10 запусков. В таблице указаны
средние значения. Длина пути считается в клетках, включая старт и выход.

| Лабиринт | Стратегия | Время, мс | Посещено клеток | Длина пути | Путь найден |
|---|---:|---:|---:|---:|---|
| Маленький 10x10 | BFS | 0.0423 | 37.0 | 20.0 | да |
| Маленький 10x10 | DFS | 0.0273 | 24.0 | 22.0 | да |
| Маленький 10x10 | A* | 0.0677 | 31.0 | 20.0 | да |
| Маленький 10x10 | Dijkstra | 0.0551 | 37.0 | 20.0 | да |
| Средний 50x50 | BFS | 1.2769 | 1151.0 | 709.0 | да |
| Средний 50x50 | DFS | 0.9106 | 784.0 | 709.0 | да |
| Средний 50x50 | A* | 2.0089 | 1133.0 | 709.0 | да |
| Средний 50x50 | Dijkstra | 1.7041 | 1151.0 | 709.0 | да |
| Большой 100x100 | BFS | 5.2983 | 4801.0 | 1685.0 | да |
| Большой 100x100 | DFS | 2.5044 | 2155.0 | 1685.0 | да |
| Большой 100x100 | A* | 8.6574 | 4791.0 | 1685.0 | да |
| Большой 100x100 | Dijkstra | 7.1532 | 4800.0 | 1685.0 | да |
| Пустой 50x50 | BFS | 2.8927 | 2304.0 | 95.0 | да |
| Пустой 50x50 | DFS | 0.1404 | 187.0 | 95.0 | да |
| Пустой 50x50 | A* | 0.2374 | 95.0 | 95.0 | да |
| Пустой 50x50 | Dijkstra | 4.0408 | 2304.0 | 95.0 | да |
| Без пути 30x30 | BFS | 0.0015 | 1.0 | 0.0 | нет |
| Без пути 30x30 | DFS | 0.0013 | 1.0 | 0.0 | нет |
| Без пути 30x30 | A* | 0.0016 | 1.0 | 0.0 | нет |
| Без пути 30x30 | Dijkstra | 0.0018 | 1.0 | 0.0 | нет |

CSV с результатами сохранен в `reports/results.csv`.

Графики:

| Лабиринт | Время | Посещенные клетки |
|---|---|---|
| Маленький | ![](charts/small_time.svg) | ![](charts/small_visited.svg) |
| Средний | ![](charts/medium_time.svg) | ![](charts/medium_visited.svg) |
| Большой | ![](charts/large_time.svg) | ![](charts/large_visited.svg) |
| Пустой | ![](charts/empty_time.svg) | ![](charts/empty_visited.svg) |
| Без пути | ![](charts/no_exit_time.svg) | ![](charts/no_exit_visited.svg) |

## 4. Анализ эффективности

BFS гарантирует кратчайший путь в невзвешенном лабиринте. Это видно на маленьком
лабиринте: BFS, A* и Дейкстра нашли путь длиной 20, а DFS нашел более длинный путь
длиной 22. Недостаток BFS - широкий фронт поиска, из-за чего в пустом лабиринте он
посетил все 2304 доступные клетки.

DFS не гарантирует кратчайший путь, но часто работает быстро, потому что уходит
глубоко по одному направлению. На маленьком лабиринте это дало путь хуже оптимального.
На сгенерированных идеальных лабиринтах путь между двумя клетками единственный, поэтому
DFS, BFS, A* и Дейкстра получили одинаковую длину пути.

A* использует манхэттенскую эвристику. На пустом лабиринте он посетил только 95 клеток,
то есть фактически прошел по оптимальному маршруту. В запутанных идеальных лабиринтах
эвристика помогает слабее: прямое направление к выходу часто упирается в стены, поэтому
A* посещает почти столько же клеток, сколько BFS, а из-за приоритетной очереди тратит
больше времени.

Дейкстра в невзвешенном лабиринте по результату близок к BFS, но работает медленнее
из-за приоритетной очереди. Его преимущество проявляется при взвешенных клетках.
В проекте Builder уже поддерживает символы `2`, `3` и `~` как клетки с повышенной
стоимостью прохода, поэтому Дейкстру и A* можно использовать для дополнительного
сравнения на взвешенных картах.

Лабиринт "Без пути" проверяет корректную обработку отсутствия решения: стратегии
возвращают пустой путь, а `MazeSolver` фиксирует длину 0.

## 5. Выводы

ООП позволило разделить предметную модель, загрузку данных, алгоритмы и интерфейс.
Паттерн Builder делает формат входного файла заменяемым: можно добавить JSON-builder,
не меняя `Maze` и стратегии. Strategy позволяет добавлять новые алгоритмы без правок
в `MazeSolver`. Observer отделяет вычисления от вывода, а Command показывает, как
инкапсулировать пользовательские действия и поддержать undo.

Без этих паттернов код быстро стал бы монолитным: парсинг файла, поиск, статистика,
печать и ручное управление оказались бы в одном месте. Тогда добавление нового формата,
алгоритма или режима отображения требовало бы менять уже работающую логику.
