2026-rff_mp/pogodinda/lab2/src/experiments.py

240 lines
8.7 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.

import os
import random
import time
import csv
from typing import Dict, List
import matplotlib.pyplot as plt
import numpy as np
from maze import Maze, Cell
from maze_builder import RandomMazeBuilder
from pathfinding import BFSStrategy, DFSStrategy, AStarStrategy, DijkstraStrategy
from maze_solver import MazeSolver, SearchStats
def create_test_mazes() -> Dict[str, Maze]:
"""Создаёт тестовые лабиринты разной сложности."""
mazes = {}
# 1. Маленький 10×10 с простым путём
small = Maze(10, 10)
for y in range(10):
for x in range(10):
is_wall = (x == 0 or x == 9 or y == 0 or y == 9 or
(x == 3 and y < 7) or (x == 6 and y > 2))
is_start = (x == 1 and y == 1)
is_exit = (x == 8 and y == 8)
small.set_cell(x, y, Cell(x, y, is_wall=is_wall,
is_start=is_start, is_exit=is_exit))
mazes['small_10x10'] = small
# 2. Средний 50×50 — случайный
mazes['medium_50x50'] = RandomMazeBuilder(51, 51).build_from_file()
# 3. Большой 100×100 — случайный
mazes['large_100x100'] = RandomMazeBuilder(101, 101).build_from_file()
# 4. Пустой 20×20 — без стен
empty = Maze(20, 20)
for y in range(20):
for x in range(20):
empty.set_cell(x, y, Cell(x, y, is_wall=False,
is_start=(x==1 and y==1),
is_exit=(x==18 and y==18)))
mazes['empty_20x20'] = empty
# 5. Без выхода — проверка обработки
no_exit = Maze(10, 10)
for y in range(10):
for x in range(10):
is_wall = (x == 0 or x == 9 or y == 0 or y == 9 or x == 5)
no_exit.set_cell(x, y, Cell(x, y, is_wall=is_wall,
is_start=(x==1 and y==1)))
mazes['no_exit_10x10'] = no_exit
# 6. Взвешенный 15×15
weighted = Maze(15, 15)
for y in range(15):
for x in range(15):
is_wall = (x == 0 or x == 14 or y == 0 or y == 14 or
(x == 5 and y != 7) or (x == 10 and y != 3))
weight = 1
if 3 <= x <= 7 and 3 <= y <= 7:
weight = 2 # песок
elif 8 <= x <= 12 and 8 <= y <= 12:
weight = 3 # болото
weighted.set_cell(x, y, Cell(x, y,
is_wall=is_wall,
is_start=(x==1 and y==1),
is_exit=(x==13 and y==13),
weight=weight))
mazes['weighted_15x15'] = weighted
return mazes
def run_experiments(mazes: Dict[str, Maze],
strategies: List,
runs_per_test: int = 5) -> List[SearchStats]:
"""Запускает эксперименты, возвращает список статистик."""
results = []
for maze_name, maze in mazes.items():
print(f"\n{'='*60}")
print(f"Лабиринт: {maze_name} ({maze.width}x{maze.height})")
print(f"{'='*60}")
for strategy in strategies:
print(f"\nАлгоритм: {strategy.get_name()}")
times = []
visited_list = []
path_lens = []
solver = MazeSolver(maze, strategy)
for run in range(runs_per_test):
stats = solver.solve(maze_name)
times.append(stats.time_ms)
visited_list.append(stats.visited_cells)
path_lens.append(stats.path_length)
print(f" Запуск {run+1}: {stats.time_ms:.4f} мс, "
f"посещено: {stats.visited_cells}, "
f"длина: {stats.path_length}")
# Средние значения
avg = SearchStats(
time_ms=np.mean(times),
visited_cells=int(np.mean(visited_list)),
path_length=int(np.mean(path_lens)),
algorithm_name=strategy.get_name(),
maze_name=maze_name
)
results.append(avg)
print(f" СРЕДНЕЕ: {avg.time_ms:.4f} мс, "
f"посещено: {avg.visited_cells}, "
f"длина: {avg.path_length}")
return results
def save_csv(results: List[SearchStats], filename: str):
"""Сохраняет результаты в CSV."""
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Лабиринт', 'Алгоритм', 'Время_мс',
'Посещено_клеток', 'Длина_пути'])
for r in results:
writer.writerow([
r.maze_name, r.algorithm_name,
f"{r.time_ms:.6f}", r.visited_cells, r.path_length
])
print(f"\nCSV сохранён: {filename}")
def create_plots(results: List[SearchStats], output_dir: str = "."):
"""Создаёт графики сравнения."""
os.makedirs(output_dir, exist_ok=True)
maze_names = sorted(set(r.maze_name for r in results))
algorithms = sorted(set(r.algorithm_name for r in results))
# 1. Общее сравнение по лабиринтам
fig, axes = plt.subplots(2, 3, figsize=(18, 12))
fig.suptitle('Сравнение алгоритмов поиска пути', fontsize=16)
for idx, maze_name in enumerate(maze_names):
ax = axes[idx // 3, idx % 3]
maze_res = [r for r in results if r.maze_name == maze_name]
names = [r.algorithm_name for r in maze_res]
times = [r.time_ms for r in maze_res]
visited = [r.visited_cells for r in maze_res]
paths = [r.path_length for r in maze_res]
x = np.arange(len(names))
w = 0.25
ax.bar(x - w, times, w, label='Время (мс)', color='skyblue')
ax.bar(x, [v/10 for v in visited], w,
label='Посещено (÷10)', color='lightcoral')
ax.bar(x + w, paths, w, label='Длина пути', color='lightgreen')
ax.set_title(maze_name)
ax.set_xticks(x)
ax.set_xticklabels(names, rotation=45)
ax.legend(fontsize=8)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig(f'{output_dir}/maze_comparison.png', dpi=150)
plt.close()
# 2. Посещённые клетки
fig, ax = plt.subplots(figsize=(14, 8))
for algo in algorithms:
algo_res = [r for r in results if r.algorithm_name == algo]
names = [r.maze_name for r in algo_res]
vals = [r.visited_cells for r in algo_res]
ax.plot(names, vals, marker='o', label=algo, linewidth=2)
ax.set_title('Посещённые клетки по лабиринтам', fontsize=14)
ax.set_xlabel('Лабиринт')
ax.set_ylabel('Клетки')
ax.legend()
ax.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig(f'{output_dir}/visited_cells.png', dpi=150)
plt.close()
# 3. Время выполнения (логарифмическая шкала)
fig, ax = plt.subplots(figsize=(14, 8))
for algo in algorithms:
algo_res = [r for r in results if r.algorithm_name == algo]
names = [r.maze_name for r in algo_res]
vals = [max(r.time_ms, 0.001) for r in algo_res]
ax.plot(names, vals, marker='s', label=algo, linewidth=2)
ax.set_title('Время выполнения (лог. шкала)', fontsize=14)
ax.set_xlabel('Лабиринт')
ax.set_ylabel('Время (мс)')
ax.set_yscale('log')
ax.legend()
ax.grid(True, alpha=0.3, which='both')
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig(f'{output_dir}/time_comparison.png', dpi=150)
plt.close()
print(f"Графики сохранены в {output_dir}/")
if __name__ == "__main__":
print("=" * 70)
print("ЭКСПЕРИМЕНТАЛЬНАЯ ЧАСТЬ")
print("=" * 70)
# Создаём лабиринты
mazes = create_test_mazes()
# Алгоритмы для сравнения
strategies = [BFSStrategy(), DFSStrategy(),
AStarStrategy(), DijkstraStrategy()]
# Запускаем эксперименты
results = run_experiments(mazes, strategies, runs_per_test=5)
# Сохраняем CSV
save_csv(results, "experiment_results.csv")
# Строим графики
create_plots(results)
print("\n" + "=" * 70)
print("ГОТОВО!")
print("=" * 70)