diff --git a/semyanovra/scr/maze.py b/semyanovra/scr/maze.py index 56295a3..5f013ee 100644 --- a/semyanovra/scr/maze.py +++ b/semyanovra/scr/maze.py @@ -416,7 +416,145 @@ class MoveAction(Action): return False -# ----------------------------- Главный игровой цикл ----------------------------- +# ----------------------------- Эксперименты и статистика ----------------------------- +def run_benchmark(maze_file, algorithm, runs=5): + builder = TxtLabyrinthBuilder() + maze = builder.build_from_file(maze_file) + + total_time = 0.0 + total_visited = 0 + total_length = 0 + + for _ in range(runs): + solver = Pathfinder(maze) + solver.set_algorithm(algorithm) + stats = solver.solve() + if stats: + total_time += stats.time_ms + total_visited += stats.visited_cells + total_length += stats.path_length + + return { + 'time_ms': total_time / runs, + 'visited_cells': total_visited / runs, + 'path_length': total_length / runs + } + + +def generate_charts(results): + mazes = list(set(r['maze'] for r in results)) + alg_names = ['BFS', 'DFS', 'AStar'] + + fig, axes = plt.subplots(1, 3, figsize=(15, 5)) + + x = np.arange(len(mazes)) + width = 0.25 + + for i, alg in enumerate(alg_names): + times = [] + for m in mazes: + val = next((r['time_ms'] for r in results if r['maze'] == m and r['strategy'] == alg), 0) + times.append(val) + axes[0].bar(x + i * width, times, width, label=alg) + + axes[0].set_xlabel('Maze') + axes[0].set_ylabel('Time (ms)') + axes[0].set_title('Execution Time') + axes[0].set_xticks(x + width) + axes[0].set_xticklabels(mazes, rotation=45, ha='right') + axes[0].legend() + axes[0].grid(True, alpha=0.3) + + for i, alg in enumerate(alg_names): + visited = [] + for m in mazes: + val = next((r['visited_cells'] for r in results if r['maze'] == m and r['strategy'] == alg), 0) + visited.append(val) + axes[1].bar(x + i * width, visited, width, label=alg) + + axes[1].set_xlabel('Maze') + axes[1].set_ylabel('Visited Cells') + axes[1].set_title('Visited Nodes') + axes[1].set_xticks(x + width) + axes[1].set_xticklabels(mazes, rotation=45, ha='right') + axes[1].legend() + axes[1].grid(True, alpha=0.3) + + for i, alg in enumerate(alg_names): + lengths = [] + for m in mazes: + val = next((r['path_length'] for r in results if r['maze'] == m and r['strategy'] == alg), 0) + lengths.append(val) + axes[2].bar(x + i * width, lengths, width, label=alg) + + axes[2].set_xlabel('Maze') + axes[2].set_ylabel('Path Length') + axes[2].set_title('Optimality') + axes[2].set_xticks(x + width) + axes[2].set_xticklabels(mazes, rotation=45, ha='right') + axes[2].legend() + axes[2].grid(True, alpha=0.3) + + plt.tight_layout() + plt.savefig('maze_benchmark.png', dpi=150, bbox_inches='tight') + plt.show() + + +def run_experiments(): + test_mazes = [ + ("maze/level1.txt", "Small 10x6"), + ("maze/medium10x10.txt", "Medium 10x10"), + ("maze/large20x20.txt", "Large 20x20"), + ("maze/empty15x15.txt", "Empty 15x15"), + ("maze/no_exit10x10.txt", "No exit 10x10") + ] + + algorithms = [ + ("BFS", BFS()), + ("DFS", DFS()), + ("AStar", AStar()) + ] + + results = [] + + for filepath, display_name in test_mazes: + print(f"Testing {display_name}...") + for alg_name, alg_obj in algorithms: + try: + stats = run_benchmark(filepath, alg_obj, runs=3) + results.append({ + 'maze': display_name, + 'strategy': alg_name, + 'time_ms': stats['time_ms'], + 'visited_cells': stats['visited_cells'], + 'path_length': stats['path_length'] + }) + print(f" {alg_name}: time={stats['time_ms']:.3f}ms, visited={stats['visited_cells']:.0f}, length={stats['path_length']:.0f}") + except Exception as e: + print(f" {alg_name}: ERROR - {e}") + results.append({ + 'maze': display_name, + 'strategy': alg_name, + 'time_ms': -1, + 'visited_cells': -1, + 'path_length': -1 + }) + + valid = [r for r in results if r['time_ms'] >= 0] + if not valid: + print("No valid results to save.") + return + + with open('maze_experiment.csv', 'w', newline='', encoding='utf-8') as f: + writer = csv.DictWriter(f, fieldnames=['maze', 'strategy', 'time_ms', 'visited_cells', 'path_length']) + writer.writeheader() + writer.writerows(valid) + + generate_charts(valid) + print("\nResults saved to maze_experiment.csv") + print("Plot saved to maze_benchmark.png") + + def play_game(): builder = TxtLabyrinthBuilder() maze = builder.build_from_file("maze/level1.txt") @@ -485,4 +623,7 @@ def play_game(): if __name__ == "__main__": - play_game() \ No newline at end of file + if len(sys.argv) > 1 and sys.argv[1] in ('experiment', 'benchmark'): + run_experiments() + else: + play_game() \ No newline at end of file