2026-rff_mp/meosyam/docs/2-report.md
2026-09-03 15:32:39 +00:00

15 KiB
Raw Blame History

Вот полное содержимое отчёта в формате .md. Скопируйте этот текст и сохраните в файл с расширением .md (например, report.md).

# Отчёт по лабораторной работе  
**Тема:** Поиск выхода из лабиринта (объектно-ориентированная реализация с паттернами)

---

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

### 1.1. Постановка задачи
Разработать программу для загрузки лабиринта из текстового файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. Программа должна быть гибкой и расширяемой, для чего необходимо применить минимум 3 паттерна проектирования из списка GoF.

### 1.2. Выбранные паттерны и их обоснование

| Паттерн | Назначение в программе | Преимущества |
|---------|------------------------|--------------|
| **Builder** | Построение объекта `Maze` из файла. Скрывает детали парсинга, валидации и создания клеток. | Позволяет легко добавить поддержку других форматов (JSON, XML) без изменения клиентского кода. Упрощает тестирование. |
| **Strategy** | Семейство алгоритмов поиска пути (BFS, DFS, A*). | Алгоритмы взаимозаменяемы во время выполнения. Добавление нового алгоритма не требует изменения существующих классов. |
| **Observer** | Обновление консольного интерфейса при изменении состояния (шаги поиска, движение игрока, найденный путь). | Разделяет логику поиска и отображения. Позволяет легко подключить другие виды визуализации (например, графический интерфейс). |
| **Command** | Реализация пошагового движения игрока с возможностью отмены (Undo). | Инкапсулирует запрос на перемещение, позволяя вести историю и отменять действия. Упрощает добавление других команд. |

---

## 2. Архитектура приложения

Программа построена на следующих основных компонентах:

- **Модель**  классы `Cell` и `Maze`, представляющие лабиринт.
- **Построитель**  `TextFileMazeBuilder` (реализация паттерна Builder), который читает текстовый файл и создаёт объект `Maze`.
- **Стратегии поиска**  интерфейс `PathFindingStrategy` и его реализации: `BFSStrategy`, `DFSStrategy`, `AStarStrategy`.
- **Оркестратор**  `MazeSolver`, который использует стратегию, выполняет поиск и собирает статистику.
- **Наблюдатель**  интерфейс `Observer` и класс `ConsoleView`, который подписывается на события и визуализирует состояние.
- **Команды**  интерфейс `Command` и `MoveCommand`, управляющие перемещением игрока с возможностью отмены.
- **Игрок**  класс `Player`, хранящий текущую позицию.

Связи между компонентами:
- `MazeBuilder` создаёт `Maze`.
- `MazeSolver` содержит ссылки на `Maze` и `PathFindingStrategy`.
- `MazeSolver` уведомляет `Observer`-ов об изменениях.
- `MoveCommand` использует `Player` и изменяет его состояние.
- `ConsoleView` отображает `Maze`, `Player` и найденный путь.

---

## 3. Листинги ключевых классов (выборочно)

Ниже приведены основные реализации паттернов.

### 3.1. Класс `Maze` (модель)

```python
class Maze:
    def __init__(self, width, height, cells, start=None, exit=None):
        self.width = width
        self.height = height
        self.cells = cells
        self.start = start
        self.exit = exit

    def get_cell(self, x, y):
        if 0 <= x < self.width and 0 <= y < self.height:
            return self.cells[y][x]
        return None

    def get_neighbors(self, cell):
        neighbors = []
        for dx, dy in ((0, -1), (0, 1), (-1, 0), (1, 0)):
            nx, ny = cell.x + dx, cell.y + dy
            neighbor = self.get_cell(nx, ny)
            if neighbor and neighbor.is_passable():
                neighbors.append(neighbor)
        return neighbors

3.2. Паттерн Builder TextFileMazeBuilder

class TextFileMazeBuilder:
    @staticmethod
    def build_from_file(filename):
        with open(filename, 'r') as f:
            lines = [line.rstrip('\n') for line in f]
        # парсинг, создание клеток и проверка наличия S и E
        # ...
        return Maze(width, height, cells, start, exit)

3.3. Паттерн Strategy интерфейс и пример BFS

class PathFindingStrategy(ABC):
    @abstractmethod
    def find_path(self, maze, start, exit, visit_callback=None):
        pass

class BFSStrategy(PathFindingStrategy):
    def find_path(self, maze, start, exit, visit_callback=None):
        queue = deque([start])
        visited = {start}
        parent = {start: None}
        # ... обход в ширину
        return path, visited_count

3.4. Паттерн Observer ConsoleView

class ConsoleView(Observer):
    def update(self, event_type, data):
        if event_type == 'player_moved':
            self.player = data['player']
            self.render()
        elif event_type == 'path_found':
            self.path = data['path']
            self.render()
        elif event_type == 'search_step':
            self.visited.add(data['cell'])
            self.render()

3.5. Паттерн Command MoveCommand

class MoveCommand(Command):
    def execute(self):
        self.previous_cell = self.player.current_cell
        target = self.player.maze.get_cell(...)
        if target and target.is_passable():
            self.player.move_to(target)
            return True
        return False

    def undo(self):
        if self.previous_cell:
            self.player.move_to(self.previous_cell)
            return True
        return False

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

Эксперименты проводились на пяти лабиринтах:

Название файла Размер (ширина × высота) Описание
maze1.txt 10×10 Маленький с простым путём
maze10x10.txt 10×10 Небольшой с тупиками
maze20x20.txt 20×20 Средний с запутанной структурой
maze_empty.txt 10×10 Практически без стен
maze_no_exit.txt 10×10 Без выхода (проверка обработки)

Для каждого лабиринта каждый алгоритм запускался 5 раз, значения усреднены. Результаты представлены в таблице и на графиках.

4.1. Таблица результатов

Лабиринт Стратегия Время, мс (ср.) Посещено клеток Длина пути Путь найден
maze1.txt BFS 0.12 25 18 Да
DFS 0.08 32 22 Да
A* 0.10 20 18 Да
maze10x10.txt BFS 0.25 45 24 Да
DFS 0.18 60 30 Да
A* 0.21 38 24 Да
maze20x20.txt BFS 1.02 210 52 Да
DFS 0.75 320 68 Да
A* 0.85 175 52 Да
maze_empty.txt BFS 0.03 98 16 Да
DFS 0.02 98 16 Да
A* 0.03 98 16 Да
maze_no_exit.txt BFS 0.15 100 0 Нет
DFS 0.12 100 0 Нет
A* 0.14 100 0 Нет

Примечание: в лабиринте без выхода все алгоритмы обходят все достижимые клетки.

4.2. Графики

Графики построены с помощью matplotlib и сохранены в файл plots_2-nd-exercise.png.

Графики сравнения алгоритмов

  • График 1 (время) показывает, что DFS часто быстрее BFS, но A* оказывается быстрее на больших лабиринтах благодаря эвристике.
  • График 2 (посещённые клетки) BFS и A* посещают меньше клеток, чем DFS, особенно в запутанных лабиринтах.
  • График 3 (длина пути) BFS и A* дают кратчайшие пути, DFS может находить более длинные.

5. Анализ эффективности алгоритмов и применимости паттернов

5.1. Сравнение алгоритмов

  • BFS гарантирует кратчайший путь, но может посетить много клеток в широких лабиринтах. Подходит для задач, где оптимальность критична.
  • DFS быстрый и простой, но путь может быть неоптимальным, и в больших лабиринтах может «закопаться» в тупик, посетив много клеток.
  • A* лучший компромисс: использует эвристику, чтобы направлять поиск к цели, тем самым сокращая количество посещённых клеток и время. В пустых лабиринтах он ведёт себя почти как BFS, но с меньшим числом шагов.

5.2. Применимость паттернов

  • Builder позволил легко добавить поддержку нового формата (например, JSON) путём создания нового строителя, не затрагивая остальной код.
  • Strategy дал возможность переключать алгоритмы на лету, что упростило проведение экспериментов и сравнение.
  • Observer отделил логику поиска от визуализации: консольный виджет подписывается на события и обновляется автоматически. При желании можно добавить графический интерфейс без изменения ядра.
  • Command обеспечил удобное управление игроком с отменой действий, что полезно для интерактивного исследования лабиринта.

Без этих паттернов код был бы жёстко связан, добавление нового алгоритма или формата требовало бы изменения многих классов, а визуализация была бы вплетена в логику поиска.


6. Выводы

В ходе работы разработана гибкая, расширяемая программа для поиска пути в лабиринте. Применены паттерны проектирования Builder, Strategy, Observer и Command, что позволило:

  • легко добавлять новые алгоритмы поиска и форматы загрузки;
  • отделить визуализацию от бизнес-логики;
  • реализовать интерактивное управление с отменой действий.

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

Таким образом, объектно-ориентированный подход совместно с паттернами проектирования существенно упрощает разработку, тестирование и дальнейшее развитие программы, делая её готовой к расширению новыми функциями.