forked from UNN/2026-rff_mp
200 lines
6.2 KiB
Python
200 lines
6.2 KiB
Python
# experiments.py
|
|
import time
|
|
import csv
|
|
from typing import List, Dict
|
|
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.builder = TextFileMazeBuilder()
|
|
self.strategies = [
|
|
BFSStrategy(),
|
|
DFSStrategy(),
|
|
AStarStrategy(),
|
|
]
|
|
self.results: List[Dict] = []
|
|
|
|
def create_test_maze_file(self, filename: str, maze_data: List[str]) -> None:
|
|
with open(filename, 'w', encoding='utf-8') as f:
|
|
f.write('\n'.join(maze_data))
|
|
|
|
def generate_simple_maze(self) -> List[str]:
|
|
maze = [
|
|
"S E",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" ",
|
|
" "
|
|
]
|
|
return maze
|
|
|
|
def generate_complex_maze(self, size: int = 50) -> List[str]:
|
|
import random
|
|
random.seed(42)
|
|
|
|
maze = []
|
|
for y in range(size):
|
|
row = []
|
|
for x in range(size):
|
|
if (x == 0 and y == 0):
|
|
row.append('S')
|
|
elif (x == size - 1 and y == size - 1):
|
|
row.append('E')
|
|
elif random.random() < 0.3: # 30% стен
|
|
row.append('#')
|
|
else:
|
|
row.append(' ')
|
|
maze.append(''.join(row))
|
|
|
|
for i in range(size):
|
|
if maze[i][0] == '#':
|
|
row = list(maze[i])
|
|
row[0] = ' '
|
|
maze[i] = ''.join(row)
|
|
if maze[0][i] == '#':
|
|
row = list(maze[0])
|
|
row[i] = ' '
|
|
maze[0] = ''.join(row)
|
|
|
|
return maze
|
|
|
|
def generate_empty_maze(self, size: int = 50) -> List[str]:
|
|
maze = []
|
|
for y in range(size):
|
|
row = []
|
|
for x in range(size):
|
|
if x == 0 and y == 0:
|
|
row.append('S')
|
|
elif x == size - 1 and y == size - 1:
|
|
row.append('E')
|
|
else:
|
|
row.append(' ')
|
|
maze.append(''.join(row))
|
|
return maze
|
|
|
|
def generate_no_exit_maze(self, size: int = 20) -> List[str]:
|
|
maze = []
|
|
for y in range(size):
|
|
row = []
|
|
for x in range(size):
|
|
if x == 0 and y == 0:
|
|
row.append('S')
|
|
elif x == size - 1 and y == size - 1:
|
|
row.append('#') # Выход заблокирован
|
|
else:
|
|
row.append('#') # Всё стены
|
|
maze.append(''.join(row))
|
|
|
|
# выход в тупике
|
|
row = list(maze[size - 1])
|
|
row[size - 1] = 'E'
|
|
maze[size - 1] = ''.join(row)
|
|
|
|
return maze
|
|
|
|
def run_experiment(self, maze_name: str, maze_data: List[str],
|
|
num_runs: int = 5) -> List[Dict]:
|
|
filename = f"test_{maze_name}.txt"
|
|
self.create_test_maze_file(filename, maze_data)
|
|
|
|
maze = self.builder.build_from_file(filename)
|
|
results = []
|
|
|
|
for strategy in self.strategies:
|
|
solver = MazeSolver(maze, strategy)
|
|
|
|
times = []
|
|
path_lengths = []
|
|
|
|
for run in range(num_runs):
|
|
stats = solver.solve()
|
|
times.append(stats.time_ms)
|
|
path_lengths.append(stats.path_length)
|
|
|
|
avg_time = sum(times) / len(times)
|
|
avg_path_length = sum(path_lengths) / len(path_lengths)
|
|
|
|
result = {
|
|
'maze': maze_name,
|
|
'strategy': strategy.name,
|
|
'avg_time_ms': round(avg_time, 3),
|
|
'min_time_ms': round(min(times), 3),
|
|
'max_time_ms': round(max(times), 3),
|
|
'path_length': int(avg_path_length) if avg_path_length else 0,
|
|
'path_found': avg_path_length > 0
|
|
}
|
|
results.append(result)
|
|
|
|
print(f"{maze_name} - {strategy.name}: "
|
|
f"{avg_time:.3f} мс, путь: {int(avg_path_length)}")
|
|
|
|
return results
|
|
|
|
def run_all_experiments(self):
|
|
|
|
experiments = [
|
|
("simple_10x10", self.generate_simple_maze()),
|
|
("complex_50x50", self.generate_complex_maze(50)),
|
|
("large_100x100", self.generate_complex_maze(100)),
|
|
("empty_50x50", self.generate_empty_maze(50)),
|
|
("no_exit_20x20", self.generate_no_exit_maze(20))
|
|
]
|
|
|
|
all_results = []
|
|
|
|
for name, data in experiments:
|
|
print(f"\n Лабиринт: {name} ---")
|
|
results = self.run_experiment(name, data)
|
|
all_results.extend(results)
|
|
|
|
self.save_to_csv(all_results, "experiment_results.csv")
|
|
|
|
|
|
|
|
return all_results
|
|
|
|
def save_to_csv(self, results: List[Dict], filename: str):
|
|
if not results:
|
|
return
|
|
|
|
with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
|
|
fieldnames = results[0].keys()
|
|
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
|
writer.writeheader()
|
|
writer.writerows(results)
|
|
|
|
|
|
def print_analysis(results: List[Dict]):
|
|
|
|
# Группировка
|
|
mazes = set(r['maze'] for r in results)
|
|
|
|
for maze in sorted(mazes):
|
|
print(f"\nЛабиринт: {maze}")
|
|
print("-" * 40)
|
|
|
|
maze_results = [r for r in results if r['maze'] == maze]
|
|
|
|
#по времени
|
|
sorted_results = sorted(maze_results, key=lambda x: x['avg_time_ms'])
|
|
|
|
for r in sorted_results:
|
|
status = "✓" if r['path_found'] else "✗"
|
|
print(f" {status} {r['strategy']:8} | "
|
|
f"Время: {r['avg_time_ms']:8.3f} мс | "
|
|
f"Путь: {r['path_length']:4} шагов")
|
|
|
|
# Определяем лучший
|
|
fastest = sorted_results[0]
|
|
print(f"\n → Самый быстрый: {fastest['strategy']} "
|
|
f"({fastest['avg_time_ms']:.3f} мс)") |