diff --git a/dyachenkoas/docs/data2/experiment_results.csv b/dyachenkoas/docs/data2/experiment_results.csv new file mode 100644 index 0000000..ed737e3 --- /dev/null +++ b/dyachenkoas/docs/data2/experiment_results.csv @@ -0,0 +1,16 @@ +лабиринт,стратегия,время_мс,посещено_клеток,длина_пути +Empty,BFS,0.158,162,26 +Empty,DFS,0.096,162,94 +Empty,AStar,0.290,162,26 +Large,BFS,0.194,220,75 +Large,DFS,0.073,77,75 +Large,AStar,0.337,216,75 +Medium,BFS,0.156,179,34 +Medium,DFS,0.038,44,38 +Medium,AStar,0.140,85,34 +No Exit,BFS,0.001,0,0 +No Exit,DFS,0.000,0,0 +No Exit,AStar,0.000,0,0 +Small,BFS,0.022,22,16 +Small,DFS,0.016,19,16 +Small,AStar,0.033,21,16 diff --git a/dyachenkoas/docs/data2/main.py b/dyachenkoas/docs/data2/main.py new file mode 100644 index 0000000..9c9c8b1 --- /dev/null +++ b/dyachenkoas/docs/data2/main.py @@ -0,0 +1,425 @@ +import sys +import os +import time +import csv +from collections import deque +import heapq +import matplotlib.pyplot as plt +import numpy as np + + +# ========== Модель данных ========== +class Tile: + def __init__(self, x, y): + self._x = x + self._y = y + self._wall = False + self._entry = False + self._goal = False + + @property + def x(self): return self._x + @property + def y(self): return self._y + @property + def is_wall(self): return self._wall + @is_wall.setter + def is_wall(self, value): self._wall = value + @property + def is_entry(self): return self._entry + @is_entry.setter + def is_entry(self, value): self._entry = value + @property + def is_goal(self): return self._goal + @is_goal.setter + def is_goal(self, value): self._goal = value + + def can_walk(self): + return not self._wall + + +class Labyrinth: + def __init__(self, width, height): + self._width = width + self._height = height + self._grid = [[Tile(x, y) for x in range(width)] for y in range(height)] + self._start = None + self._exit = None + + @property + def width(self): return self._width + @property + def height(self): return self._height + @property + def start(self): return self._start + @property + def exit(self): return self._exit + + def tile_at(self, x, y): + if 0 <= x < self._width and 0 <= y < self._height: + return self._grid[y][x] + return None + + def configure_tile(self, x, y, kind): + tile = self.tile_at(x, y) + if tile is None: + return + if kind == 'wall': + tile.is_wall = True + elif kind == 'entry': + if self._start: + self._start.is_entry = False + tile.is_entry = True + tile.is_wall = False + self._start = tile + elif kind == 'goal': + if self._exit: + self._exit.is_goal = False + tile.is_goal = True + tile.is_wall = False + self._exit = tile + elif kind == 'floor': + tile.is_wall = False + + def neighbours(self, tile): + res = [] + for dx, dy in ((0, -1), (0, 1), (-1, 0), (1, 0)): + nb = self.tile_at(tile.x + dx, tile.y + dy) + if nb and nb.can_walk(): + res.append(nb) + return res + + +# ========== Загрузка из файла ========== +class TextLabyrinthBuilder: + def build(self, filename): + with open(filename, 'r', encoding='utf-8') as f: + lines = [line.rstrip('\n') for line in f] + h = len(lines) + w = max(len(l) for l in lines) if h else 0 + lab = Labyrinth(w, h) + for y, row in enumerate(lines): + for x, ch in enumerate(row): + if ch == '#': + lab.configure_tile(x, y, 'wall') + elif ch == 'S': + lab.configure_tile(x, y, 'entry') + elif ch == 'E': + lab.configure_tile(x, y, 'goal') + elif ch == ' ': + lab.configure_tile(x, y, 'floor') + return lab + + +# ========== Алгоритмы поиска ========== +class BFS_Pathfinder: + def find_path(self, lab, start, goal): + if goal is None: + self._visited = 0 + return [] + q = deque([start]) + preds = {start: None} + seen = {start} + while q: + cur = q.popleft() + if cur == goal: + self._visited = len(seen) + return self._build_path(preds, start, goal) + for nb in lab.neighbours(cur): + if nb not in seen: + seen.add(nb) + preds[nb] = cur + q.append(nb) + self._visited = len(seen) + return [] + + def _build_path(self, preds, start, goal): + path = [] + cur = goal + while cur is not None: + path.append(cur) + cur = preds.get(cur) + path.reverse() + return path + + @property + def visited_count(self): + return getattr(self, '_visited', 0) + + +class DFS_Pathfinder: + def find_path(self, lab, start, goal): + if goal is None: + self._visited = 0 + return [] + stack = [start] + preds = {start: None} + seen = {start} + while stack: + cur = stack.pop() + if cur == goal: + self._visited = len(seen) + return self._build_path(preds, start, goal) + for nb in lab.neighbours(cur): + if nb not in seen: + seen.add(nb) + preds[nb] = cur + stack.append(nb) + self._visited = len(seen) + return [] + + def _build_path(self, preds, start, goal): + path = [] + cur = goal + while cur is not None: + path.append(cur) + cur = preds.get(cur) + path.reverse() + return path + + @property + def visited_count(self): + return getattr(self, '_visited', 0) + + +class AStar_Pathfinder: + def _heuristic(self, a, b): + return abs(a.x - b.x) + abs(a.y - b.y) + + def find_path(self, lab, start, goal): + if goal is None: + self._visited = 0 + return [] + heap = [] + cnt = 0 + f_start = self._heuristic(start, goal) + heapq.heappush(heap, (f_start, cnt, start)) + cnt += 1 + preds = {} + g = {start: 0} + f = {start: f_start} + seen = set() + while heap: + cur_f, _, cur = heapq.heappop(heap) + seen.add(cur) + if cur == goal: + self._visited = len(seen) + return self._build_path(preds, start, goal) + if cur_f > f.get(cur, float('inf')): + continue + for nb in lab.neighbours(cur): + tent_g = g[cur] + 1 + if tent_g < g.get(nb, float('inf')): + preds[nb] = cur + g[nb] = tent_g + new_f = tent_g + self._heuristic(nb, goal) + f[nb] = new_f + heapq.heappush(heap, (new_f, cnt, nb)) + cnt += 1 + self._visited = len(seen) + return [] + + def _build_path(self, preds, start, goal): + path = [] + cur = goal + while cur is not None: + path.append(cur) + cur = preds.get(cur) + path.reverse() + return path + + @property + def visited_count(self): + return getattr(self, '_visited', 0) + + +class LabyrinthSolver: + def __init__(self, lab): + self._lab = lab + self._strategy = None + + def set_strategy(self, strategy): + self._strategy = strategy + + def solve(self): + start_t = time.perf_counter() + path = self._strategy.find_path(self._lab, self._lab.start, self._lab.exit) + elapsed = (time.perf_counter() - start_t) * 1000 + return { + 'time_ms': elapsed, + 'visited': self._strategy.visited_count, + 'length': len(path) + } + + +# ========== Графики ========== +def create_charts(results, save_filename='maze_performance.png'): + mazes = list(set([r['maze'] for r in results])) + strategies = ['BFS', 'DFS', 'AStar'] + + data = {s: {m: None for m in mazes} for s in strategies} + for r in results: + data[r['strategy']][r['maze']] = r + + times = {s: [data[s][m]['time_ms'] for m in mazes] for s in strategies} + visited = {s: [data[s][m]['visited_cells'] for m in mazes] for s in strategies} + lengths = {s: [data[s][m]['path_length'] for m in mazes] for s in strategies} + + fig, axes = plt.subplots(1, 3, figsize=(15, 5)) + x = np.arange(len(mazes)) + width = 0.25 + + # Время + axes[0].bar(x - width, times['BFS'], width, label='BFS', color='blue', alpha=0.7) + axes[0].bar(x, times['DFS'], width, label='DFS', color='green', alpha=0.7) + axes[0].bar(x + width, times['AStar'], width, label='A*', color='red', alpha=0.7) + axes[0].set_xlabel('Лабиринты') + axes[0].set_ylabel('Время (мс)') + axes[0].set_title('Время выполнения алгоритмов') + axes[0].set_xticks(x) + axes[0].set_xticklabels(mazes, rotation=15, ha='right', fontsize=8) + axes[0].legend() + axes[0].grid(True, alpha=0.3) + + # Посещённые клетки + axes[1].bar(x - width, visited['BFS'], width, label='BFS', color='blue', alpha=0.7) + axes[1].bar(x, visited['DFS'], width, label='DFS', color='green', alpha=0.7) + axes[1].bar(x + width, visited['AStar'], width, label='A*', color='red', alpha=0.7) + axes[1].set_xlabel('Лабиринты') + axes[1].set_ylabel('Количество клеток') + axes[1].set_title('Посещённые клетки') + axes[1].set_xticks(x) + axes[1].set_xticklabels(mazes, rotation=15, ha='right', fontsize=8) + axes[1].legend() + axes[1].grid(True, alpha=0.3) + + # Длина пути + axes[2].bar(x - width, lengths['BFS'], width, label='BFS', color='blue', alpha=0.7) + axes[2].bar(x, lengths['DFS'], width, label='DFS', color='green', alpha=0.7) + axes[2].bar(x + width, lengths['AStar'], width, label='A*', color='red', alpha=0.7) + axes[2].set_xlabel('Лабиринты') + axes[2].set_ylabel('Длина пути') + axes[2].set_title('Длина найденного пути') + axes[2].set_xticks(x) + axes[2].set_xticklabels(mazes, rotation=15, ha='right', fontsize=8) + axes[2].legend() + axes[2].grid(True, alpha=0.3) + + plt.suptitle('Сравнение алгоритмов поиска пути', fontsize=14, fontweight='bold') + plt.tight_layout() + plt.savefig(save_filename, dpi=300, bbox_inches='tight') + print(f"\nГрафик сохранён в файл: {save_filename}") + plt.show() + + +# ========== Основная программа ========== +def main(): + print('\n' + '=' * 60) + print('ПОИСК ВЫХОДА ИЗ ЛАБИРИНТА') + print('Сравнение алгоритмов BFS, DFS и A*') + print('=' * 60) + + # Находим все txt файлы в текущей директории + maze_files = [f for f in os.listdir('.') if f.endswith('.txt') and f.startswith('maze_')] + + if not maze_files: + print("\nНет файлов с лабиринтами (maze_*.txt)") + return + + print(f"\nНайдено лабиринтов: {len(maze_files)}") + for f in maze_files: + print(f" - {f}") + + strategies = [ + ('BFS', BFS_Pathfinder()), + ('DFS', DFS_Pathfinder()), + ('AStar', AStar_Pathfinder()) + ] + + all_results = [] + builder = TextLabyrinthBuilder() + + for maze_file in sorted(maze_files): + maze_name = maze_file.replace('.txt', '').replace('maze_', '').replace('_', ' ').title() + print(f"\nОбработка лабиринта: {maze_name}") + + lab = builder.build(maze_file) + + for sname, strat in strategies: + solver = LabyrinthSolver(lab) + solver.set_strategy(strat) + stats = solver.solve() + + all_results.append({ + 'maze': maze_name, + 'strategy': sname, + 'time_ms': stats['time_ms'], + 'visited_cells': stats['visited'], + 'path_length': stats['length'] + }) + + status = "Найден" if stats['length'] > 0 else "Не найден" + print(f" {sname}: {status} (время: {stats['time_ms']:.3f} мс, посещено: {stats['visited']}, длина: {stats['length']})") + + # Сохранение в CSV + print('\n' + '=' * 60) + print('СОХРАНЕНИЕ РЕЗУЛЬТАТОВ В CSV') + print('=' * 60) + + csv_filename = 'experiment_results.csv' + with open(csv_filename, 'w', newline='', encoding='utf-8') as csvfile: + fieldnames = ['лабиринт', 'стратегия', 'время_мс', 'посещено_клеток', 'длина_пути'] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames) + + writer.writeheader() + for result in all_results: + writer.writerow({ + 'лабиринт': result['maze'], + 'стратегия': result['strategy'], + 'время_мс': f"{result['time_ms']:.3f}", + 'посещено_клеток': result['visited_cells'], + 'длина_пути': result['path_length'] + }) + + print(f"\nРезультаты сохранены в файл: {csv_filename}") + + # Вывод таблицы в консоль + print('\n' + '=' * 100) + print('ТАБЛИЦА РЕЗУЛЬТАТОВ') + print('=' * 100) + print(f"{'Лабиринт':<20} {'Стратегия':<10} {'Время (мс)':<12} {'Посещено':<12} {'Длина пути':<12}") + print('-' * 100) + for r in all_results: + print(f"{r['maze']:<20} {r['strategy']:<10} {r['time_ms']:<12.3f} {r['visited_cells']:<12.0f} {r['path_length']:<12.0f}") + print('=' * 100) + + # Создание графика + print('\nСОЗДАНИЕ ГРАФИКА...') + create_charts(all_results, 'maze_performance.png') + + # Статистика + print('\n' + '=' * 60) + print('СТАТИСТИКА') + print('=' * 60) + + for strategy in ['BFS', 'DFS', 'AStar']: + strategy_results = [r for r in all_results if r['strategy'] == strategy and r['path_length'] > 0] + if strategy_results: + avg_time = sum(r['time_ms'] for r in strategy_results) / len(strategy_results) + avg_visited = sum(r['visited_cells'] for r in strategy_results) / len(strategy_results) + avg_length = sum(r['path_length'] for r in strategy_results) / len(strategy_results) + print(f'\n{strategy}:') + print(f' Среднее время: {avg_time:.3f} мс') + print(f' Среднее посещено: {avg_visited:.0f} клеток') + print(f' Средняя длина пути: {avg_length:.1f}') + + print('\n' + '=' * 60) + print('ГОТОВО') + print(f'Результаты сохранены в:') + print(f' - {csv_filename} (таблица)') + print(f' - maze_performance.png (график)') + print('=' * 60) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/dyachenkoas/docs/data2/maze_performance.png b/dyachenkoas/docs/data2/maze_performance.png new file mode 100644 index 0000000..cd031bb Binary files /dev/null and b/dyachenkoas/docs/data2/maze_performance.png differ