177 lines
15 KiB
Markdown
177 lines
15 KiB
Markdown
# Отчет по лабораторной работе: Поиск выхода из лабиринта
|
||
|
||
## 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 |
|
||
|
||
### Графики
|
||
|
||

|
||
|
||
На графике представлено сравнение трех алгоритмов по трем метрикам: время выполнения (мс), количество посещенных клеток и длина найденного пути.
|
||
|
||
## 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 остается надежным выбором для небольших лабиринтов, где простота реализации важнее производительности. |