forked from UNN/2026-rff_mp
100 lines
3.9 KiB
Python
100 lines
3.9 KiB
Python
|
|
import csv
|
|||
|
|
import time
|
|||
|
|
from typing import List, Dict
|
|||
|
|
from models import Maze
|
|||
|
|
from builders import TextFileMazeBuilder
|
|||
|
|
from strategies import BFSStrategy, DFSStrategy, AStarStrategy
|
|||
|
|
from solver import MazeSolver
|
|||
|
|
|
|||
|
|
|
|||
|
|
def run_experiment(maze: Maze, strategy_name: str, strategy, repeats: int = 5) -> Dict:
|
|||
|
|
"""Запускает эксперимент для одной стратегии"""
|
|||
|
|
times = []
|
|||
|
|
visited_counts = []
|
|||
|
|
path_lengths = []
|
|||
|
|
path_found = True
|
|||
|
|
|
|||
|
|
for _ in range(repeats):
|
|||
|
|
solver = MazeSolver(maze, strategy)
|
|||
|
|
path, stats = solver.solve()
|
|||
|
|
|
|||
|
|
times.append(stats.time_ms)
|
|||
|
|
visited_counts.append(stats.visited_cells)
|
|||
|
|
path_lengths.append(stats.path_length)
|
|||
|
|
path_found = stats.path_found
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
'strategy': strategy_name,
|
|||
|
|
'time_mean': sum(times) / len(times),
|
|||
|
|
'time_min': min(times),
|
|||
|
|
'time_max': max(times),
|
|||
|
|
'visited_mean': sum(visited_counts) / len(visited_counts),
|
|||
|
|
'path_length_mean': sum(path_lengths) / len(path_lengths) if path_found else 0,
|
|||
|
|
'path_found': path_found
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def run_all_experiments(maze_files: List[str], repeats: int = 5) -> List[Dict]:
|
|||
|
|
"""Запускает эксперименты для всех лабиринтов и стратегий"""
|
|||
|
|
builder = TextFileMazeBuilder()
|
|||
|
|
strategies = [
|
|||
|
|
('BFS', BFSStrategy()),
|
|||
|
|
('DFS', DFSStrategy()),
|
|||
|
|
('A*', AStarStrategy())
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
results = []
|
|||
|
|
|
|||
|
|
for maze_file in maze_files:
|
|||
|
|
try:
|
|||
|
|
maze = builder.build_from_file(maze_file)
|
|||
|
|
except (ValueError, FileNotFoundError) as e:
|
|||
|
|
print(f"❌ Ошибка: {e}")
|
|||
|
|
continue
|
|||
|
|
|
|||
|
|
print(f"\n📊 Лабиринт: {maze_file}")
|
|||
|
|
print(f" Размер: {maze.width}×{maze.height}")
|
|||
|
|
print(f" Старт: ({maze.start.x}, {maze.start.y})")
|
|||
|
|
print(f" Выход: ({maze.exit.x}, {maze.exit.y})")
|
|||
|
|
|
|||
|
|
for strategy_name, strategy in strategies:
|
|||
|
|
print(f" 🧪 Тестирование: {strategy_name}")
|
|||
|
|
result = run_experiment(maze, strategy_name, strategy, repeats)
|
|||
|
|
result['maze_file'] = maze_file.split('/')[-1]
|
|||
|
|
result['maze_size'] = f"{maze.width}×{maze.height}"
|
|||
|
|
results.append(result)
|
|||
|
|
|
|||
|
|
status = "✅" if result['path_found'] else "❌"
|
|||
|
|
print(f" {status} Время: {result['time_mean']:.2f} мс, "
|
|||
|
|
f"Посещено: {result['visited_mean']:.0f}, "
|
|||
|
|
f"Путь: {result['path_length_mean']:.0f}")
|
|||
|
|
|
|||
|
|
return results
|
|||
|
|
|
|||
|
|
|
|||
|
|
def save_results_to_csv(results: List[Dict], filename: str = "experiment_results.csv") -> None:
|
|||
|
|
"""Сохраняет результаты в CSV файл"""
|
|||
|
|
with open(filename, 'w', newline='', encoding='utf-8') as f:
|
|||
|
|
writer = csv.DictWriter(f, fieldnames=[
|
|||
|
|
'maze_file', 'maze_size', 'strategy',
|
|||
|
|
'time_mean', 'time_min', 'time_max',
|
|||
|
|
'visited_mean', 'path_length_mean', 'path_found'
|
|||
|
|
])
|
|||
|
|
writer.writeheader()
|
|||
|
|
writer.writerows(results)
|
|||
|
|
print(f"\n💾 Результаты сохранены в {filename}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def print_results_table(results: List[Dict]) -> None:
|
|||
|
|
"""Выводит результаты в виде таблицы"""
|
|||
|
|
print("\n" + "=" * 80)
|
|||
|
|
print("РЕЗУЛЬТАТЫ ЭКСПЕРИМЕНТОВ")
|
|||
|
|
print("=" * 80)
|
|||
|
|
|
|||
|
|
for res in results:
|
|||
|
|
print(f"\n📁 Лабиринт: {res['maze_file']}")
|
|||
|
|
print(f" 📐 Размер: {res['maze_size']}")
|
|||
|
|
print(f" 🎯 Стратегия: {res['strategy']}")
|
|||
|
|
print(f" ⏱️ Время (ср): {res['time_mean']:.2f} мс")
|
|||
|
|
print(f" 📍 Посещено: {res['visited_mean']:.0f} клеток")
|
|||
|
|
print(f" 🛤️ Длина пути: {res['path_length_mean']:.0f}")
|