исправлены ошибки
This commit is contained in:
parent
d3212347dc
commit
1d5a0f1539
187
konnovaea/lab2_report.ipynb
Normal file
187
konnovaea/lab2_report.ipynb
Normal file
|
|
@ -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",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\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
|
||||||
|
}
|
||||||
|
|
@ -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,-,-
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
##################################################
|
|
||||||
#S # # # ## # # # # # # # #
|
|
||||||
# # # ## # ## # # ## ## # ##
|
|
||||||
# # # # # # # ## ### # # #
|
|
||||||
# # ### # ## # # # ##
|
|
||||||
# # ## ## # # # #
|
|
||||||
## # # ### # # ## # ###
|
|
||||||
# # ## # # # # # ## # #
|
|
||||||
# # # # # # # # #
|
|
||||||
# # # # # # #
|
|
||||||
## ## # # # # # # # # #
|
|
||||||
# ## # # # ## # ##
|
|
||||||
# ## # # ### ## #
|
|
||||||
## # # # ## # # # # # #
|
|
||||||
# # # # # # # # # # #
|
|
||||||
# # # # # # # ## #
|
|
||||||
# ## # ## # # # # # # #
|
|
||||||
# # # # # # #### # ##
|
|
||||||
# # # ## # # # # # # ###
|
|
||||||
# # # ## # # # # # # # #
|
|
||||||
# # # ## ## # # # ## # #
|
|
||||||
## ## # # ## # # # # # # # ## #
|
|
||||||
# # # # # ## #
|
|
||||||
# # # # # # # #
|
|
||||||
## # # # # #
|
|
||||||
# # # # # # # # ## ## # #
|
|
||||||
## ### # # # ## # # ##
|
|
||||||
# #### # # # # # # # #
|
|
||||||
# # ## # # # # # # #
|
|
||||||
# ## # # # # # # #
|
|
||||||
# # ## # # # # # ## # ## ## #
|
|
||||||
### # ### # # ## # # # #
|
|
||||||
# ## ### ## ## # # #
|
|
||||||
# ## # ## # ## # ##
|
|
||||||
### # # # # # # # ## # #
|
|
||||||
# # # # # # # # ## # # #
|
|
||||||
# # # # # # # #
|
|
||||||
# # ## # #
|
|
||||||
# ## # # # ###
|
|
||||||
# # # # # # # # # ## # ##
|
|
||||||
# ## # # ## #
|
|
||||||
# # # # # # # # # # #
|
|
||||||
# # # # # # # # # #
|
|
||||||
# # # ## # ## # ## # ##
|
|
||||||
# # ## ## # # # # #
|
|
||||||
## ## # # # # # ## # # #
|
|
||||||
### # # # # # # #
|
|
||||||
## # # # ## ## # # # #
|
|
||||||
# # # # # ## E#
|
|
||||||
##################################################
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
####################################################################################################
|
|
||||||
#S #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# E#
|
|
||||||
####################################################################################################
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
####################
|
|
||||||
#S##################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
####################
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
##########
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
# #
|
|
||||||
##########
|
|
||||||
93
konnovaea/make_lab2_plots.py
Normal file
93
konnovaea/make_lab2_plots.py
Normal file
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
154
konnovaea/maze_experiments.py
Normal file
154
konnovaea/maze_experiments.py
Normal file
|
|
@ -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()
|
||||||
Loading…
Reference in New Issue
Block a user