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

10 KiB
Raw Blame History

Отчет по заданию: поиск выхода из лабиринта

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 Инкапсулирует ход игрока и поддерживает отмену хода.

Диаграмма классов:

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-графиков.

Пример запуска:

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.

Графики:

Лабиринт Время Посещенные клетки
Маленький
Средний
Большой
Пустой
Без пути

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.

Без этих паттернов код быстро стал бы монолитным: парсинг файла, поиск, статистика, печать и ручное управление оказались бы в одном месте. Тогда добавление нового формата, алгоритма или режима отображения требовало бы менять уже работающую логику.