2026-rff_mp/osipovamd/maze_project/experiment.py
2026-05-27 22:49:24 +03:00

283 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Экспериментальный запуск для всех лабиринтов и алгоритмов
Создание CSV и графиков
"""
import os
import csv
from datetime import datetime
from maze_model import Maze
from maze_builder import TextFileMazeBuilder
from pathfinding_strategies import BFSStrategy, DFSStrategy, AStarStrategy
from maze_solver import MazeSolver, SearchStats
class ExperimentRunner:
def __init__(self):
self.all_results = []
self.labirints = {
'labirint1.txt': 'Маленький (10x10) с простым путём',
'labirint2.txt': 'Средний (50x50) с тупиками',
'labirint3.txt': 'Большой (100x100) запутанный',
'labirint4.txt': 'Пустой (20x20) без стен',
'labirint5.txt': 'Без выхода (20x20)'
}
self.strategies = [
BFSStrategy(),
DFSStrategy(),
AStarStrategy()
]
def run_all_experiments(self):
"""Запускает эксперименты для всех лабиринтов и алгоритмов"""
print("\n" + "="*70)
print("ЗАПУСК ЭКСПЕРИМЕНТОВ")
print("="*70)
builder = TextFileMazeBuilder()
for filename, description in self.labirints.items():
if not os.path.exists(filename):
print(f"\n⚠️ Файл {filename} не найден, пропускаем...")
continue
print(f"\n📁 Лабиринт: {description}")
print(f" Файл: {filename}")
print("-" * 50)
try:
maze = builder.build_from_file(filename)
maze_name = filename.replace('.txt', '')
for strategy in self.strategies:
print(f" Тестирование {strategy.get_name()}...", end=" ", flush=True)
solver = MazeSolver(maze, maze_name, strategy)
path, stats = solver.solve_with_stats()
self.all_results.append({
'лабиринт': description,
'стратегия': stats.algorithm_name,
'время_мс': stats.execution_time_ms,
'посещено_клеток': stats.visited_cells,
'длина_пути': stats.path_length,
'путь_найден': 'Да' if stats.path_found else 'Нет',
'размер': stats.maze_size
})
print(f"готово! время={stats.execution_time_ms:.3f}мс, путь={stats.path_length}")
except Exception as e:
print(f" ❌ Ошибка: {e}")
print("\n" + "="*70)
print("ЭКСПЕРИМЕНТЫ ЗАВЕРШЕНЫ")
print("="*70)
def save_to_csv(self, filename="experiment_results.csv"):
"""Сохраняет результаты в CSV"""
if not self.all_results:
print("Нет результатов для сохранения!")
return
with open(filename, 'w', newline='', encoding='utf-8-sig') as f:
fieldnames = ['лабиринт', 'стратегия', 'время_мс', 'посещено_клеток', 'длина_пути', 'путь_найден', 'размер']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(self.all_results)
print(f"\n✅ Результаты сохранены в {filename}")
# Показываем содержимое CSV
print("\n" + "="*70)
print("СОДЕРЖИМОЕ CSV ФАЙЛА:")
print("="*70)
with open(filename, 'r', encoding='utf-8-sig') as f:
print(f.read())
def create_charts(self):
"""Создаёт графики для каждого лабиринта"""
try:
import matplotlib.pyplot as plt
import numpy as np
print("\n" + "="*70)
print("ПОСТРОЕНИЕ ГРАФИКОВ")
print("="*70)
# Группируем результаты по лабиринтам
results_by_maze = {}
for result in self.all_results:
maze = result['лабиринт']
if maze not in results_by_maze:
results_by_maze[maze] = []
results_by_maze[maze].append(result)
# Для каждого лабиринта создаём отдельный график
for maze_name, maze_results in results_by_maze.items():
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
fig.suptitle(f'Сравнение алгоритмов: {maze_name}', fontsize=14, fontweight='bold')
algorithms = [r['стратегия'] for r in maze_results]
# График 1: Время выполнения
times = [r['время_мс'] for r in maze_results]
bars1 = axes[0].bar(algorithms, times, color=['blue', 'green', 'red'])
axes[0].set_ylabel('Время (мс)')
axes[0].set_title('Время выполнения')
axes[0].tick_params(axis='x', rotation=15)
for bar, val in zip(bars1, times):
axes[0].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# График 2: Посещённые клетки
visited = [r['посещено_клеток'] for r in maze_results]
bars2 = axes[1].bar(algorithms, visited, color=['blue', 'green', 'red'])
axes[1].set_ylabel('Количество клеток')
axes[1].set_title('Посещённые клетки')
axes[1].tick_params(axis='x', rotation=15)
for bar, val in zip(bars2, visited):
axes[1].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{val:.0f}', ha='center', va='bottom', fontsize=9)
# График 3: Длина пути
lengths = [r['длина_пути'] for r in maze_results]
bars3 = axes[2].bar(algorithms, lengths, color=['blue', 'green', 'red'])
axes[2].set_ylabel('Шагов')
axes[2].set_title('Длина найденного пути')
axes[2].tick_params(axis='x', rotation=15)
for bar, val in zip(bars3, lengths):
axes[2].text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
f'{val:.0f}', ha='center', va='bottom', fontsize=9)
plt.tight_layout()
# Сохраняем график
safe_name = maze_name.replace(' ', '_').replace('(', '').replace(')', '').replace('×', 'x')
filename = f"chart_{safe_name}.png"
plt.savefig(filename, dpi=150, bbox_inches='tight')
print(f"✅ Сохранён: {filename}")
plt.close()
# Общий сводный график
self._create_summary_chart()
except ImportError:
print("\n⚠️ Для построения графиков установите matplotlib:")
print(" pip install matplotlib numpy")
def _create_summary_chart(self):
"""Создаёт сводный график по всем лабиринтам"""
import matplotlib.pyplot as plt
import numpy as np
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle('Сводное сравнение алгоритмов по всем лабиринтам', fontsize=14, fontweight='bold')
# Получаем уникальные лабиринты и алгоритмы
mazes = list(set([r['лабиринт'] for r in self.all_results]))
algorithms = ['BFS (Поиск в ширину)', 'DFS (Поиск в глубину)', 'A* (A-Star)']
# 1. Время по лабиринтам
ax1 = axes[0, 0]
x = np.arange(len(mazes))
width = 0.25
for i, algo in enumerate(algorithms):
times = []
for maze in mazes:
result = next((r for r in self.all_results if r['лабиринт'] == maze and r['стратегия'] == algo), None)
times.append(result['время_мс'] if result else 0)
bars = ax1.bar(x + (i - 1) * width, times, width, label=algo)
for bar, val in zip(bars, times):
if val > 0:
ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
f'{val:.1f}', ha='center', va='bottom', fontsize=7)
ax1.set_xlabel('Лабиринт')
ax1.set_ylabel('Время (мс)')
ax1.set_title('Сравнение времени выполнения')
ax1.set_xticks(x)
ax1.set_xticklabels([m[:20] for m in mazes], rotation=45, ha='right')
ax1.legend()
# 2. Посещённые клетки
ax2 = axes[0, 1]
for i, algo in enumerate(algorithms):
visited = []
for maze in mazes:
result = next((r for r in self.all_results if r['лабиринт'] == maze and r['стратегия'] == algo), None)
visited.append(result['посещено_клеток'] if result else 0)
bars = ax2.bar(x + (i - 1) * width, visited, width, label=algo)
for bar, val in zip(bars, visited):
if val > 0:
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5,
f'{val:.0f}', ha='center', va='bottom', fontsize=7)
ax2.set_xlabel('Лабиринт')
ax2.set_ylabel('Посещённые клетки')
ax2.set_title('Сравнение посещённых клеток')
ax2.set_xticks(x)
ax2.set_xticklabels([m[:20] for m in mazes], rotation=45, ha='right')
ax2.legend()
# 3. Длина пути
ax3 = axes[1, 0]
for i, algo in enumerate(algorithms):
lengths = []
for maze in mazes:
result = next((r for r in self.all_results if r['лабиринт'] == maze and r['стратегия'] == algo), None)
lengths.append(result['длина_пути'] if result else 0)
bars = ax3.bar(x + (i - 1) * width, lengths, width, label=algo)
for bar, val in zip(bars, lengths):
if val > 0:
ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{val:.0f}', ha='center', va='bottom', fontsize=7)
ax3.set_xlabel('Лабиринт')
ax3.set_ylabel('Длина пути (шагов)')
ax3.set_title('Сравнение длины пути')
ax3.set_xticks(x)
ax3.set_xticklabels([m[:20] for m in mazes], rotation=45, ha='right')
ax3.legend()
# 4. Таблица результатов
ax4 = axes[1, 1]
ax4.axis('tight')
ax4.axis('off')
# Создаём таблицу
table_data = []
for maze in mazes:
row = [maze[:25]]
for algo in algorithms:
result = next((r for r in self.all_results if r['лабиринт'] == maze and r['стратегия'] == algo), None)
if result:
row.append(f"{result['время_мс']:.1f}мс")
else:
row.append("-")
table_data.append(row)
columns = ['Лабиринт', 'BFS', 'DFS', 'A*']
table = ax4.table(cellText=table_data, colLabels=columns, cellLoc='center', loc='center')
table.auto_set_font_size(False)
table.set_fontsize(9)
table.scale(1.2, 1.5)
ax4.set_title('Сводная таблица (время в мс)', fontsize=10)
plt.tight_layout()
plt.savefig('summary_chart.png', dpi=150, bbox_inches='tight')
print("Сохранён: summary_chart.png")
plt.close()
def main():
runner = ExperimentRunner()
runner.run_all_experiments()
runner.save_to_csv("experiment_results.csv")
runner.create_charts()
if __name__ == "__main__":
main()