{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "1ef30a86-b41f-49eb-84c3-5d425614cbdd", "metadata": {}, "outputs": [], "source": [ "import time\n", "from typing import List, Optional\n", "from dataclasses import dataclass, field\n", "from modelsMaze import Maze\n", "from modelsCell import Cell\n", "from strategiesPathfinding_strategy import PathFindingStrategy\n", "from visualizationObserver import Observer\n", "\n", "@dataclass\n", "class SearchStats:\n", " \"\"\"Статистика поиска.\"\"\"\n", " algorithm_name: str\n", " time_ms: float\n", " visited_cells: int\n", " path_length: int\n", " path_found: bool = True\n", "\n", "class MazeSolver:\n", " \"\"\"\n", " Оркестратор для решения лабиринта.\n", " Использует паттерн Strategy для алгоритмов поиска.\n", " Поддерживает Observer для уведомлений.\n", " \"\"\"\n", " \n", " def __init__(self, maze: Maze, strategy: Optional[PathFindingStrategy] = None):\n", " self.maze = maze\n", " self._strategy = strategy\n", " self._observers: List[Observer] = []\n", " self._last_path: List[Cell] = []\n", " self._last_stats: Optional[SearchStats] = None\n", " \n", " def set_strategy(self, strategy: PathFindingStrategy) -> None:\n", " \"\"\"Динамическая смена стратегии.\"\"\"\n", " self._strategy = strategy\n", " self._notify(f\"Стратегия изменена на {strategy.name}\")\n", " \n", " def attach(self, observer: Observer) -> None:\n", " \"\"\"Подписать наблюдателя.\"\"\"\n", " self._observers.append(observer)\n", " \n", " def detach(self, observer: Observer) -> None:\n", " \"\"\"Отписать наблюдателя.\"\"\"\n", " if observer in self._observers:\n", " self._observers.remove(observer)\n", " \n", " def _notify(self, event: str) -> None:\n", " \"\"\"Уведомить всех наблюдателей.\"\"\"\n", " for observer in self._observers:\n", " observer.update(event)\n", " \n", " def solve(self) -> List[Cell]:\n", " \"\"\"\n", " Выполнить поиск пути с текущей стратегией.\n", " Возвращает путь (список клеток).\n", " \"\"\"\n", " if self._strategy is None:\n", " raise ValueError(\"Стратегия не установлена\")\n", " \n", " if not self.maze.start_cell or not self.maze.exit_cell:\n", " raise ValueError(\"Лабиринт не имеет старта или выхода\")\n", " \n", " self._notify(f\"Начинаем поиск пути с использованием {self._strategy.name}...\")\n", " \n", " start_time = time.perf_counter()\n", " path = self._strategy.find_path(self.maze, self.maze.start_cell, self.maze.exit_cell)\n", " end_time = time.perf_counter()\n", " \n", " time_ms = (end_time - start_time) * 1000\n", " \n", " # Получаем количество посещённых клеток из стратегии\n", " visited_cells = getattr(self._strategy, 'last_visited_count', 0)\n", " \n", " self._last_path = path\n", " self._last_stats = SearchStats(\n", " algorithm_name=self._strategy.name,\n", " time_ms=time_ms,\n", " visited_cells=visited_cells,\n", " path_length=len(path),\n", " path_found=len(path) > 0\n", " )\n", " \n", " if path:\n", " self._notify(f\"Путь найден! Длина: {len(path)}, время: {time_ms:.2f} мс, посещено: {visited_cells}\")\n", " else:\n", " self._notify(f\"Путь не найден! Время: {time_ms:.2f} мс, посещено: {visited_cells}\")\n", " \n", " return path\n", " \n", " @property\n", " def last_path(self) -> List[Cell]:\n", " return self._last_path\n", " \n", " @property\n", " def last_stats(self) -> Optional[SearchStats]:\n", " return self._last_stats" ] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:base] *", "language": "python", "name": "conda-base-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.13.9" } }, "nbformat": 4, "nbformat_minor": 5 }