From b1331cab3758e0953d5591648973a8c37871c6ad Mon Sep 17 00:00:00 2001 From: mddcorporation Date: Sat, 9 May 2026 16:08:21 +0300 Subject: [PATCH] =?UTF-8?q?[5]=20=D0=B1=D0=BE=D0=BB=D1=8C=D1=88=D0=B5=20?= =?UTF-8?q?=D0=BB=D0=B0=D0=B1=D0=B8=D1=80=D0=B8=D0=BD=D1=82=D0=BE=D0=B2=20?= =?UTF-8?q?+=20=D1=81=D1=82=D0=B0=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VaravinVV/docs/data/task2/main.py | 257 ++++++++++++++++++------------ 1 file changed, 152 insertions(+), 105 deletions(-) diff --git a/VaravinVV/docs/data/task2/main.py b/VaravinVV/docs/data/task2/main.py index e4a6850..8e1feb9 100644 --- a/VaravinVV/docs/data/task2/main.py +++ b/VaravinVV/docs/data/task2/main.py @@ -4,6 +4,9 @@ import time from collections import deque from dataclasses import dataclass from typing import List, Optional, Dict, Set, Tuple, Any +import csv +import os +import sys class Cell: #тут что такое клетка @@ -14,8 +17,14 @@ class Cell: self.is_wall = is_wall self.is_exit = is_exit self.is_start = is_start + + def __eq__(self, other): + return isinstance(other, Cell) and self.x == other.x and self.y == other.y - def is_passable(self) -> bool: #можно ли пройти через клетку + def __hash__(self): + return hash((self.x, self.y)) + + def is_passable(self) -> bool: return not self.is_wall def __repr__(self) -> str: @@ -243,140 +252,178 @@ class Subject: self._observers.remove(observer) def notify(self, event_type: str, data: Any = None) -> None: - for observer in self._observers: - observer.update(event_type, data) + for obs in self._observers: + obs.update(event_type, data) + class MazeSolver(Subject): def __init__(self, maze: Maze, strategy: Optional[PathFindingStrategy] = None): super().__init__() self.maze = maze self._strategy = strategy - - def set_strategy(self, strategy: PathFindingStrategy) -> None: #смена алгоритма + + def set_strategy(self, strategy: PathFindingStrategy) -> None: self._strategy = strategy - + def solve(self) -> Optional[SearchStats]: if self._strategy is None: - print("где стратегия?") return None - - self.notify("solving_start", {"strategy": self._strategy.__class__.__name__}) - start_time = time.perf_counter() path, visited = self._strategy.find_path(self.maze, self.maze.start_cell, self.maze.exit_cell) end_time = time.perf_counter() - time_ms = (end_time - start_time) * 1000.0 - path_found = len(path) > 0 - stats = SearchStats( - time_ms=time_ms, - visited_cells=visited, - path_length=len(path) if path_found else 0, - path_found=path_found - ) - - if path_found: - self.notify("path_found", {"path": path, "stats": stats}) - else: - self.notify("no_path", {"stats": stats}) - - self.notify("solving_end", {"stats": stats}) - return stats + return SearchStats(time_ms, visited, len(path), len(path) > 0) -class ConsoleView(Observer): #красиво в консоль выводит лабиринт - def __init__(self, maze: Maze): - self.maze = maze - self.last_path: List[Cell] = [] - - def update(self, event_type: str, data: Any = None) -> None: - if event_type == "path_found": - self.last_path = data["path"] - self.render() - elif event_type == "no_path": - self.last_path = [] - self.render(no_path=True) - elif event_type == "solving_start": - print(f"\nпоиск пути (алгоритм: {data['strategy']})\n") - - def render(self, no_path: bool = False) -> None: - path_set = set(self.last_path) if self.last_path else set() - - for y in range(self.maze.height): - row = [] - for x in range(self.maze.width): - cell = self.maze.get_cell(x, y) - if cell is None: - row.append('?') - continue - if cell is self.maze.start_cell: - row.append('S') - elif cell is self.maze.exit_cell: - row.append('E') - elif cell in path_set: - row.append('*') - elif cell.is_wall: - row.append('#') +class Benchmark: + def __init__(self, maze_files: List[str], runs_per_strategy: int = 5): + self.maze_files = maze_files + self.runs = runs_per_strategy + self.strategies = { + "BFS": BFSStrategy(), + "DFS": DFSStrategy(), + "AStar": AStarStrategy() + } + self.builder = TextFileMazeBuilder() + self.results = [] + + def run(self, output_csv: str): + for maze_file in self.maze_files: + if not os.path.exists(maze_file): + print(f"файл {maze_file} не найден") + continue + try: + maze = self.builder.build_from_file(maze_file) + except Exception as e: + print(f"ошибка загрузки {maze_file}: {e}") + continue + + print(f"обработка лабиринта: {maze_file} (размер {maze.width}x{maze.height})") + for strat_name, strategy in self.strategies.items(): + solver = MazeSolver(maze, strategy) + times = [] + visited_list = [] + path_lengths = [] + path_found = False + for run_idx in range(self.runs): + stats = solver.solve() + if stats is None: + continue + times.append(stats.time_ms) + visited_list.append(stats.visited_cells) + path_lengths.append(stats.path_length) + path_found = stats.path_found + if times: + avg_time = sum(times) / len(times) + avg_visited = sum(visited_list) / len(visited_list) + avg_length = sum(path_lengths) / len(path_lengths) else: - row.append(' ') - print(''.join(row)) - - if no_path: - print("\nнет пути (no way)") - elif self.last_path: - print(f"\nнайден путь длиной {len(self.last_path)} клеток") - else: - print("\nожидание решения") + avg_time = avg_visited = avg_length = 0.0 + self.results.append({ + "лабиринт": os.path.basename(maze_file), + "стратегия": strat_name, + "время_мс": round(avg_time, 3), + "посещено_клеток": round(avg_visited, 1), + "длина_пути": round(avg_length, 1), + "путь_найден": path_found + }) + print(f" {strat_name}: {avg_time:.3f} мс, посещено {avg_visited:.1f}, длина {avg_length:.1f}") -def main(): - import sys - - if len(sys.argv) < 2: - print("для запуска: python main.py <имя_лабиринта>.txt") - return - - filename = sys.argv[1] - - #строится лабиринт из файла + with open(output_csv, 'w', newline='', encoding='utf-8') as csvfile: + fieldnames = ["лабиринт", "стратегия", "время_мс", "посещено_клеток", "длина_пути", "путь_найден"] + writer = csv.DictWriter(csvfile, fieldnames=fieldnames, delimiter=',') + writer.writeheader() + for row in self.results: + writer.writerow(row) + print(f"\nрезультаты сохранены в {output_csv}") + + +# ----------------------------- Консольный режим (для одного лабиринта, с визуализацией) ----------------------------- +def interactive_mode(maze_file: str): builder = TextFileMazeBuilder() try: - maze = builder.build_from_file(filename) + maze = builder.build_from_file(maze_file) except Exception as e: print(f"ошибка загрузки лабиринта: {e}") return - - #наблюдатель и решатель прикрепляются - solver = MazeSolver(maze) - view = ConsoleView(maze) - solver.attach(view) + strategies = { - "1": BFSStrategy(), - "2": DFSStrategy(), - "3": AStarStrategy() + "1": ("BFS", BFSStrategy()), + "2": ("DFS", DFSStrategy()), + "3": ("A*", AStarStrategy()) } - print("\nвыберите алгоритм поиска:") print("1. BFS") print("2. DFS") print("3. A*") choice = input("введите (1/2/3): ").strip() - - strategy = strategies.get(choice) - if not strategy: - print("неверный выбор, по умолчанию используется BFS.") - strategy = BFSStrategy() - - solver.set_strategy(strategy) - stats = solver.solve() - - if stats: - print("\nстатистика по поиску пути в данном лабиринте") - print(f"выбранный алгоритм: {strategy.__class__.__name__}") - print(f"время выполнения: {stats.time_ms:.3f} мс") - print(f"посещено клеток: {stats.visited_cells}") - print(f"путь найден?: {'да' if stats.path_found else 'нет'}") - if stats.path_found: - print(f"длина пути: {stats.path_length}") + if choice not in strategies: + print("неверный выбор, по умолчанию используется BFS.") + strat_name, strategy = strategies["1"] + else: + strat_name, strategy = strategies[choice] + + solver = MazeSolver(maze, strategy) + stats = solver.solve() + if stats is None: + print("ошибка с решением") + return + + path, _ = strategy.find_path(maze, maze.start_cell, maze.exit_cell) + path_set = set(path) + for y in range(maze.height): + row = [] + for x in range(maze.width): + cell = maze.get_cell(x, y) + if cell is maze.start_cell: + row.append('S') + elif cell is maze.exit_cell: + row.append('E') + elif cell in path_set: + row.append('*') + elif cell and cell.is_wall: + row.append('#') + else: + row.append(' ') + print(''.join(row)) + + print(f"\nстатистика ({strat_name}):") + print(f"время выполнения: {stats.time_ms:.3f} мс") + print(f"посещено клеток: {stats.visited_cells}") + print(f"длина пути: {stats.path_length}") + print(f"путь найден: {'да' if stats.path_found else 'нет'}") + + +def main(): + if len(sys.argv) < 2: + print("использование:") + print("режим визуализации: python main.py <файл_лабиринта>") + print("режим замера: python main.py --benchmark <список_лабиринтов> --runs <кол_во_итераций> --output <название_таблицы>.csv") + return + + if sys.argv[1] == "--benchmark": + args = sys.argv[2:] + maze_files = [] + runs = 5 + output = "benchmark_results.csv" + i = 0 + while i < len(args): + if args[i] == "--runs" and i+1 < len(args): + runs = int(args[i+1]) + i += 2 + elif args[i] == "--output" and i+1 < len(args): + output = args[i+1] + i += 2 + else: + maze_files.append(args[i]) + i += 1 + if not maze_files: + print("Ошибка: не указаны файлы лабиринтов.") + return + benchmark = Benchmark(maze_files, runs_per_strategy=runs) + benchmark.run(output) + else: + interactive_mode(sys.argv[1]) if __name__ == "__main__": main() \ No newline at end of file