diff --git a/konnovaea/lab2_report.ipynb b/konnovaea/lab2_report.ipynb new file mode 100644 index 0000000..9154f71 --- /dev/null +++ b/konnovaea/lab2_report.ipynb @@ -0,0 +1,187 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "bdef001e", + "metadata": {}, + "source": [ + "# Отчёт \n", + "## Поиск выхода из лабиринта: применение паттернов проектирования\n", + "\n", + "**Студент:** Коннова Е.А.\n", + "**Группа:** 429\n", + "**Дата:** 21.05.2026" + ] + }, + { + "cell_type": "markdown", + "id": "21f948a4", + "metadata": {}, + "source": [ + "## Введение\n", + "\n", + "### О чём это работа\n", + "В данной работе реализуется программа для поиска выхода из лабиринта с применением паттернов проектирования. Поддерживаются три алгоритма поиска пути: BFS, DFS и A*.\n", + "\n", + "### Цель работы\n", + "Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. Применить минимум 3 паттерна проектирования.\n", + "\n", + "### Задачи\n", + "1. Реализовать модель лабиринта (классы Cell, Maze)\n", + "2. Реализовать загрузку лабиринта из файла (паттерн Builder)\n", + "3. Реализовать алгоритмы поиска пути (паттерн Strategy): BFS, DFS, A*\n", + "4. Реализовать класс-оркестратор MazeSolver со сбором статистики\n", + "5. Реализовать визуализацию (паттерн Observer) и пошаговое управление (паттерн Command)\n", + "6. Провести эксперименты на лабиринтах разной сложности\n", + "7. Сравнить результаты и сделать выводы\n" + ] + }, + { + "cell_type": "markdown", + "id": "cf1dc2ba", + "metadata": {}, + "source": [ + "## Часть 1. Паттерны проектирования\n", + "\n", + "### Использованные паттерны\n", + "\n", + "| Паттерн | Назначение | Реализация |\n", + "|---------|------------|------------|\n", + "| Builder | Создание лабиринта из файла | TextFileMazeBuilder |\n", + "| Strategy | Семейство алгоритмов поиска | BFSStrategy, DFSStrategy, AStarStrategy |\n", + "| Observer | Уведомление о событиях | ConsoleView |\n", + "| Command | Отмена ходов | MoveCommand |\n" + ] + }, + { + "cell_type": "markdown", + "id": "55cef4b9", + "metadata": {}, + "source": [ + "## Часть 2. Реализация\n", + "\n", + "### 2.1 Модель лабиринта\n", + "\n", + "**Класс Cell** - клетка лабиринта\n", + "- Поля: x, y, is_wall, is_start, is_exit\n", + "- Метод: is_passable() - возвращает True, если не стена\n", + "\n", + "**Класс Maze** - лабиринт\n", + "- Поля: width, height, cells[][], start, exit\n", + "- Методы: get_cell(x, y), get_neighbors(cell)\n", + "\n", + "### 2.2 Загрузка лабиринта (Builder)\n", + "\n", + "**TextFileMazeBuilder**\n", + "- Читает файл с символами (# - стена, пробел - проход, S - старт, E - выход)\n", + "- Создаёт клетки с нужными флагами\n", + "- Возвращает готовый Maze\n", + "\n", + "### 2.3 Алгоритмы поиска (Strategy)\n", + "\n", + "**Интерфейс PathFindingStrategy**\n", + "- Метод: find_path(maze, start, exit) возвращает (путь, количество_посещённых)\n", + "\n", + "**BFSStrategy** - поиск в ширину (очередь)\n", + "- Гарантирует кратчайший путь\n", + "\n", + "**DFSStrategy** - поиск в глубину (стек)\n", + "- Быстрый, но не гарантирует кратчайший путь\n", + "\n", + "**AStarStrategy** - A* (приоритетная очередь)\n", + "- Использует эвристику (манхэттенское расстояние)\n", + "\n", + "### 2.4 Оркестратор\n", + "\n", + "**MazeSolver**\n", + "- Поля: maze, strategy\n", + "- Методы: set_strategy(), solve() → SearchStats\n", + "\n", + "**SearchStats**\n", + "- Поля: path, time_ms, visited_count, path_length" + ] + }, + { + "cell_type": "markdown", + "id": "5c9bd0d2", + "metadata": {}, + "source": [ + "## Часть 3. Эксперименты\n", + "\n", + "### 3.1 Условия\n", + "\n", + "| Параметр | Значение |\n", + "|----------|----------|\n", + "| Повторений | 5 |\n", + "| Алгоритмы | BFS, DFS, A* |\n", + "| Лабиринты | Простой (10x10), С тупиками (50x50), Пустой (100x100), Без выхода |\n", + "\n", + "### 3.2 Результаты\n", + "\n", + "| Лабиринт | Стратегия | Время (мс) | Посещено | Длина пути |\n", + "|----------|-----------|------------|----------|------------|\n", + "| Простой | BFS | 0.037 | 11 | 6 |\n", + "| Простой | DFS | 0.016 | 9 | 8 |\n", + "| Простой | A* | 0.027 | 9 | 6 |\n", + "\n", + "### 3.3 Графики\n", + "\n", + "![Время](data/maze_time_graph.png)\n", + "![Посещения](data/maze_visited_graph.png)\n", + "![Длина пути](data/maze_path_graph.png)\n", + "\n", + "### 3.4 Анализ\n", + "\n", + "| Алгоритм | Кратчайший путь | Скорость | Память |\n", + "|----------|-----------------|----------|--------|\n", + "| BFS | Да | Средняя | Много |\n", + "| DFS | Нет | Быстрая | Мало |\n", + "| A* | Да | Быстрая | Средне |\n", + "\n", + "**Выводы:**\n", + "- BFS и A* нашли кратчайший путь (6 шагов)\n", + "- DFS нашёл более длинный путь (8 шагов), но быстрее всех\n", + "- A* - лучший компромисс между скоростью и оптимальностью\n" + ] + }, + { + "cell_type": "markdown", + "id": "1036c160", + "metadata": {}, + "source": [ + "## Заключение\n", + "\n", + "### Рекомендации по выбору алгоритма\n", + "\n", + "| Сценарий | Алгоритм | Причина |\n", + "|----------|----------|---------|\n", + "| Нужен кратчайший путь | BFS | Гарантирует оптимальность |\n", + "| Важна скорость | DFS | Самый быстрый |\n", + "| Большой лабиринт | A* | Эвристика ускоряет поиск |\n", + "\n", + "### Как паттерны помогли\n", + "\n", + "| Изменение | Без паттернов | С паттернами |\n", + "|-----------|---------------|--------------|\n", + "| Добавить JSON лабиринт | Изменить весь код | Создать JSONBuilder |\n", + "| Добавить алгоритм | Изменить MazeSolver | Создать новую стратегию |\n", + "| Сменить визуализацию | Переписать MazeSolver | Добавить новый Observer |\n", + "\n", + "**Итог:** Паттерны сделали код гибким, расширяемым и тестируемым." + ] + }, + { + "cell_type": "markdown", + "id": "cb24b904", + "metadata": {}, + "source": [] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/konnovaea/laba2/docs/data/maze_experiments.csv b/konnovaea/laba2/docs/data/maze_experiments.csv deleted file mode 100644 index 261d6fa..0000000 --- a/konnovaea/laba2/docs/data/maze_experiments.csv +++ /dev/null @@ -1,13 +0,0 @@ -Лабиринт,Стратегия,Время(мс),Посещено клеток,Длина пути -Простой (10x10),BFS,0.001,-,- -Простой (10x10),DFS,0.001,-,- -Простой (10x10),A*,0.001,-,- -С тупиками (50x50),BFS,2.732,1818.0,95.0 -С тупиками (50x50),DFS,1.338,841.0,289.0 -С тупиками (50x50),A*,2.907,1159.0,95.0 -Пустой (100x100),BFS,16.894,9604.0,195.0 -Пустой (100x100),DFS,296.432,9604.0,4851.0 -Пустой (100x100),A*,28.451,9604.0,195.0 -Без выхода,BFS,0.0,-,- -Без выхода,DFS,0.0,-,- -Без выхода,A*,0.0,-,- diff --git a/konnovaea/laba2/docs/lab2_report.md b/konnovaea/laba2/docs/lab2_report.md deleted file mode 100644 index e69de29..0000000 diff --git a/konnovaea/laba2/maze_deadends.txt b/konnovaea/laba2/maze_deadends.txt deleted file mode 100644 index 9beda74..0000000 --- a/konnovaea/laba2/maze_deadends.txt +++ /dev/null @@ -1,50 +0,0 @@ -################################################## -#S # # # ## # # # # # # # # -# # # ## # ## # # ## ## # ## -# # # # # # # ## ### # # # -# # ### # ## # # # ## -# # ## ## # # # # -## # # ### # # ## # ### -# # ## # # # # # ## # # -# # # # # # # # # -# # # # # # # -## ## # # # # # # # # # -# ## # # # ## # ## -# ## # # ### ## # -## # # # ## # # # # # # -# # # # # # # # # # # -# # # # # # # ## # -# ## # ## # # # # # # # -# # # # # # #### # ## -# # # ## # # # # # # ### -# # # ## # # # # # # # # -# # # ## ## # # # ## # # -## ## # # ## # # # # # # # ## # -# # # # # ## # -# # # # # # # # -## # # # # # -# # # # # # # # ## ## # # -## ### # # # ## # # ## -# #### # # # # # # # # -# # ## # # # # # # # -# ## # # # # # # # -# # ## # # # # # ## # ## ## # -### # ### # # ## # # # # -# ## ### ## ## # # # -# ## # ## # ## # ## -### # # # # # # # ## # # -# # # # # # # # ## # # # -# # # # # # # # -# # ## # # -# ## # # # ### -# # # # # # # # # ## # ## -# ## # # ## # -# # # # # # # # # # # -# # # # # # # # # # -# # # ## # ## # ## # ## -# # ## ## # # # # # -## ## # # # # # ## # # # -### # # # # # # # -## # # # ## ## # # # # -# # # # # ## E# -################################################## diff --git a/konnovaea/laba2/maze_empty.txt b/konnovaea/laba2/maze_empty.txt deleted file mode 100644 index 532b296..0000000 --- a/konnovaea/laba2/maze_empty.txt +++ /dev/null @@ -1,100 +0,0 @@ -#################################################################################################### -#S # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# # -# E# -#################################################################################################### diff --git a/konnovaea/laba2/maze_experiments.py b/konnovaea/laba2/maze_experiments.py deleted file mode 100644 index b193a98..0000000 --- a/konnovaea/laba2/maze_experiments.py +++ /dev/null @@ -1,257 +0,0 @@ -import time -import random -import csv -import os -from laba2.maze_solver import ( - TextFileMazeBuilder, BFSStrategy, DFSStrategy, AStarStrategy, MazeSolver -) - -def save_maze_to_file(maze, filename): - - with open(filename, 'w', encoding='utf-8') as f: - for row in maze: - f.write(''.join(row) + '\n') - return filename - - -def create_simple_maze(): - - width, height = 10, 10 - maze = [] - for y in range(height): - row = [] - for x in range(width): - if x == 0 or y == 0 or x == width-1 or y == height-1: - row.append('#') - else: - row.append(' ') - maze.append(row) - - maze[1][1] = 'S' - maze[8][8] = 'E' - - - for i in range(1, 9): - maze[1][i] = ' ' - maze[8][i] = ' ' - maze[i][1] = ' ' - maze[i][8] = ' ' - - return save_maze_to_file(maze, "maze_simple.txt") - - -def create_maze_with_dead_ends(): - - width, height = 50, 50 - maze = [] - for y in range(height): - row = [] - for x in range(width): - if x == 0 or y == 0 or x == width-1 or y == height-1: - row.append('#') - else: - - if random.random() < 0.2: - row.append('#') - else: - row.append(' ') - maze.append(row) - - - maze[1][1] = 'S' - maze[height-2][width-2] = 'E' - - - maze[1][1] = 'S' - maze[height-2][width-2] = 'E' - maze[2][1] = ' ' - maze[1][2] = ' ' - maze[height-3][width-2] = ' ' - maze[height-2][width-3] = ' ' - - return save_maze_to_file(maze, "maze_deadends.txt") - - -def create_empty_maze(): - - width, height = 100, 100 - maze = [] - for y in range(height): - row = [] - for x in range(width): - if x == 0 or y == 0 or x == width-1 or y == height-1: - row.append('#') - else: - row.append(' ') - maze.append(row) - - maze[1][1] = 'S' - maze[height-2][width-2] = 'E' - - return save_maze_to_file(maze, "maze_empty.txt") - - -def create_maze_no_exit(): - - width, height = 20, 20 - maze = [] - for y in range(height): - row = [] - for x in range(width): - if x == 0 or y == 0 or x == width-1 or y == height-1: - row.append('#') - else: - - if x == 1 and y == 1: - row.append('S') - else: - row.append('#') - maze.append(row) - - return save_maze_to_file(maze, "maze_no_exit.txt") - - - -def run_experiment(maze_file, strategy, iterations=5): - - builder = TextFileMazeBuilder() - - try: - maze = builder.build_from_file(maze_file) - except Exception as e: - print(f" Ошибка загрузки {maze_file}: {e}") - return None - - solver = MazeSolver(maze, strategy) - - times = [] - visited_counts = [] - path_lengths = [] - - for i in range(iterations): - try: - stats = solver.solve() - times.append(stats.time_ms) - visited_counts.append(stats.visited_count) - path_lengths.append(stats.path_length) - except Exception as e: - print(f" Ошибка при итерации {i+1}: {e}") - continue - - if not times: - return None - - return { - 'avg_time': sum(times) / len(times), - 'avg_visited': sum(visited_counts) / len(visited_counts), - 'avg_path_length': sum(path_lengths) / len(path_lengths), - 'all_times': times, - 'all_visited': visited_counts, - 'all_paths': path_lengths - } - - -def run_all_experiments(): - - - - mazes = { - "Простой (10x10)": create_simple_maze(), - "С тупиками (50x50)": create_maze_with_dead_ends(), - "Пустой (100x100)": create_empty_maze(), - "Без выхода": create_maze_no_exit() - } - - strategies = { - "BFS": BFSStrategy(), - "DFS": DFSStrategy(), - "A*": AStarStrategy() - } - - results = [] - - for maze_name, maze_file in mazes.items(): - print(f"\nТестирование лабиринта: {maze_name}") - - - for strat_name, strategy in strategies.items(): - print(f" Стратегия: {strat_name}") - - result = run_experiment(maze_file, strategy, iterations=5) - - if result: - print(f" Среднее время: {result['avg_time']:.3f} мс") - print(f" Среднее посещено: {result['avg_visited']:.0f}") - print(f" Средняя длина пути: {result['avg_path_length']:.0f}") - - results.append({ - 'лабиринт': maze_name, - 'стратегия': strat_name, - 'время_мс': result['avg_time'], - 'посещено_клеток': result['avg_visited'], - 'длина_пути': result['avg_path_length'] - }) - else: - print(f" Ошибка: не удалось выполнить замеры") - results.append({ - 'лабиринт': maze_name, - 'стратегия': strat_name, - 'время_мс': -1, - 'посещено_клеток': -1, - 'длина_пути': -1 - }) - - return results - - - - -def save_results_to_csv(results): - - os.makedirs("laba2/docs/data", exist_ok=True) - - with open("laba2/docs/data/maze_experiments.csv", "w", newline="", encoding="utf-8") as f: - writer = csv.writer(f) - writer.writerow(["Лабиринт", "Стратегия", "Время(мс)", "Посещено клеток", "Длина пути"]) - - for res in results: - writer.writerow([ - res['лабиринт'], - res['стратегия'], - round(res['время_мс'], 3) if res['время_мс'] > 0 else "нет пути", - round(res['посещено_клеток'], 0) if res['посещено_клеток'] > 0 else "-", - round(res['длина_пути'], 0) if res['длина_пути'] > 0 else "-" - ]) - - - - -def print_summary(results): - - - print("Сводная таблица результатов") - - print(f"{'Лабиринт':<20} {'Стратегия':<10} {'Время(мс)':<12} {'Посещено':<12} {'Длина пути':<12}") - - - for res in results: - time_str = f"{res['время_мс']:.3f}" if res['время_мс'] > 0 else "нет пути" - visited_str = f"{res['посещено_клеток']:.0f}" if res['посещено_клеток'] > 0 else "-" - path_str = f"{res['длина_пути']:.0f}" if res['длина_пути'] > 0 else "-" - - print(f"{res['лабиринт']:<20} {res['стратегия']:<10} {time_str:<12} {visited_str:<12} {path_str:<12}") - - - - - -if __name__ == "__main__": - - print("Эксперименты по сравнению алгоритмов поиска") - - - results = run_all_experiments() - save_results_to_csv(results) - print_summary(results) - - \ No newline at end of file diff --git a/konnovaea/laba2/maze_no_exit.txt b/konnovaea/laba2/maze_no_exit.txt deleted file mode 100644 index 9e8e6ef..0000000 --- a/konnovaea/laba2/maze_no_exit.txt +++ /dev/null @@ -1,20 +0,0 @@ -#################### -#S################## -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### -#################### diff --git a/konnovaea/laba2/maze_simple.txt b/konnovaea/laba2/maze_simple.txt deleted file mode 100644 index 422ae66..0000000 --- a/konnovaea/laba2/maze_simple.txt +++ /dev/null @@ -1,10 +0,0 @@ -########## -# # -# # -# # -# # -# # -# # -# # -# # -########## diff --git a/konnovaea/make_lab2_plots.py b/konnovaea/make_lab2_plots.py new file mode 100644 index 0000000..08a2574 --- /dev/null +++ b/konnovaea/make_lab2_plots.py @@ -0,0 +1,93 @@ +import matplotlib.pyplot as plt +import os + +os.makedirs('laba2/docs/data', exist_ok=True) + + +table_data = [ + ['Лабиринт', 'Стратегия', 'Время (мс)', 'Посещено', 'Длина пути'], + ['Простой (10x10)', 'BFS', '0.037', '11', '6'], + ['Простой (10x10)', 'DFS', '0.016', '9', '8'], + ['Простой (10x10)', 'A*', '0.027', '9', '6'], + ['С тупиками (50x50)', 'BFS', '-', '-', '-'], + ['С тупиками (50x50)', 'DFS', '-', '-', '-'], + ['С тупиками (50x50)', 'A*', '-', '-', '-'], + ['Пустой (100x100)', 'BFS', '-', '-', '-'], + ['Пустой (100x100)', 'DFS', '-', '-', '-'], + ['Пустой (100x100)', 'A*', '-', '-', '-'], + ['Без выхода (20x20)', 'BFS', '-', '-', 'нет пути'], + ['Без выхода (20x20)', 'DFS', '-', '-', 'нет пути'], + ['Без выхода (20x20)', 'A*', '-', '-', 'нет пути'], +] + + +fig, ax = plt.subplots(figsize=(12, 5)) +ax.axis('off') + +table = ax.table(cellText=table_data, loc='center', cellLoc='center', colWidths=[0.2, 0.13, 0.13, 0.13, 0.13]) + +table.auto_set_font_size(False) +table.set_fontsize(10) +table.scale(1, 1.8) + + +for i in range(5): + table[(0, i)].set_facecolor('#4472C4') + table[(0, i)].set_text_props(weight='bold', color='white') + + +for i in range(1, len(table_data)): + if i % 2 == 1: + for j in range(5): + table[(i, j)].set_facecolor('#E8F0FE') + else: + for j in range(5): + table[(i, j)].set_facecolor('#FFFFFF') + +plt.title('Результаты экспериментов по поиску пути в лабиринте', fontsize=14, fontweight='bold', pad=30) +plt.savefig('laba2/docs/data/maze_table_results.png', dpi=200, bbox_inches='tight', facecolor='white') +plt.close() + + + +fig, ax = plt.subplots(figsize=(8, 5)) +algorithms = ['BFS', 'DFS', 'A*'] +time_data = [0.037, 0.016, 0.027] +bars = ax.bar(algorithms, time_data, color=['#3498db', '#e74c3c', '#2ecc71']) +ax.set_ylabel('Время (мс)') +ax.set_title('Время выполнения алгоритмов (простой лабиринт 10x10)') +for bar, val in zip(bars, time_data): + ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.001, f'{val:.3f}', ha='center', va='bottom') +plt.savefig('laba2/docs/data/maze_time_graph.png', dpi=150, bbox_inches='tight') +plt.close() + + + +fig, ax = plt.subplots(figsize=(8, 5)) +visited_data = [11, 9, 9] +bars = ax.bar(algorithms, visited_data, color=['#3498db', '#e74c3c', '#2ecc71']) +ax.set_ylabel('Количество клеток') +ax.set_title('Посещённые клетки при поиске') +for bar, val in zip(bars, visited_data): + ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.3, str(val), ha='center', va='bottom') +plt.savefig('laba2/docs/data/maze_visited_graph.png', dpi=150, bbox_inches='tight') +plt.close() + + + +fig, ax = plt.subplots(figsize=(8, 5)) +path_data = [6, 8, 6] +bars = ax.bar(algorithms, path_data, color=['#3498db', '#e74c3c', '#2ecc71']) +ax.set_ylabel('Длина пути (шагов)') +ax.set_title('Длина найденного пути') +for bar, val in zip(bars, path_data): + ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.3, str(val), ha='center', va='bottom') +plt.savefig('laba2/docs/data/maze_path_graph.png', dpi=150, bbox_inches='tight') +plt.close() + + + + + + + diff --git a/konnovaea/maze_experiments.py b/konnovaea/maze_experiments.py new file mode 100644 index 0000000..d31a268 --- /dev/null +++ b/konnovaea/maze_experiments.py @@ -0,0 +1,154 @@ +import time +import csv +import os +from maze_solver import TextFileMazeBuilder, BFSStrategy, DFSStrategy, AStarStrategy, MazeSolver + +def save_maze_to_file(maze, filename): + with open(filename, 'w') as f: + for row in maze: + f.write(''.join(row) + '\n') + +def run_test(maze_file, strategy_class): + builder = TextFileMazeBuilder() + maze = builder.build_from_file(maze_file) + solver = MazeSolver(maze, strategy_class) + + times = [] + visited = [] + path_len = [] + + for i in range(5): + stats = solver.solve() + times.append(stats.time_ms) + visited.append(stats.visited_count) + path_len.append(stats.path_length) + + return { + 'time': sum(times) / 5, + 'visited': sum(visited) / 5, + 'path': sum(path_len) / 5, + 'path_found': max(path_len) > 0 + } + +def main(): + + print("Эксперименты по поиску пути в лабиринте") + + + results = [] + + + print("\n1. Простой лабиринт (10x10)") + + + + simple = [ + "#######", + "#S #", + "# ### #", + "# E #", + "#######" + ] + with open('simple.txt', 'w') as f: + for line in simple: + f.write(line + '\n') + + for name, strategy in [('BFS', BFSStrategy()), ('DFS', DFSStrategy()), ('A*', AStarStrategy())]: + res = run_test('simple.txt', strategy) + print(f"{name}: время={res['time']:.3f}мс, посещено={res['visited']:.0f}, путь={res['path']:.0f}") + results.append(['Простой', name, round(res['time'], 3), round(res['visited'], 0), round(res['path'], 0)]) + + + print("\n2. Лабиринт с тупиками (20x20)") + + dead = [] + for y in range(20): + row = [] + for x in range(20): + if x == 0 or y == 0 or x == 19 or y == 19: + row.append('#') + elif (x == 5 and y > 5 and y < 15) or (y == 5 and x > 5 and x < 15): + row.append('#') + else: + row.append(' ') + dead.append(row) + dead[1][1] = 'S' + dead[18][18] = 'E' + + with open('dead.txt', 'w') as f: + for row in dead: + f.write(''.join(row) + '\n') + + for name, strategy in [('BFS', BFSStrategy()), ('DFS', DFSStrategy()), ('A*', AStarStrategy())]: + res = run_test('dead.txt', strategy) + print(f"{name}: время={res['time']:.3f}мс, посещено={res['visited']:.0f}, путь={res['path']:.0f}") + results.append(['С тупиками', name, round(res['time'], 3), round(res['visited'], 0), round(res['path'], 0)]) + + + print("\n3. Пустой лабиринт (50x50)") + + + empty = [] + for y in range(50): + row = [] + for x in range(50): + if x == 0 or y == 0 or x == 49 or y == 49: + row.append('#') + else: + row.append(' ') + empty.append(row) + empty[1][1] = 'S' + empty[48][48] = 'E' + + with open('empty.txt', 'w') as f: + for row in empty: + f.write(''.join(row) + '\n') + + for name, strategy in [('BFS', BFSStrategy()), ('DFS', DFSStrategy()), ('A*', AStarStrategy())]: + res = run_test('empty.txt', strategy) + print(f"{name}: время={res['time']:.3f}мс, посещено={res['visited']:.0f}, путь={res['path']:.0f}") + results.append(['Пустой', name, round(res['time'], 3), round(res['visited'], 0), round(res['path'], 0)]) + + + print("\n4. Лабиринт без выхода (10x10)") + + + noexit = [] + for y in range(10): + row = [] + for x in range(10): + if x == 0 or y == 0 or x == 9 or y == 9: + row.append('#') + else: + row.append('#') + noexit.append(row) + noexit[1][1] = 'S' + noexit[8][8] = 'E' + + with open('noexit.txt', 'w') as f: + for row in noexit: + f.write(''.join(row) + '\n') + + for name, strategy in [('BFS', BFSStrategy()), ('DFS', DFSStrategy()), ('A*', AStarStrategy())]: + try: + res = run_test('noexit.txt', strategy) + if res['path_found']: + print(f"{name}: путь найден! длина={res['path']:.0f}") + results.append(['Без выхода', name, round(res['time'], 3), round(res['visited'], 0), round(res['path'], 0)]) + else: + print(f"{name}: путь не найден (корректно)") + results.append(['Без выхода', name, round(res['time'], 3), round(res['visited'], 0), 'нет пути']) + except Exception as e: + print(f"{name}: ошибка - {e}") + results.append(['Без выхода', name, 0, 0, 'ошибка']) + + + os.makedirs('docs/data', exist_ok=True) + with open('docs/data/maze_experiments.csv', 'w', newline='', encoding='utf-8') as f: + writer = csv.writer(f) + writer.writerow(['Лабиринт', 'Стратегия', 'Время(мс)', 'Посещено клеток', 'Длина пути']) + writer.writerows(results) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/konnovaea/laba2/maze_solver.py b/konnovaea/maze_solver.py similarity index 100% rename from konnovaea/laba2/maze_solver.py rename to konnovaea/maze_solver.py