# Отчет по лабораторной работе: Поиск выхода из лабиринта ## 1. Описание задачи Разработать программу для загрузки лабиринта из текстового файла, поиска пути от стартовой клетки до выхода с возможностью выбора алгоритма поиска, визуализации процесса и экспериментального сравнения эффективности алгоритмов. ### Основные требования: - Реализовать модель лабиринта (классы Cell, Maze) - Реализовать загрузку лабиринта из файла с символами # (стена), S (старт), E (выход) - Реализовать три алгоритма поиска пути: BFS, DFS, A* - Реализовать класс-оркестратор MazeSolver с возможностью смены стратегии - Собрать статистику: время выполнения, количество посещенных клеток, длина пути - Провести эксперименты на лабиринтах разной сложности ### Использованные паттерны проектирования GoF: #### 1. Builder - **Где используется:** Классы `LabyrinthBuilder` и `TxtLabyrinthBuilder` - **Почему выбран:** Создание лабиринта из файла включает сложную логику парсинга, валидации и установки старта и выхода. Builder скрывает эти детали от клиента и позволяет легко добавлять новые форматы файлов - **Преимущества:** При добавлении нового формата достаточно создать новый класс-строитель, не меняя существующие классы Labyrinth и алгоритмы поиска #### 2. Strategy - **Где используется:** Классы `SearchAlgorithm`, `BFS`, `DFS`, `AStar` - **Почему выбран:** Алгоритмы поиска пути взаимозаменяемы и решают одну задачу разными способами. Strategy позволяет динамически менять алгоритм во время выполнения и легко добавлять новые алгоритмы - **Преимущества:** Класс Pathfinder может использовать любую стратегию через метод set_algorithm. Добавление нового алгоритма требует только создания нового класса #### 3. Observer - **Где используется:** Классы `EventListener` и `ConsoleDisplay` - **Почему выбран:** Приложение должно обновлять консольный интерфейс при различных событиях. Observer отделяет логику отображения от логики приложения - **Преимущества:** Легко добавить новые виды отображения без изменения основной логики #### 4. Command - **Где используется:** Классы `Action` и `MoveAction` - **Почему выбран:** Для реализации пошагового перемещения игрока с возможностью отмены действий. Command инкапсулирует действие в объект и позволяет реализовать undo и redo - **Преимущества:** Хранение истории действий и возможность отмены последних ходов без изменения логики класса Walker ## 2. Архитектура приложения Приложение состоит из следующих основных компонентов: | Компонент | Назначение | |-----------|------------| | `GridCell` | Модель клетки лабиринта (координаты, стена, старт, выход) | | `Labyrinth` | Модель лабиринта (сетка клеток, методы доступа) | | `TxtLabyrinthBuilder` | Загрузка лабиринта из текстового файла | | `BFS`, `DFS`, `AStar` | Алгоритмы поиска пути | | `Pathfinder` | Оркестратор, управляющий поиском | | `ConsoleDisplay` | Визуализация лабиринта и игрока | | `Walker` | Управление позицией игрока | | `MoveAction` | Команда перемещения с поддержкой Undo | ## 3. Реализация алгоритмов поиска пути ### BFS (Поиск в ширину) Алгоритм использует очередь для обхода лабиринта. Начинает со стартовой клетки, помещает её в очередь. Затем циклически извлекает клетку из начала очереди, проверяет не является ли она выходом, и добавляет всех непосещенных соседей в конец очереди. **Гарантирует нахождение кратчайшего пути** по количеству шагов. ### DFS (Поиск в глубину) Алгоритм использует стек для обхода лабиринта. Начинает со стартовой клетки, помещает её в стек. Затем циклически извлекает клетку из конца стека, проверяет не является ли она выходом, и добавляет всех непосещенных соседей в стек. **Не гарантирует нахождение кратчайшего пути**, но обычно быстрее по времени. ### A* (A звездочка) Алгоритм использует приоритетную очередь с эвристической функцией. Оценивает клетки по формуле f = g + h, где g - реальная стоимость пути от старта, h - эвристическое расстояние до выхода (манхэттенское расстояние). **Всегда находит кратчайший путь** при допустимой эвристике и обычно быстрее BFS. ## 4. Экспериментальная часть ### Тестовые лабиринты | Имя файла | Размер | Описание | |-----------|--------|----------| | level1.txt | 10x6 | Простой лабиринт | | medium10x10.txt | 10x10 | Лабиринт среднего размера | | large20x20.txt | 20x20 | Большой запутанный лабиринт | | empty15x15.txt | 15x15 | Пустой лабиринт без стен | | no_exit10x10.txt | 10x10 | Лабиринт без достижимого выхода | ### Результаты замеров Каждый эксперимент проводился 3 раза с усреднением результатов. | Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути | |----------|----------|------------|-----------------|------------| | Small 10x6 | BFS | 0.032 | 24 | 11 | | Small 10x6 | DFS | 0.017 | 17 | 11 | | Small 10x6 | A* | 0.064 | 24 | 11 | | Medium 10x10 | BFS | 0.044 | 42 | 16 | | Medium 10x10 | DFS | 0.024 | 26 | 16 | | Medium 10x10 | A* | 0.060 | 30 | 16 | | Large 20x20 | BFS | 0.245 | 211 | 36 | | Large 20x20 | DFS | 0.211 | 170 | 100 | | Large 20x20 | A* | 0.264 | 103 | 36 | | Empty 15x15 | BFS | 0.199 | 169 | 25 | | Empty 15x15 | DFS | 0.122 | 169 | 97 | | Empty 15x15 | A* | 0.411 | 169 | 25 | | No exit 10x10 | BFS | 0.054 | 45 | 18 | | No exit 10x10 | DFS | 0.030 | 28 | 18 | | No exit 10x10 | A* | 0.083 | 35 | 18 | ### Графики ![Сравнение производительности алгоритмов](maze_benchmark.png) На графике представлено сравнение трех алгоритмов по трем метрикам: время выполнения (мс), количество посещенных клеток и длина найденного пути. ## 5. Анализ результатов ### Сравнение характеристик алгоритмов | Характеристика | BFS | DFS | A* | |----------------|-----|-----|-----| | Гарантия кратчайшего пути | Да | Нет | Да | | Скорость на малых лабиринтах | Средняя | Быстрая | Средняя | | Скорость на больших лабиринтах | Средняя | Быстрая | Средняя | | Потребление памяти | Высокое | Низкое | Среднее | | Количество посещенных клеток | Много (211) | Среднее (170) | Мало (103) | ### Детальный анализ по лабиринтам **Small 10x6:** - Все алгоритмы нашли оптимальный путь длиной 11 шагов - DFS оказался самым быстрым (0.017 мс) и посетил меньше всего клеток (17) - A* посетил больше клеток (24), но нашел оптимальный путь **Medium 10x10:** - Оптимальный путь - 16 шагов (BFS и A*) - DFS нашел путь длиной 16 (в данном случае совпал с оптимальным) - DFS снова самый быстрый (0.024 мс) и посетил 26 клеток против 42 у BFS **Large 20x20:** - BFS и A* нашли оптимальный путь (36 шагов) - **DFS нашел неоптимальный путь (100 шагов), что на 64 шага длиннее!** - A* посетил значительно меньше клеток (103 против 211 у BFS) - Это показывает преимущество эвристики A* на больших лабиринтах **Empty 15x15:** - Оптимальный путь - 25 шагов (по прямой) - DFS нашел путь длиной 97 шагов, что в 3.8 раза длиннее! - Все алгоритмы посетили одинаковое количество клеток (169) - весь лабиринт - A* показал самое большое время из-за накладных расходов на эвристику **No exit 10x10:** - Все алгоритмы обошли весь достижимый лабиринт - DFS посетил меньше клеток (28 против 45 у BFS) - Длина пути 18 показывает, что алгоритмы прошли до тупика ### Ключевые выводы 1. **BFS** - надежный выбор, когда гарантия кратчайшего пути критична. Работает предсказуемо, но на больших лабиринтах посещает много клеток (211 против 103 у A*) 2. **DFS** - самый быстрый алгоритм в большинстве тестов (0.017-0.211 мс), но **ненадежен для поиска оптимального пути**. В большом лабиринте путь оказался на 64% длиннее оптимального! 3. **A*** - лучший баланс. Находит кратчайший путь (как BFS), но посещает на 51% меньше клеток в большом лабиринте. Немного медленнее DFS из-за вычисления эвристики. ### Рекомендации по выбору алгоритма | Ситуация | Рекомендуемый алгоритм | Обоснование | |----------|----------------------|-------------| | Небольшой лабиринт (< 100 клеток) | Любой | Разница в производительности незначительна | | Большой лабиринт, нужен кратчайший путь | **A*** | Быстрее BFS, посещает меньше клеток | | Максимальная скорость, путь не важен | **DFS** | Самый быстрый, но может найти длинный путь | | Лабиринт неизвестной структуры | **A*** | Лучшее соотношение скорость/качество | ## 6. Заключение ### Преимущества использованных паттернов **Builder** позволил легко реализовать загрузку лабиринтов из текстовых файлов и оставил возможность для добавления других форматов без изменения основного кода. **Strategy** сделал алгоритмы поиска взаимозаменяемыми. Добавление нового алгоритма (например, Дейкстры) потребовало бы только создания нового класса. **Observer** отделил логику отображения от логики приложения, что упростило добавление новых видов визуализации. **Command** позволил реализовать пошаговое управление игроком с возможностью отмены действий без усложнения класса Walker. ### Итог Разработанная программа демонстрирует преимущества объектно-ориентированного подхода и использования паттернов проектирования. Код является гибким, расширяемым и легко поддерживаемым. Эксперименты показали, что **A*** является наиболее сбалансированным алгоритмом для поиска пути в лабиринте, обеспечивая оптимальный путь при приемлемой скорости работы и минимальном количестве посещенных клеток. DFS может быть полезен только когда скорость критична, а оптимальность пути не важна. BFS остается надежным выбором для небольших лабиринтов, где простота реализации важнее производительности.