2026-rff_mp/shahovaa/zadanie 2/reports/report.md
2026-05-19 22:39:51 +03:00

209 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Отчет по заданию: поиск выхода из лабиринта
## 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.
Без этих паттернов код быстро стал бы монолитным: парсинг файла, поиск, статистика,
печать и ручное управление оказались бы в одном месте. Тогда добавление нового формата,
алгоритма или режима отображения требовало бы менять уже работающую логику.