11 KiB
Лабораторная работа: Поиск выхода из лабиринта
1. Постановка задачи
Требуется разработать приложение, которое загружает лабиринт из текстового файла, находит путь от стартовой клетки до выхода с возможностью выбора алгоритма поиска, отображает процесс и проводит экспериментальное сравнение алгоритмов.
Ключевые требования:
- Создать модель лабиринта (классы
Cell,Maze) - Реализовать загрузку лабиринта из файла с символами
#(стена),S(старт),E(выход) - Реализовать три алгоритма поиска: BFS, DFS, A*
- Создать класс-оркестратор
MazeSolverс возможностью смены стратегии - Собирать статистику: время выполнения, количество посещённых клеток, длина пути
- Провести эксперименты на лабиринтах разного размера и сложности
Применённые паттерны проектирования GoF:
1. Строитель (Builder)
- Где используется: классы
MazeBuilderиTextFileMazeBuilder - Обоснование: создание лабиринта из файла включает парсинг, валидацию и установку старта/выхода. Строитель скрывает эти детали и упрощает добавление новых форматов.
- Плюсы: новый формат файла требует только создания ещё одного строителя, не затрагивая остальные классы.
2. Стратегия (Strategy)
- Где используется: классы
PathFindingStrategy,BFSStrategy,DFSStrategy,AStarStrategy - Обоснование: алгоритмы поиска взаимозаменяемы и решают одну задачу разными способами. Стратегия позволяет менять алгоритм во время выполнения и легко добавлять новые.
- Плюсы: класс
MazeSolverможет использовать любую стратегию черезset_strategy. Новый алгоритм требует только создания нового класса.
3. Наблюдатель (Observer)
- Где используется: классы
ObserverиConsoleView - Обоснование: приложение должно обновлять консольный интерфейс при различных событиях. Наблюдатель отделяет логику отображения от логики приложения.
- Плюсы: легко добавить новые виды отображения без изменения основной логики.
4. Команда (Command)
- Где используется: классы
CommandиMoveCommand - Обоснование: для пошагового перемещения игрока с возможностью отмены действий. Команда инкапсулирует действие в объект и позволяет реализовать undo/redo.
- Плюсы: хранение истории действий и возможность отмены последних ходов без изменения класса
Player.
2. Архитектура приложения
Приложение состоит из следующих компонентов:
- Модель: классы
CellиMaze- представляют клетку и лабиринт. - Загрузка: классы
MazeBuilderиTextFileMazeBuilder- загрузка из файлов. - Алгоритмы: классы
BFSStrategy,DFSStrategy,AStarStrategy, реализующиеPathFindingStrategy. - Оркестрация: класс
MazeSolver, управляющий процессом поиска. - Визуализация: класс
ConsoleView, реализующийObserver. - Управление: классы
CommandиMoveCommandдля пошагового движения. - Игрок: класс
Player, хранящий текущую позицию.
3. Реализация алгоритмов поиска
BFS (поиск в ширину)
Использует очередь. Начинает со стартовой клетки, помещает её в очередь, затем циклически извлекает клетку из начала, проверяет, не является ли она выходом, и добавляет всех непосещённых соседей в конец. Гарантирует нахождение кратчайшего пути по числу шагов.
DFS (поиск в глубину)
Использует стек. Начинает со стартовой клетки, помещает её в стек, затем циклически извлекает клетку из конца, проверяет на выход и добавляет непосещённых соседей в стек. Не гарантирует кратчайший путь, но обычно быстрее и экономичнее по памяти.
A* (А звездочка)
Использует приоритетную очередь с эвристикой. Оценивает клетки по формуле f = g + h, где g - реальная стоимость пути от старта, h - эвристическое расстояние до выхода (манхэттенское расстояние). Находит кратчайший путь при допустимой эвристике и часто быстрее BFS.
4. Экспериментальная часть
Тестовые лабиринты
maze1.txt(10x6) простой лабиринт из задания.maze10x10.txt(10x10) лабиринт среднего размера со случайными стенами.maze20x20.txt(20x20) большой запутанный лабиринт.maze_empty.txt(15x15) пустой лабиринт без стен.maze_no_exit.txt(10x10) лабиринт без достижимого выхода.
Результаты замеров
Каждый эксперимент проводился 5 раз, значения усреднены.
| Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути |
|---|---|---|---|---|
| Small 10x6 | BFS | 0.040 | 27 | 14 |
| Small 10x6 | DFS | 0.025 | 27 | 18 |
| Small 10x6 | A* | 0.051 | 19 | 14 |
| Medium 10x10 | BFS | 0.023 | 19 | 12 |
| Medium 10x10 | DFS | 0.018 | 18 | 12 |
| Medium 10x10 | A* | 0.037 | 12 | 12 |
| Large 20x20 | BFS | 0.019 | 16 | 5 |
| Large 20x20 | DFS | 0.019 | 17 | 9 |
| Large 20x20 | A* | 0.023 | 9 | 5 |
| Empty 15x15 | BFS | 0.182 | 78 | 15 |
| Empty 15x15 | DFS | 0.069 | 76 | 43 |
| Empty 15x15 | A* | 0.156 | 63 | 15 |
| No exit 10x10 | BFS | 0 | ||
| No exit 10x10 | DFS | 0 | ||
| No exit 10x10 | A* | 0 |
Графики
На графике показано сравнение трёх алгоритмов по трём метрикам: время выполнения, количество посещённых клеток и длина найденного пути.
5. Анализ результатов
Сравнение характеристик
BFS:
- Гарантия кратчайшего пути: да
- Скорость на малых лабиринтах: средняя
- Скорость на больших лабиринтах: медленная
- Память: высокая
- Посещённых клеток: много
DFS:
- Гарантия кратчайшего пути: нет
- Скорость на малых лабиринтах: быстрая
- Скорость на больших лабиринтах: быстрая
- Память: низкая
- Посещённых клеток: мало
A:*
- Гарантия кратчайшего пути: да (при допустимой эвристике)
- Скорость на малых лабиринтах: быстрая
- Скорость на больших лабиринтах: средняя
- Память: средняя
- Посещённых клеток: среднее
Выводы
- BFS стабильно находит кратчайший путь, но на больших лабиринтах требует больше памяти и времени.
- DFS - самый быстрый и экономный, но путь может быть далёк от оптимального (в пустом лабиринте нашёл путь 43 вместо 15).
- A* показывает лучший баланс: находит кратчайший путь, как BFS, но при этом посещает меньше клеток и работает быстрее на больших лабиринтах.
- В лабиринте 20x20 все алгоритмы сработали быстро (0.019-0.023 мс), так как путь оказался очень коротким (5 шагов).
- При отсутствии пути все алгоритмы корректно обрабатывают ситуацию и возвращают пустой список.
6. Заключение
Использованные паттерны проектирования позволили создать гибкую и расширяемую архитектуру. Builder упростил загрузку лабиринтов, Strategy сделал алгоритмы взаимозаменяемыми, Observer отделил визуализацию от логики, а Command реализовал отмену действий.
Разработанная программа успешно решает поставленную задачу. Эксперименты подтвердили, что A* является наиболее сбалансированным алгоритмом для поиска пути в лабиринте, обеспечивая оптимальный путь при приемлемой скорости.
