From d8ab579a4660ecee4a21674ec98fe06e8eaf37e0 Mon Sep 17 00:00:00 2001 From: Veronika Minina Date: Sun, 17 May 2026 17:56:08 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9A=D0=BE=D0=BD=D0=B2=D0=B5=D1=80=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D0=B2=D1=81=D0=B5=D1=85?= =?UTF-8?q?=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=BC=20=D0=B2=20=D1=80=D0=B0?= =?UTF-8?q?=D1=81=D1=88=D0=B8=D1=80=D0=B5=D0=BD=D0=B8=D0=B5=20py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs2/data2/buildersMaze_builder.ipynb | 23 -- MininaVD/docs2/data2/buildersMaze_builder.py | 10 + .../data2/buildersText_maze_builder.ipynb | 23 -- .../docs2/data2/buildersText_maze_builder.py | 60 ++++ MininaVD/docs2/data2/commandsCommand.ipynb | 48 ---- MininaVD/docs2/data2/commandsCommand.py | 21 ++ .../docs2/data2/commandsMove_command.ipynb | 84 ------ MininaVD/docs2/data2/commandsMove_command.py | 57 ++++ MininaVD/docs2/data2/commandsPlayer.ipynb | 65 ----- MininaVD/docs2/data2/commandsPlayer.py | 38 +++ .../docs2/data2/experimentsBenchmark.ipynb | 127 --------- MininaVD/docs2/data2/experimentsBenchmark.py | 100 +++++++ MininaVD/docs2/data2/main.ipynb | 256 ------------------ MininaVD/docs2/data2/main.py | 215 +++++++++++++++ MininaVD/docs2/data2/modelsCell.ipynb | 58 ---- MininaVD/docs2/data2/modelsCell.py | 31 +++ MininaVD/docs2/data2/modelsMaze.ipynb | 82 ------ MininaVD/docs2/data2/modelsMaze.py | 55 ++++ MininaVD/docs2/data2/solverMaze_solver.ipynb | 129 --------- MininaVD/docs2/data2/solverMaze_solver.py | 102 +++++++ .../data2/strategiesA_star_strategy.ipynb | 90 ------ .../docs2/data2/strategiesA_star_strategy.py | 63 +++++ .../docs2/data2/strategiesBfs_strategy.ipynb | 75 ----- .../docs2/data2/strategiesBfs_strategy.py | 48 ++++ .../docs2/data2/strategiesDfs_strategy.ipynb | 73 ----- .../docs2/data2/strategiesDfs_strategy.py | 46 ++++ .../strategiesPathfinding_strategy.ipynb | 67 ----- .../data2/strategiesPathfinding_strategy.py | 40 +++ .../data2/visualizationConsole_view.ipynb | 116 -------- .../docs2/data2/visualizationConsole_view.py | 89 ++++++ .../docs2/data2/visualizationObserver.ipynb | 43 --- MininaVD/docs2/data2/visualizationObserver.py | 16 ++ 32 files changed, 991 insertions(+), 1359 deletions(-) delete mode 100644 MininaVD/docs2/data2/buildersMaze_builder.ipynb create mode 100644 MininaVD/docs2/data2/buildersMaze_builder.py delete mode 100644 MininaVD/docs2/data2/buildersText_maze_builder.ipynb create mode 100644 MininaVD/docs2/data2/buildersText_maze_builder.py delete mode 100644 MininaVD/docs2/data2/commandsCommand.ipynb create mode 100644 MininaVD/docs2/data2/commandsCommand.py delete mode 100644 MininaVD/docs2/data2/commandsMove_command.ipynb create mode 100644 MininaVD/docs2/data2/commandsMove_command.py delete mode 100644 MininaVD/docs2/data2/commandsPlayer.ipynb create mode 100644 MininaVD/docs2/data2/commandsPlayer.py delete mode 100644 MininaVD/docs2/data2/experimentsBenchmark.ipynb create mode 100644 MininaVD/docs2/data2/experimentsBenchmark.py delete mode 100644 MininaVD/docs2/data2/main.ipynb create mode 100644 MininaVD/docs2/data2/main.py delete mode 100644 MininaVD/docs2/data2/modelsCell.ipynb create mode 100644 MininaVD/docs2/data2/modelsCell.py delete mode 100644 MininaVD/docs2/data2/modelsMaze.ipynb create mode 100644 MininaVD/docs2/data2/modelsMaze.py delete mode 100644 MininaVD/docs2/data2/solverMaze_solver.ipynb create mode 100644 MininaVD/docs2/data2/solverMaze_solver.py delete mode 100644 MininaVD/docs2/data2/strategiesA_star_strategy.ipynb create mode 100644 MininaVD/docs2/data2/strategiesA_star_strategy.py delete mode 100644 MininaVD/docs2/data2/strategiesBfs_strategy.ipynb create mode 100644 MininaVD/docs2/data2/strategiesBfs_strategy.py delete mode 100644 MininaVD/docs2/data2/strategiesDfs_strategy.ipynb create mode 100644 MininaVD/docs2/data2/strategiesDfs_strategy.py delete mode 100644 MininaVD/docs2/data2/strategiesPathfinding_strategy.ipynb create mode 100644 MininaVD/docs2/data2/strategiesPathfinding_strategy.py delete mode 100644 MininaVD/docs2/data2/visualizationConsole_view.ipynb create mode 100644 MininaVD/docs2/data2/visualizationConsole_view.py delete mode 100644 MininaVD/docs2/data2/visualizationObserver.ipynb create mode 100644 MininaVD/docs2/data2/visualizationObserver.py diff --git a/MininaVD/docs2/data2/buildersMaze_builder.ipynb b/MininaVD/docs2/data2/buildersMaze_builder.ipynb deleted file mode 100644 index e59e19c..0000000 --- a/MininaVD/docs2/data2/buildersMaze_builder.ipynb +++ /dev/null @@ -1,23 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "761fe720-5ebb-456c-96dc-fa3288bc6280", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "", - "name": "" - }, - "language_info": { - "name": "" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/MininaVD/docs2/data2/buildersMaze_builder.py b/MininaVD/docs2/data2/buildersMaze_builder.py new file mode 100644 index 0000000..e7c588a --- /dev/null +++ b/MininaVD/docs2/data2/buildersMaze_builder.py @@ -0,0 +1,10 @@ +from abc import ABC, abstractmethod +from modelsMaze import Maze + +class MazeBuilder(ABC): + """Интерфейс строителя лабиринта (паттерн Builder).""" + + @abstractmethod + def build_from_file(self, filename: str) -> Maze: + """Загрузить лабиринт из файла.""" + pass diff --git a/MininaVD/docs2/data2/buildersText_maze_builder.ipynb b/MininaVD/docs2/data2/buildersText_maze_builder.ipynb deleted file mode 100644 index fb62e28..0000000 --- a/MininaVD/docs2/data2/buildersText_maze_builder.ipynb +++ /dev/null @@ -1,23 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "6c5d97cf-bcbf-428a-b12c-1b86e9a02b96", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "", - "name": "" - }, - "language_info": { - "name": "" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/MininaVD/docs2/data2/buildersText_maze_builder.py b/MininaVD/docs2/data2/buildersText_maze_builder.py new file mode 100644 index 0000000..7780891 --- /dev/null +++ b/MininaVD/docs2/data2/buildersText_maze_builder.py @@ -0,0 +1,60 @@ +from typing import List, Tuple +from buildersMaze_builder import MazeBuilder +from modelsMaze import Maze +from modelsCell import Cell + +class TextFieldMazeBuilder(MazeBuilder): + """Загрузчик лабиринта из текстового файла.""" + + # Символы в файле + WALL_CHAR = '#' + PASS_CHAR = ' ' + START_CHAR = 'S' + EXIT_CHAR = 'E' + + def build_from_file(self, filename: str) -> Maze: + """Загрузить лабиринт из текстового файла.""" + with open(filename, 'r', encoding='utf-8') as f: + lines = [line.rstrip('\n') for line in f.readlines()] + + if not lines: + raise ValueError("Файл пуст") + + height = len(lines) + width = max(len(line) for line in lines) + + maze = Maze(width, height) + start_cell = None + exit_cell = None + + for y, line in enumerate(lines): + for x, ch in enumerate(line): + if x >= width: + continue + + is_wall = (ch == self.WALL_CHAR) + is_start = (ch == self.START_CHAR) + is_exit = (ch == self.EXIT_CHAR) + + # Пробел или буква - проходимая клетка + if ch == self.PASS_CHAR or is_start or is_exit: + is_wall = False + + cell = Cell(x=x, y=y, is_wall=is_wall, is_start=is_start, is_exit=is_exit) + maze.set_cell(x, y, cell) + + if is_start: + start_cell = cell + if is_exit: + exit_cell = cell + + # Валидация + if start_cell is None: + raise ValueError("В лабиринте нет стартовой клетки (S)") + if exit_cell is None: + raise ValueError("В лабиринте нет выходной клетки (E)") + + maze.start_cell = start_cell + maze.exit_cell = exit_cell + + return maze diff --git a/MininaVD/docs2/data2/commandsCommand.ipynb b/MininaVD/docs2/data2/commandsCommand.ipynb deleted file mode 100644 index f4f4961..0000000 --- a/MininaVD/docs2/data2/commandsCommand.ipynb +++ /dev/null @@ -1,48 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "29468b18-8654-4914-a68e-76c1a7f01c49", - "metadata": {}, - "outputs": [], - "source": [ - "from abc import ABC, abstractmethod\n", - "\n", - "class Command(ABC):\n", - " \"\"\"Интерфейс команды (паттерн Command).\"\"\"\n", - " \n", - " @abstractmethod\n", - " def execute(self) -> None:\n", - " \"\"\"Выполнить команду.\"\"\"\n", - " pass\n", - " \n", - " @abstractmethod\n", - " def undo(self) -> None:\n", - " \"\"\"Отменить команду.\"\"\"\n", - " pass" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/commandsCommand.py b/MininaVD/docs2/data2/commandsCommand.py new file mode 100644 index 0000000..c7d1fac --- /dev/null +++ b/MininaVD/docs2/data2/commandsCommand.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from abc import ABC, abstractmethod + +class Command(ABC): + """Интерфейс команды (паттерн Command).""" + + @abstractmethod + def execute(self) -> None: + """Выполнить команду.""" + pass + + @abstractmethod + def undo(self) -> None: + """Отменить команду.""" + pass + diff --git a/MininaVD/docs2/data2/commandsMove_command.ipynb b/MininaVD/docs2/data2/commandsMove_command.ipynb deleted file mode 100644 index 8bcd6d7..0000000 --- a/MininaVD/docs2/data2/commandsMove_command.ipynb +++ /dev/null @@ -1,84 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "492dd9b1-922e-4c95-9112-4b2df086eae6", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import Optional\n", - "from commandsCommand import Command\n", - "from commandsPlayer import Player\n", - "from modelsMaze import Maze\n", - "from modelsCell import Cell\n", - "\n", - "class MoveCommand(Command):\n", - " \"\"\"Команда перемещения игрока.\"\"\"\n", - " \n", - " # Направления\n", - " DIRECTIONS = {\n", - " 'w': (0, -1), # вверх\n", - " 's': (0, 1), # вниз\n", - " 'a': (-1, 0), # влево\n", - " 'd': (1, 0), # вправо\n", - " }\n", - " \n", - " def __init__(self, player: Player, maze: Maze, direction: str):\n", - " self.player = player\n", - " self.maze = maze\n", - " self.direction = direction.lower()\n", - " self._target_cell: Optional[Cell] = None\n", - " self._executed = False\n", - " \n", - " def execute(self) -> bool:\n", - " \"\"\"Выполнить перемещение.\"\"\"\n", - " if self.direction not in self.DIRECTIONS:\n", - " return False\n", - " \n", - " dx, dy = self.DIRECTIONS[self.direction]\n", - " x = self.player.current_cell.x + dx\n", - " y = self.player.current_cell.y + dy\n", - " \n", - " self._target_cell = self.maze.get_cell(x, y)\n", - " \n", - " if self._target_cell and self._target_cell.is_passable():\n", - " self.player.move_to(self._target_cell)\n", - " self._executed = True\n", - " return True\n", - " \n", - " return False\n", - " \n", - " def undo(self) -> bool:\n", - " \"\"\"Отменить перемещение.\"\"\"\n", - " if self._executed:\n", - " success = self.player.undo_move()\n", - " if success:\n", - " self._executed = False\n", - " return True\n", - " return False" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/commandsMove_command.py b/MininaVD/docs2/data2/commandsMove_command.py new file mode 100644 index 0000000..b916987 --- /dev/null +++ b/MininaVD/docs2/data2/commandsMove_command.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from typing import Optional +from commandsCommand import Command +from commandsPlayer import Player +from modelsMaze import Maze +from modelsCell import Cell + +class MoveCommand(Command): + """Команда перемещения игрока.""" + + # Направления + DIRECTIONS = { + 'w': (0, -1), # вверх + 's': (0, 1), # вниз + 'a': (-1, 0), # влево + 'd': (1, 0), # вправо + } + + def __init__(self, player: Player, maze: Maze, direction: str): + self.player = player + self.maze = maze + self.direction = direction.lower() + self._target_cell: Optional[Cell] = None + self._executed = False + + def execute(self) -> bool: + """Выполнить перемещение.""" + if self.direction not in self.DIRECTIONS: + return False + + dx, dy = self.DIRECTIONS[self.direction] + x = self.player.current_cell.x + dx + y = self.player.current_cell.y + dy + + self._target_cell = self.maze.get_cell(x, y) + + if self._target_cell and self._target_cell.is_passable(): + self.player.move_to(self._target_cell) + self._executed = True + return True + + return False + + def undo(self) -> bool: + """Отменить перемещение.""" + if self._executed: + success = self.player.undo_move() + if success: + self._executed = False + return True + return False + diff --git a/MininaVD/docs2/data2/commandsPlayer.ipynb b/MininaVD/docs2/data2/commandsPlayer.ipynb deleted file mode 100644 index 2329588..0000000 --- a/MininaVD/docs2/data2/commandsPlayer.ipynb +++ /dev/null @@ -1,65 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c2a5ffd4-9644-453e-83de-bdac76215a37", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import Optional\n", - "from modelsMaze import Maze\n", - "from modelsCell import Cell\n", - "\n", - "class Player:\n", - " \"\"\"Игрок, перемещающийся по лабиринту.\"\"\"\n", - " \n", - " def __init__(self, maze: Maze, start_cell: Cell):\n", - " self.maze = maze\n", - " self.current_cell = start_cell\n", - " self._previous_cell: Optional[Cell] = None\n", - " \n", - " def move_to(self, cell: Cell) -> bool:\n", - " \"\"\"Переместить игрока в указанную клетку (если она проходима).\"\"\"\n", - " if cell and cell.is_passable():\n", - " self._previous_cell = self.current_cell\n", - " self.current_cell = cell\n", - " return True\n", - " return False\n", - " \n", - " def undo_move(self) -> bool:\n", - " \"\"\"Отменить последнее перемещение.\"\"\"\n", - " if self._previous_cell:\n", - " self.current_cell = self._previous_cell\n", - " self._previous_cell = None\n", - " return True\n", - " return False\n", - " \n", - " @property\n", - " def position(self) -> Cell:\n", - " return self.current_cell" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/commandsPlayer.py b/MininaVD/docs2/data2/commandsPlayer.py new file mode 100644 index 0000000..f78a9a7 --- /dev/null +++ b/MininaVD/docs2/data2/commandsPlayer.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from typing import Optional +from modelsMaze import Maze +from modelsCell import Cell + +class Player: + """Игрок, перемещающийся по лабиринту.""" + + def __init__(self, maze: Maze, start_cell: Cell): + self.maze = maze + self.current_cell = start_cell + self._previous_cell: Optional[Cell] = None + + def move_to(self, cell: Cell) -> bool: + """Переместить игрока в указанную клетку (если она проходима).""" + if cell and cell.is_passable(): + self._previous_cell = self.current_cell + self.current_cell = cell + return True + return False + + def undo_move(self) -> bool: + """Отменить последнее перемещение.""" + if self._previous_cell: + self.current_cell = self._previous_cell + self._previous_cell = None + return True + return False + + @property + def position(self) -> Cell: + return self.current_cell + diff --git a/MininaVD/docs2/data2/experimentsBenchmark.ipynb b/MininaVD/docs2/data2/experimentsBenchmark.ipynb deleted file mode 100644 index a07df9d..0000000 --- a/MininaVD/docs2/data2/experimentsBenchmark.ipynb +++ /dev/null @@ -1,127 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "c7d4d33a-ead6-4906-b307-e357ba0995e2", - "metadata": {}, - "outputs": [], - "source": [ - "import csv\n", - "import time\n", - "from typing import List, Dict, Any\n", - "from modelsMaze import Maze\n", - "from strategiesBfs_strategy import BFSStrategy\n", - "from strategiesDfs_strategy import DFSStrategy\n", - "from strategiesA_star_strategy import AStarStrategy\n", - "from solverMaze_solver import MazeSolver\n", - "\n", - "class Benchmark:\n", - " \"\"\"Экспериментальное сравнение алгоритмов.\"\"\"\n", - " \n", - " def __init__(self):\n", - " self.strategies = [\n", - " BFSStrategy(),\n", - " DFSStrategy(),\n", - " AStarStrategy(),\n", - " ]\n", - " self.results: List[Dict[str, Any]] = []\n", - " \n", - " def run_on_maze(self, maze: Maze, maze_name: str, iterations: int = 5) -> List[Dict]:\n", - " \"\"\"Запустить все стратегии на одном лабиринте.\"\"\"\n", - " results = []\n", - " \n", - " for strategy in self.strategies:\n", - " solver = MazeSolver(maze, strategy)\n", - " \n", - " times = []\n", - " visited_counts = []\n", - " path_lengths = []\n", - " path_found = False\n", - " \n", - " for i in range(iterations):\n", - " # Сбрасываем состояние стратегии для честного замера\n", - " # (кэш посещённых клеток не должен влиять)\n", - " start_time = time.perf_counter()\n", - " path = strategy.find_path(maze, maze.start_cell, maze.exit_cell)\n", - " end_time = time.perf_counter()\n", - " \n", - " times.append((end_time - start_time) * 1000)\n", - " visited_counts.append(getattr(strategy, 'last_visited_count', 0))\n", - " path_lengths.append(len(path))\n", - " path_found = len(path) > 0\n", - " \n", - " result = {\n", - " 'maze': maze_name,\n", - " 'algorithm': strategy.name,\n", - " 'avg_time_ms': sum(times) / len(times),\n", - " 'min_time_ms': min(times),\n", - " 'max_time_ms': max(times),\n", - " 'avg_visited': sum(visited_counts) / len(visited_counts),\n", - " 'avg_path_length': sum(path_lengths) / len(path_lengths),\n", - " 'path_found': path_found,\n", - " 'iterations': iterations\n", - " }\n", - " results.append(result)\n", - " self.results.append(result)\n", - " \n", - " return results\n", - " \n", - " def save_to_csv(self, filename: str = \"benchmark_results.csv\") -> None:\n", - " \"\"\"Сохранить результаты в CSV.\"\"\"\n", - " if not self.results:\n", - " print(\"Нет результатов для сохранения\")\n", - " return\n", - " \n", - " fieldnames = ['maze', 'algorithm', 'avg_time_ms', 'min_time_ms', \n", - " 'max_time_ms', 'avg_visited', 'avg_path_length', \n", - " 'path_found', 'iterations']\n", - " \n", - " with open(filename, 'w', newline='', encoding='utf-8') as f:\n", - " writer = csv.DictWriter(f, fieldnames=fieldnames)\n", - " writer.writeheader()\n", - " writer.writerows(self.results)\n", - " \n", - " print(f\"Результаты сохранены в {filename}\")\n", - " \n", - " def print_summary(self) -> None:\n", - " \"\"\"Вывести сводку результатов.\"\"\"\n", - " print(\"РЕЗУЛЬТАТЫ ЭКСПЕРИМЕНТОВ\")\n", - " \n", - " current_maze = None\n", - " for r in self.results:\n", - " if r['maze'] != current_maze:\n", - " current_maze = r['maze']\n", - " print(f\"\\n--- Лабиринт: {current_maze} ---\")\n", - " \n", - " status = \" НАЙДЕН\" if r['path_found'] else \" НЕ НАЙДЕН\"\n", - " print(f\" {r['algorithm']:6} | Время: {r['avg_time_ms']:8.2f} мс | \"\n", - " f\"Посещено: {r['avg_visited']:8.1f} | \"\n", - " f\"Путь: {r['avg_path_length']:6.1f} | {status}\")\n", - " \n", - " " - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/experimentsBenchmark.py b/MininaVD/docs2/data2/experimentsBenchmark.py new file mode 100644 index 0000000..7f88121 --- /dev/null +++ b/MininaVD/docs2/data2/experimentsBenchmark.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +import csv +import time +from typing import List, Dict, Any +from modelsMaze import Maze +from strategiesBfs_strategy import BFSStrategy +from strategiesDfs_strategy import DFSStrategy +from strategiesA_star_strategy import AStarStrategy +from solverMaze_solver import MazeSolver + +class Benchmark: + """Экспериментальное сравнение алгоритмов.""" + + def __init__(self): + self.strategies = [ + BFSStrategy(), + DFSStrategy(), + AStarStrategy(), + ] + self.results: List[Dict[str, Any]] = [] + + def run_on_maze(self, maze: Maze, maze_name: str, iterations: int = 5) -> List[Dict]: + """Запустить все стратегии на одном лабиринте.""" + results = [] + + for strategy in self.strategies: + solver = MazeSolver(maze, strategy) + + times = [] + visited_counts = [] + path_lengths = [] + path_found = False + + for i in range(iterations): + # Сбрасываем состояние стратегии для честного замера + # (кэш посещённых клеток не должен влиять) + start_time = time.perf_counter() + path = strategy.find_path(maze, maze.start_cell, maze.exit_cell) + end_time = time.perf_counter() + + times.append((end_time - start_time) * 1000) + visited_counts.append(getattr(strategy, 'last_visited_count', 0)) + path_lengths.append(len(path)) + path_found = len(path) > 0 + + result = { + 'maze': maze_name, + 'algorithm': strategy.name, + 'avg_time_ms': sum(times) / len(times), + 'min_time_ms': min(times), + 'max_time_ms': max(times), + 'avg_visited': sum(visited_counts) / len(visited_counts), + 'avg_path_length': sum(path_lengths) / len(path_lengths), + 'path_found': path_found, + 'iterations': iterations + } + results.append(result) + self.results.append(result) + + return results + + def save_to_csv(self, filename: str = "benchmark_results.csv") -> None: + """Сохранить результаты в CSV.""" + if not self.results: + print("Нет результатов для сохранения") + return + + fieldnames = ['maze', 'algorithm', 'avg_time_ms', 'min_time_ms', + 'max_time_ms', 'avg_visited', 'avg_path_length', + 'path_found', 'iterations'] + + with open(filename, 'w', newline='', encoding='utf-8') as f: + writer = csv.DictWriter(f, fieldnames=fieldnames) + writer.writeheader() + writer.writerows(self.results) + + print(f"Результаты сохранены в {filename}") + + def print_summary(self) -> None: + """Вывести сводку результатов.""" + print("РЕЗУЛЬТАТЫ ЭКСПЕРИМЕНТОВ") + + current_maze = None + for r in self.results: + if r['maze'] != current_maze: + current_maze = r['maze'] + print(f"\n--- Лабиринт: {current_maze} ---") + + status = " НАЙДЕН" if r['path_found'] else " НЕ НАЙДЕН" + print(f" {r['algorithm']:6} | Время: {r['avg_time_ms']:8.2f} мс | " + f"Посещено: {r['avg_visited']:8.1f} | " + f"Путь: {r['avg_path_length']:6.1f} | {status}") + + + diff --git a/MininaVD/docs2/data2/main.ipynb b/MininaVD/docs2/data2/main.ipynb deleted file mode 100644 index 1d1c73f..0000000 --- a/MininaVD/docs2/data2/main.ipynb +++ /dev/null @@ -1,256 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 7, - "id": "ac02445d-6e74-4f6e-bb96-2c28ccd82d83", - "metadata": {}, - "outputs": [ - { - "ename": "ModuleNotFoundError", - "evalue": "No module named 'modelsMaze'", - "output_type": "error", - "traceback": [ - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", - "\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)", - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[7]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m 5\u001b[39m sys.path.insert(\u001b[32m0\u001b[39m, os.getcwd())\n\u001b[32m 7\u001b[39m \u001b[38;5;66;03m# Импорты с вашими именами файлов\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mmodelsMaze\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m Maze, Cell\n\u001b[32m 9\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mbuildersText_maze_builder\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m TextFieldMazeBuilder\n\u001b[32m 10\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mstrategiesBFS_strategy\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m BFSStrategy\n", - "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'modelsMaze'" - ] - } - ], - "source": [ - "import sys\n", - "import os\n", - "\n", - "# Добавляем текущую папку в путь\n", - "sys.path.insert(0, os.getcwd())\n", - "\n", - "# Импорты с вашими именами файлов\n", - "from modelsMaze import Maze, Cell\n", - "from buildersText_maze_builder import TextFieldMazeBuilder\n", - "from strategiesBFS_strategy import BFSStrategy\n", - "from strategiesDFS_strategy import DFSStrategy\n", - "from strategiesA_star_strategy import AStarStrategy\n", - "from solverMaze_solver import MazeSolver\n", - "from visualizationConsole_view import ConsoleView\n", - "from commandsPlayer import Player\n", - "from commandsMove_command import MoveCommand\n", - "from experimentsBenchmark import Benchmark\n", - "\n", - "def create_test_mazes():\n", - " \"\"\"Создать тестовые лабиринты в папке mazes/.\"\"\"\n", - " mazes_dir = \"mazes\"\n", - " os.makedirs(mazes_dir, exist_ok=True)\n", - " \n", - " # Маленький лабиринт 10×10\n", - " small = [\n", - " \"##########\",\n", - " \"#S #\",\n", - " \"# ##### #\",\n", - " \"# # # #\",\n", - " \"# # # # #\",\n", - " \"# # # #\",\n", - " \"##### # #\",\n", - " \"# #\",\n", - " \"# E#\",\n", - " \"##########\",\n", - " ]\n", - " \n", - " # Пустой лабиринт\n", - " empty = [\"S\" + \" \" * 48 + \"E\"] + [\" \" * 50 for _ in range(48)]\n", - " \n", - " # Лабиринт без выхода\n", - " no_exit = [\n", - " \"##########\",\n", - " \"#S #\",\n", - " \"# ##### #\",\n", - " \"# # # #\",\n", - " \"# # # # #\",\n", - " \"# # # #\",\n", - " \"##### # #\",\n", - " \"# #\",\n", - " \"##########\",\n", - " \"##########\",\n", - " ]\n", - " \n", - " mazes = {\n", - " \"small.txt\": small,\n", - " \"empty.txt\": empty,\n", - " \"no_exit.txt\": no_exit,\n", - " }\n", - " \n", - " for name, content in mazes.items():\n", - " path = os.path.join(mazes_dir, name)\n", - " with open(path, 'w', encoding='utf-8') as f:\n", - " f.write('\\n'.join(content))\n", - " print(f\"Создан тестовый лабиринт: {path}\")\n", - " \n", - " print()\n", - "\n", - "def demo_builder_and_strategy():\n", - " \"\"\"Демонстрация паттернов Builder и Strategy.\"\"\"\n", - " print(\"\\n\" + \"=\" * 60)\n", - " print(\"ДЕМОНСТРАЦИЯ ПАТТЕРНОВ BUILDER И STRATEGY\")\n", - " print(\"=\" * 60)\n", - " \n", - " builder = TextFieldMazeBuilder()\n", - " maze = builder.build_from_file(\"mazes/small.txt\")\n", - " \n", - " strategies = [\n", - " BFSStrategy(),\n", - " DFSStrategy(),\n", - " AStarStrategy(),\n", - " ]\n", - " \n", - " for strategy in strategies:\n", - " print(f\"\\n--- Используем стратегию: {strategy.name} ---\")\n", - " solver = MazeSolver(maze, strategy)\n", - " path = solver.solve()\n", - " \n", - " if path:\n", - " print(f\" Путь найден! Длина: {len(path)}\")\n", - " print(f\" Время: {solver.last_stats.time_ms:.2f} мс\")\n", - " print(f\" Посещено клеток: {solver.last_stats.visited_cells}\")\n", - " else:\n", - " print(\" Путь не найден!\")\n", - " \n", - " return maze\n", - "\n", - "def demo_observer(maze: Maze):\n", - " \"\"\"Демонстрация паттерна Observer.\"\"\"\n", - " print(\"\\n\" + \"=\" * 60)\n", - " print(\"ДЕМОНСТРАЦИЯ ПАТТЕРНА OBSERVER\")\n", - " print(\"=\" * 60)\n", - " \n", - " view = ConsoleView(maze)\n", - " solver = MazeSolver(maze, BFSStrategy())\n", - " solver.attach(view)\n", - " \n", - " print(\"Запускаем поиск с наблюдателем...\")\n", - " path = solver.solve()\n", - " \n", - " view.set_solution_path(path)\n", - " view.render()\n", - " \n", - " return view\n", - "\n", - "def demo_command(maze: Maze, view: ConsoleView):\n", - " \"\"\"Демонстрация паттерна Command.\"\"\"\n", - " print(\"\\n\" + \"=\" * 60)\n", - " print(\"ДЕМОНСТРАЦИЯ ПАТТЕРНА COMMAND\")\n", - " print(\"=\" * 60)\n", - " \n", - " player = Player(maze, maze.start_cell)\n", - " view.set_player_position(player.position)\n", - " \n", - " print(\"Управление игроком:\")\n", - " print(\" W/A/S/D - движение, Z - отмена, Q - выход\")\n", - " \n", - " history = []\n", - " \n", - " while True:\n", - " view.render()\n", - " \n", - " cmd = input(\"Ваш ход: \").strip().lower()\n", - " \n", - " if cmd == 'q':\n", - " break\n", - " elif cmd == 'z':\n", - " if history:\n", - " last_cmd = history.pop()\n", - " last_cmd.undo()\n", - " view.set_player_position(player.position)\n", - " print(\"Последний ход отменён\")\n", - " else:\n", - " print(\"Нечего отменять\")\n", - " elif cmd in MoveCommand.DIRECTIONS:\n", - " move_cmd = MoveCommand(player, maze, cmd)\n", - " if move_cmd.execute():\n", - " history.append(move_cmd)\n", - " view.set_player_position(player.position)\n", - " \n", - " if player.position == maze.exit_cell:\n", - " print(\"\\n🎉 ПОБЕДА! ВЫ НАШЛИ ВЫХОД! 🎉\")\n", - " view.render()\n", - " break\n", - " else:\n", - " print(\"Туда нельзя пройти\")\n", - " else:\n", - " print(\"Неизвестная команда\")\n", - " \n", - " print(\"Игра завершена\")\n", - "\n", - "def run_experiments():\n", - " \"\"\"Запуск экспериментального сравнения.\"\"\"\n", - " print(\"\\n\" + \"=\" * 60)\n", - " print(\"ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ АЛГОРИТМОВ\")\n", - " print(\"=\" * 60)\n", - " \n", - " builder = TextFieldMazeBuilder()\n", - " benchmark = Benchmark()\n", - " \n", - " maze_files = [\"small.txt\", \"empty.txt\", \"no_exit.txt\"]\n", - " \n", - " for maze_file in maze_files:\n", - " try:\n", - " maze = builder.build_from_file(f\"mazes/{maze_file}\")\n", - " print(f\"\\nТестируем: {maze_file} ({maze.width}×{maze.height})\")\n", - " benchmark.run_on_maze(maze, maze_file, iterations=5)\n", - " except FileNotFoundError:\n", - " print(f\"Файл {maze_file} не найден\")\n", - " except ValueError as e:\n", - " print(f\"Ошибка: {e}\")\n", - " \n", - " benchmark.print_summary()\n", - " benchmark.save_to_csv()\n", - "\n", - "def main():\n", - " \"\"\"Главная функция.\"\"\"\n", - " print(\"=\" * 60)\n", - " print(\"ПРОГРАММА ПОИСКА ВЫХОДА ИЗ ЛАБИРИНТА\")\n", - " print(\"Паттерны: Builder, Strategy, Observer, Command\")\n", - " print(\"=\" * 60)\n", - " \n", - " create_test_mazes()\n", - " maze = demo_builder_and_strategy()\n", - " view = demo_observer(maze)\n", - " demo_command(maze, view)\n", - " run_experiments()\n", - " \n", - " print(\"\\nПрограмма завершена!\")\n", - "\n", - "if __name__ == \"__main__\":\n", - " main()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "d0972138-e9f0-4612-a87c-741d9d0bea13", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/main.py b/MininaVD/docs2/data2/main.py new file mode 100644 index 0000000..cb8f41b --- /dev/null +++ b/MininaVD/docs2/data2/main.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[7]: + + +import sys +import os + +# Добавляем текущую папку в путь +sys.path.insert(0, os.getcwd()) + +# Импорты с вашими именами файлов +from modelsMaze import Maze, Cell +from buildersText_maze_builder import TextFieldMazeBuilder +from strategiesBfs_strategy import BFSStrategy +from strategiesDfs_strategy import DFSStrategy +from strategiesA_star_strategy import AStarStrategy +from solverMaze_solver import MazeSolver +from visualizationConsole_view import ConsoleView +from commandsPlayer import Player +from commandsMove_command import MoveCommand +from experimentsBenchmark import Benchmark + +def create_test_mazes(): + """Создать тестовые лабиринты в папке mazes/.""" + mazes_dir = "mazes" + os.makedirs(mazes_dir, exist_ok=True) + + # Маленький лабиринт 10×10 + small = [ + "##########", + "#S #", + "# ##### #", + "# # # #", + "# # # # #", + "# # # #", + "##### # #", + "# #", + "# E#", + "##########", + ] + + # Пустой лабиринт + empty = ["S" + " " * 48 + "E"] + [" " * 50 for _ in range(48)] + + # Лабиринт без выхода + no_exit = [ + "##########", + "#S #", + "# ##### #", + "# # # #", + "# # # # #", + "# # # #", + "##### # #", + "# #", + "##########", + "##########", + ] + + mazes = { + "small.txt": small, + "empty.txt": empty, + "no_exit.txt": no_exit, + } + + for name, content in mazes.items(): + path = os.path.join(mazes_dir, name) + with open(path, 'w', encoding='utf-8') as f: + f.write('\n'.join(content)) + print(f"Создан тестовый лабиринт: {path}") + + print() + +def demo_builder_and_strategy(): + """Демонстрация паттернов Builder и Strategy.""" + print("\n" + "=" * 60) + print("ДЕМОНСТРАЦИЯ ПАТТЕРНОВ BUILDER И STRATEGY") + print("=" * 60) + + builder = TextFieldMazeBuilder() + maze = builder.build_from_file("mazes/small.txt") + + strategies = [ + BFSStrategy(), + DFSStrategy(), + AStarStrategy(), + ] + + for strategy in strategies: + print(f"\n--- Используем стратегию: {strategy.name} ---") + solver = MazeSolver(maze, strategy) + path = solver.solve() + + if path: + print(f" Путь найден! Длина: {len(path)}") + print(f" Время: {solver.last_stats.time_ms:.2f} мс") + print(f" Посещено клеток: {solver.last_stats.visited_cells}") + else: + print(" Путь не найден!") + + return maze + +def demo_observer(maze: Maze): + """Демонстрация паттерна Observer.""" + print("\n" + "=" * 60) + print("ДЕМОНСТРАЦИЯ ПАТТЕРНА OBSERVER") + print("=" * 60) + + view = ConsoleView(maze) + solver = MazeSolver(maze, BFSStrategy()) + solver.attach(view) + + print("Запускаем поиск с наблюдателем...") + path = solver.solve() + + view.set_solution_path(path) + view.render() + + return view + +def demo_command(maze: Maze, view: ConsoleView): + """Демонстрация паттерна Command.""" + print("\n" + "=" * 60) + print("ДЕМОНСТРАЦИЯ ПАТТЕРНА COMMAND") + print("=" * 60) + + player = Player(maze, maze.start_cell) + view.set_player_position(player.position) + + print("Управление игроком:") + print(" W/A/S/D - движение, Z - отмена, Q - выход") + + history = [] + + while True: + view.render() + + cmd = input("Ваш ход: ").strip().lower() + + if cmd == 'q': + break + elif cmd == 'z': + if history: + last_cmd = history.pop() + last_cmd.undo() + view.set_player_position(player.position) + print("Последний ход отменён") + else: + print("Нечего отменять") + elif cmd in MoveCommand.DIRECTIONS: + move_cmd = MoveCommand(player, maze, cmd) + if move_cmd.execute(): + history.append(move_cmd) + view.set_player_position(player.position) + + if player.position == maze.exit_cell: + print("\n🎉 ПОБЕДА! ВЫ НАШЛИ ВЫХОД! 🎉") + view.render() + break + else: + print("Туда нельзя пройти") + else: + print("Неизвестная команда") + + print("Игра завершена") + +def run_experiments(): + """Запуск экспериментального сравнения.""" + print("\n" + "=" * 60) + print("ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ АЛГОРИТМОВ") + print("=" * 60) + + builder = TextFieldMazeBuilder() + benchmark = Benchmark() + + maze_files = ["small.txt", "empty.txt", "no_exit.txt"] + + for maze_file in maze_files: + try: + maze = builder.build_from_file(f"mazes/{maze_file}") + print(f"\nТестируем: {maze_file} ({maze.width}×{maze.height})") + benchmark.run_on_maze(maze, maze_file, iterations=5) + except FileNotFoundError: + print(f"Файл {maze_file} не найден") + except ValueError as e: + print(f"Ошибка: {e}") + + benchmark.print_summary() + benchmark.save_to_csv() + +def main(): + """Главная функция.""" + print("=" * 60) + print("ПРОГРАММА ПОИСКА ВЫХОДА ИЗ ЛАБИРИНТА") + print("Паттерны: Builder, Strategy, Observer, Command") + print("=" * 60) + + create_test_mazes() + maze = demo_builder_and_strategy() + view = demo_observer(maze) + demo_command(maze, view) + run_experiments() + + print("\nПрограмма завершена!") + +if __name__ == "__main__": + main() + + +# In[ ]: + + + + diff --git a/MininaVD/docs2/data2/modelsCell.ipynb b/MininaVD/docs2/data2/modelsCell.ipynb deleted file mode 100644 index b6b5547..0000000 --- a/MininaVD/docs2/data2/modelsCell.ipynb +++ /dev/null @@ -1,58 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "3a6bb811-80ad-4ef5-95b6-0b7cffc6545e", - "metadata": {}, - "outputs": [], - "source": [ - "from dataclasses import dataclass\n", - "from typing import Optional\n", - "\n", - "@dataclass\n", - "class Cell:\n", - " \"\"\"Клетка лабиринта.\"\"\"\n", - " x: int\n", - " y: int\n", - " is_wall: bool = False\n", - " is_start: bool = False\n", - " is_exit: bool = False\n", - " weight: int = 1 # Для взвешенных лабиринтов (доп. задание)\n", - " \n", - " def is_passable(self) -> bool:\n", - " \"\"\"Проходима ли клетка.\"\"\"\n", - " return not self.is_wall\n", - " \n", - " def __hash__(self) -> int:\n", - " return hash((self.x, self.y))\n", - " \n", - " def __eq__(self, other) -> bool:\n", - " if not isinstance(other, Cell):\n", - " return False\n", - " return self.x == other.x and self.y == other.y" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/modelsCell.py b/MininaVD/docs2/data2/modelsCell.py new file mode 100644 index 0000000..e8c6b64 --- /dev/null +++ b/MininaVD/docs2/data2/modelsCell.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from dataclasses import dataclass +from typing import Optional + +@dataclass +class Cell: + """Клетка лабиринта.""" + x: int + y: int + is_wall: bool = False + is_start: bool = False + is_exit: bool = False + weight: int = 1 # Для взвешенных лабиринтов (доп. задание) + + def is_passable(self) -> bool: + """Проходима ли клетка.""" + return not self.is_wall + + def __hash__(self) -> int: + return hash((self.x, self.y)) + + def __eq__(self, other) -> bool: + if not isinstance(other, Cell): + return False + return self.x == other.x and self.y == other.y + diff --git a/MininaVD/docs2/data2/modelsMaze.ipynb b/MininaVD/docs2/data2/modelsMaze.ipynb deleted file mode 100644 index f90c4a4..0000000 --- a/MininaVD/docs2/data2/modelsMaze.ipynb +++ /dev/null @@ -1,82 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "bc164689-22b6-4efd-bdd7-6a9913be9303", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import List, Optional, Tuple\n", - "from modelsCell import Cell\n", - "\n", - "class Maze:\n", - " \"\"\"Модель лабиринта.\"\"\"\n", - " \n", - " def __init__(self, width: int = 0, height: int = 0):\n", - " self.width = width\n", - " self.height = height\n", - " self._cells: List[List[Optional[Cell]]] = [\n", - " [None for _ in range(width)] for _ in range(height)\n", - " ]\n", - " self.start_cell: Optional[Cell] = None\n", - " self.exit_cell: Optional[Cell] = None\n", - " \n", - " def set_cell(self, x: int, y: int, cell: Cell) -> None:\n", - " \"\"\"Установить клетку.\"\"\"\n", - " if 0 <= x < self.width and 0 <= y < self.height:\n", - " self._cells[y][x] = cell\n", - " \n", - " def get_cell(self, x: int, y: int) -> Optional[Cell]:\n", - " \"\"\"Получить клетку по координатам.\"\"\"\n", - " if 0 <= x < self.width and 0 <= y < self.height:\n", - " return self._cells[y][x]\n", - " return None\n", - " \n", - " def get_neighbors(self, cell: Cell) -> List[Cell]:\n", - " \"\"\"Получить проходимых соседей клетки (вверх, вниз, влево, вправо).\"\"\"\n", - " neighbors = []\n", - " directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] # вверх, вниз, влево, вправо\n", - " \n", - " for dx, dy in directions:\n", - " nx, ny = cell.x + dx, cell.y + dy\n", - " neighbor = self.get_cell(nx, ny)\n", - " if neighbor and neighbor.is_passable():\n", - " neighbors.append(neighbor)\n", - " \n", - " return neighbors\n", - " \n", - " def get_all_cells(self) -> List[Cell]:\n", - " \"\"\"Получить все клетки лабиринта.\"\"\"\n", - " cells = []\n", - " for y in range(self.height):\n", - " for x in range(self.width):\n", - " cell = self.get_cell(x, y)\n", - " if cell:\n", - " cells.append(cell)\n", - " return cells" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/modelsMaze.py b/MininaVD/docs2/data2/modelsMaze.py new file mode 100644 index 0000000..43ae196 --- /dev/null +++ b/MininaVD/docs2/data2/modelsMaze.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from typing import List, Optional, Tuple +from modelsCell import Cell + +class Maze: + """Модель лабиринта.""" + + def __init__(self, width: int = 0, height: int = 0): + self.width = width + self.height = height + self._cells: List[List[Optional[Cell]]] = [ + [None for _ in range(width)] for _ in range(height) + ] + self.start_cell: Optional[Cell] = None + self.exit_cell: Optional[Cell] = None + + def set_cell(self, x: int, y: int, cell: Cell) -> None: + """Установить клетку.""" + if 0 <= x < self.width and 0 <= y < self.height: + self._cells[y][x] = cell + + def get_cell(self, x: int, y: int) -> Optional[Cell]: + """Получить клетку по координатам.""" + if 0 <= x < self.width and 0 <= y < self.height: + return self._cells[y][x] + return None + + def get_neighbors(self, cell: Cell) -> List[Cell]: + """Получить проходимых соседей клетки (вверх, вниз, влево, вправо).""" + neighbors = [] + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] # вверх, вниз, влево, вправо + + for dx, dy in directions: + nx, ny = cell.x + dx, cell.y + dy + neighbor = self.get_cell(nx, ny) + if neighbor and neighbor.is_passable(): + neighbors.append(neighbor) + + return neighbors + + def get_all_cells(self) -> List[Cell]: + """Получить все клетки лабиринта.""" + cells = [] + for y in range(self.height): + for x in range(self.width): + cell = self.get_cell(x, y) + if cell: + cells.append(cell) + return cells + diff --git a/MininaVD/docs2/data2/solverMaze_solver.ipynb b/MininaVD/docs2/data2/solverMaze_solver.ipynb deleted file mode 100644 index 4cc5c0d..0000000 --- a/MininaVD/docs2/data2/solverMaze_solver.ipynb +++ /dev/null @@ -1,129 +0,0 @@ -{ - "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 -} diff --git a/MininaVD/docs2/data2/solverMaze_solver.py b/MininaVD/docs2/data2/solverMaze_solver.py new file mode 100644 index 0000000..b95e911 --- /dev/null +++ b/MininaVD/docs2/data2/solverMaze_solver.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +import time +from typing import List, Optional +from dataclasses import dataclass, field +from modelsMaze import Maze +from modelsCell import Cell +from strategiesPathfinding_strategy import PathFindingStrategy +from visualizationObserver import Observer + +@dataclass +class SearchStats: + """Статистика поиска.""" + algorithm_name: str + time_ms: float + visited_cells: int + path_length: int + path_found: bool = True + +class MazeSolver: + """ + Оркестратор для решения лабиринта. + Использует паттерн Strategy для алгоритмов поиска. + Поддерживает Observer для уведомлений. + """ + + def __init__(self, maze: Maze, strategy: Optional[PathFindingStrategy] = None): + self.maze = maze + self._strategy = strategy + self._observers: List[Observer] = [] + self._last_path: List[Cell] = [] + self._last_stats: Optional[SearchStats] = None + + def set_strategy(self, strategy: PathFindingStrategy) -> None: + """Динамическая смена стратегии.""" + self._strategy = strategy + self._notify(f"Стратегия изменена на {strategy.name}") + + def attach(self, observer: Observer) -> None: + """Подписать наблюдателя.""" + self._observers.append(observer) + + def detach(self, observer: Observer) -> None: + """Отписать наблюдателя.""" + if observer in self._observers: + self._observers.remove(observer) + + def _notify(self, event: str) -> None: + """Уведомить всех наблюдателей.""" + for observer in self._observers: + observer.update(event) + + def solve(self) -> List[Cell]: + """ + Выполнить поиск пути с текущей стратегией. + Возвращает путь (список клеток). + """ + if self._strategy is None: + raise ValueError("Стратегия не установлена") + + if not self.maze.start_cell or not self.maze.exit_cell: + raise ValueError("Лабиринт не имеет старта или выхода") + + self._notify(f"Начинаем поиск пути с использованием {self._strategy.name}...") + + start_time = time.perf_counter() + path = 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 + + # Получаем количество посещённых клеток из стратегии + visited_cells = getattr(self._strategy, 'last_visited_count', 0) + + self._last_path = path + self._last_stats = SearchStats( + algorithm_name=self._strategy.name, + time_ms=time_ms, + visited_cells=visited_cells, + path_length=len(path), + path_found=len(path) > 0 + ) + + if path: + self._notify(f"Путь найден! Длина: {len(path)}, время: {time_ms:.2f} мс, посещено: {visited_cells}") + else: + self._notify(f"Путь не найден! Время: {time_ms:.2f} мс, посещено: {visited_cells}") + + return path + + @property + def last_path(self) -> List[Cell]: + return self._last_path + + @property + def last_stats(self) -> Optional[SearchStats]: + return self._last_stats + diff --git a/MininaVD/docs2/data2/strategiesA_star_strategy.ipynb b/MininaVD/docs2/data2/strategiesA_star_strategy.ipynb deleted file mode 100644 index 0f3b90b..0000000 --- a/MininaVD/docs2/data2/strategiesA_star_strategy.ipynb +++ /dev/null @@ -1,90 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "f8e6c2ad-712c-44a0-8ebc-ed0d67234c05", - "metadata": {}, - "outputs": [], - "source": [ - "import heapq\n", - "from typing import List, Dict, Optional, Tuple\n", - "from strategiesPathfinding_strategy import PathFindingStrategy\n", - "from modelsMaze import Maze\n", - "from modelsCell import Cell\n", - "\n", - "class AStarStrategy(PathFindingStrategy):\n", - " \"\"\"Алгоритм A* с манхэттенской эвристикой.\"\"\"\n", - " \n", - " @property\n", - " def name(self) -> str:\n", - " return \"A*\"\n", - " \n", - " def _heuristic(self, a: Cell, b: Cell) -> int:\n", - " \"\"\"Манхэттенское расстояние.\"\"\"\n", - " return abs(a.x - b.x) + abs(a.y - b.y)\n", - " \n", - " def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]:\n", - " if start == exit_cell:\n", - " return [start]\n", - " \n", - " # Приоритетная очередь: (f_score, counter, cell)\n", - " open_set = [(0, 0, start)]\n", - " counter = 1\n", - " \n", - " came_from: Dict[Cell, Optional[Cell]] = {}\n", - " \n", - " g_score: Dict[Cell, float] = {start: 0}\n", - " f_score: Dict[Cell, float] = {start: self._heuristic(start, exit_cell)}\n", - " \n", - " visited_count = 0\n", - " \n", - " while open_set:\n", - " current_f, _, current = heapq.heappop(open_set)\n", - " visited_count += 1\n", - " \n", - " if current == exit_cell:\n", - " self._last_visited_count = visited_count\n", - " return self._reconstruct_path(came_from, start, current)\n", - " \n", - " for neighbor in maze.get_neighbors(current):\n", - " tentative_g_score = g_score.get(current, float('inf')) + 1\n", - " \n", - " if tentative_g_score < g_score.get(neighbor, float('inf')):\n", - " came_from[neighbor] = current\n", - " g_score[neighbor] = tentative_g_score\n", - " f_score[neighbor] = tentative_g_score + self._heuristic(neighbor, exit_cell)\n", - " heapq.heappush(open_set, (f_score[neighbor], counter, neighbor))\n", - " counter += 1\n", - " \n", - " self._last_visited_count = visited_count\n", - " return []\n", - " \n", - " @property\n", - " def last_visited_count(self) -> int:\n", - " return getattr(self, '_last_visited_count', 0)" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/strategiesA_star_strategy.py b/MininaVD/docs2/data2/strategiesA_star_strategy.py new file mode 100644 index 0000000..71a288a --- /dev/null +++ b/MininaVD/docs2/data2/strategiesA_star_strategy.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +import heapq +from typing import List, Dict, Optional, Tuple +from strategiesPathfinding_strategy import PathFindingStrategy +from modelsMaze import Maze +from modelsCell import Cell + +class AStarStrategy(PathFindingStrategy): + """Алгоритм A* с манхэттенской эвристикой.""" + + @property + def name(self) -> str: + return "A*" + + def _heuristic(self, a: Cell, b: Cell) -> int: + """Манхэттенское расстояние.""" + return abs(a.x - b.x) + abs(a.y - b.y) + + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + if start == exit_cell: + return [start] + + # Приоритетная очередь: (f_score, counter, cell) + open_set = [(0, 0, start)] + counter = 1 + + came_from: Dict[Cell, Optional[Cell]] = {} + + g_score: Dict[Cell, float] = {start: 0} + f_score: Dict[Cell, float] = {start: self._heuristic(start, exit_cell)} + + visited_count = 0 + + while open_set: + current_f, _, current = heapq.heappop(open_set) + visited_count += 1 + + if current == exit_cell: + self._last_visited_count = visited_count + return self._reconstruct_path(came_from, start, current) + + for neighbor in maze.get_neighbors(current): + tentative_g_score = g_score.get(current, float('inf')) + 1 + + if tentative_g_score < g_score.get(neighbor, float('inf')): + came_from[neighbor] = current + g_score[neighbor] = tentative_g_score + f_score[neighbor] = tentative_g_score + self._heuristic(neighbor, exit_cell) + heapq.heappush(open_set, (f_score[neighbor], counter, neighbor)) + counter += 1 + + self._last_visited_count = visited_count + return [] + + @property + def last_visited_count(self) -> int: + return getattr(self, '_last_visited_count', 0) + diff --git a/MininaVD/docs2/data2/strategiesBfs_strategy.ipynb b/MininaVD/docs2/data2/strategiesBfs_strategy.ipynb deleted file mode 100644 index 82be5f6..0000000 --- a/MininaVD/docs2/data2/strategiesBfs_strategy.ipynb +++ /dev/null @@ -1,75 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "263fab53-6f76-4853-b167-a51b1198ddee", - "metadata": {}, - "outputs": [], - "source": [ - "from collections import deque\n", - "from typing import List, Dict, Optional\n", - "from strategiesPathfinding_strategy import PathFindingStrategy\n", - "from modelsMaze import Maze\n", - "from modelsCell import Cell\n", - "\n", - "class BFSStrategy(PathFindingStrategy):\n", - " \"\"\"Поиск в ширину - гарантирует кратчайший путь.\"\"\"\n", - " \n", - " @property\n", - " def name(self) -> str:\n", - " return \"BFS\"\n", - " \n", - " def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]:\n", - " if start == exit_cell:\n", - " return [start]\n", - " \n", - " queue = deque([start])\n", - " came_from: Dict[Cell, Optional[Cell]] = {start: None}\n", - " visited_count = 0 # Для статистики\n", - " \n", - " while queue:\n", - " current = queue.popleft()\n", - " visited_count += 1\n", - " \n", - " if current == exit_cell:\n", - " # Сохраняем количество посещённых клеток для статистики\n", - " self._last_visited_count = visited_count\n", - " return self._reconstruct_path(came_from, start, current)\n", - " \n", - " for neighbor in maze.get_neighbors(current):\n", - " if neighbor not in came_from:\n", - " came_from[neighbor] = current\n", - " queue.append(neighbor)\n", - " \n", - " self._last_visited_count = visited_count\n", - " return []\n", - " \n", - " @property\n", - " def last_visited_count(self) -> int:\n", - " return getattr(self, '_last_visited_count', 0)" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/strategiesBfs_strategy.py b/MininaVD/docs2/data2/strategiesBfs_strategy.py new file mode 100644 index 0000000..5602391 --- /dev/null +++ b/MininaVD/docs2/data2/strategiesBfs_strategy.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from collections import deque +from typing import List, Dict, Optional +from strategiesPathfinding_strategy import PathFindingStrategy +from modelsMaze import Maze +from modelsCell import Cell + +class BFSStrategy(PathFindingStrategy): + """Поиск в ширину - гарантирует кратчайший путь.""" + + @property + def name(self) -> str: + return "BFS" + + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + if start == exit_cell: + return [start] + + queue = deque([start]) + came_from: Dict[Cell, Optional[Cell]] = {start: None} + visited_count = 0 # Для статистики + + while queue: + current = queue.popleft() + visited_count += 1 + + if current == exit_cell: + # Сохраняем количество посещённых клеток для статистики + self._last_visited_count = visited_count + return self._reconstruct_path(came_from, start, current) + + for neighbor in maze.get_neighbors(current): + if neighbor not in came_from: + came_from[neighbor] = current + queue.append(neighbor) + + self._last_visited_count = visited_count + return [] + + @property + def last_visited_count(self) -> int: + return getattr(self, '_last_visited_count', 0) + diff --git a/MininaVD/docs2/data2/strategiesDfs_strategy.ipynb b/MininaVD/docs2/data2/strategiesDfs_strategy.ipynb deleted file mode 100644 index 5ce0f5e..0000000 --- a/MininaVD/docs2/data2/strategiesDfs_strategy.ipynb +++ /dev/null @@ -1,73 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "4d8d9af1-63da-4f25-9140-a0b3c58cb96d", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import List, Dict, Optional\n", - "from strategiesPathfinding_strategy import PathFindingStrategy\n", - "from modelsMaze import Maze\n", - "from modelsCell import Cell\n", - "\n", - "class DFSStrategy(PathFindingStrategy):\n", - " \"\"\"Поиск в глубину - быстрый, но не обязательно кратчайший.\"\"\"\n", - " \n", - " @property\n", - " def name(self) -> str:\n", - " return \"DFS\"\n", - " \n", - " def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]:\n", - " if start == exit_cell:\n", - " return [start]\n", - " \n", - " stack = [start]\n", - " came_from: Dict[Cell, Optional[Cell]] = {start: None}\n", - " visited_count = 0\n", - " \n", - " while stack:\n", - " current = stack.pop()\n", - " visited_count += 1\n", - " \n", - " if current == exit_cell:\n", - " self._last_visited_count = visited_count\n", - " return self._reconstruct_path(came_from, start, current)\n", - " \n", - " for neighbor in maze.get_neighbors(current):\n", - " if neighbor not in came_from:\n", - " came_from[neighbor] = current\n", - " stack.append(neighbor)\n", - " \n", - " self._last_visited_count = visited_count\n", - " return []\n", - " \n", - " @property\n", - " def last_visited_count(self) -> int:\n", - " return getattr(self, '_last_visited_count', 0)" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/strategiesDfs_strategy.py b/MininaVD/docs2/data2/strategiesDfs_strategy.py new file mode 100644 index 0000000..74bee68 --- /dev/null +++ b/MininaVD/docs2/data2/strategiesDfs_strategy.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from typing import List, Dict, Optional +from strategiesPathfinding_strategy import PathFindingStrategy +from modelsMaze import Maze +from modelsCell import Cell + +class DFSStrategy(PathFindingStrategy): + """Поиск в глубину - быстрый, но не обязательно кратчайший.""" + + @property + def name(self) -> str: + return "DFS" + + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + if start == exit_cell: + return [start] + + stack = [start] + came_from: Dict[Cell, Optional[Cell]] = {start: None} + visited_count = 0 + + while stack: + current = stack.pop() + visited_count += 1 + + if current == exit_cell: + self._last_visited_count = visited_count + return self._reconstruct_path(came_from, start, current) + + for neighbor in maze.get_neighbors(current): + if neighbor not in came_from: + came_from[neighbor] = current + stack.append(neighbor) + + self._last_visited_count = visited_count + return [] + + @property + def last_visited_count(self) -> int: + return getattr(self, '_last_visited_count', 0) + diff --git a/MininaVD/docs2/data2/strategiesPathfinding_strategy.ipynb b/MininaVD/docs2/data2/strategiesPathfinding_strategy.ipynb deleted file mode 100644 index 6c641ac..0000000 --- a/MininaVD/docs2/data2/strategiesPathfinding_strategy.ipynb +++ /dev/null @@ -1,67 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "3f8f5923-00bf-4fc2-88e9-6a51d6183c5b", - "metadata": {}, - "outputs": [], - "source": [ - "from abc import ABC, abstractmethod\n", - "from typing import List, Optional\n", - "from modelsMaze import Maze\n", - "from modelsCell import Cell\n", - "\n", - "class PathFindingStrategy(ABC):\n", - " \"\"\"Интерфейс стратегии поиска пути (паттерн Strategy).\"\"\"\n", - " \n", - " @abstractmethod\n", - " def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]:\n", - " \"\"\"\n", - " Найти путь от start до exit_cell.\n", - " Возвращает список клеток пути (включая start и exit) или пустой список.\n", - " \"\"\"\n", - " pass\n", - " \n", - " @property\n", - " @abstractmethod\n", - " def name(self) -> str:\n", - " \"\"\"Имя стратегии для отчётов.\"\"\"\n", - " pass\n", - " \n", - " def _reconstruct_path(self, came_from: dict, start: Cell, current: Cell) -> List[Cell]:\n", - " \"\"\"Восстановить путь из словаря предков.\"\"\"\n", - " path = []\n", - " while current != start:\n", - " path.append(current)\n", - " current = came_from.get(current)\n", - " if current is None:\n", - " return []\n", - " path.append(start)\n", - " path.reverse()\n", - " return path" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/strategiesPathfinding_strategy.py b/MininaVD/docs2/data2/strategiesPathfinding_strategy.py new file mode 100644 index 0000000..579dea0 --- /dev/null +++ b/MininaVD/docs2/data2/strategiesPathfinding_strategy.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from abc import ABC, abstractmethod +from typing import List, Optional +from modelsMaze import Maze +from modelsCell import Cell + +class PathFindingStrategy(ABC): + """Интерфейс стратегии поиска пути (паттерн Strategy).""" + + @abstractmethod + def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> List[Cell]: + """ + Найти путь от start до exit_cell. + Возвращает список клеток пути (включая start и exit) или пустой список. + """ + pass + + @property + @abstractmethod + def name(self) -> str: + """Имя стратегии для отчётов.""" + pass + + def _reconstruct_path(self, came_from: dict, start: Cell, current: Cell) -> List[Cell]: + """Восстановить путь из словаря предков.""" + path = [] + while current != start: + path.append(current) + current = came_from.get(current) + if current is None: + return [] + path.append(start) + path.reverse() + return path + diff --git a/MininaVD/docs2/data2/visualizationConsole_view.ipynb b/MininaVD/docs2/data2/visualizationConsole_view.ipynb deleted file mode 100644 index c3e817b..0000000 --- a/MininaVD/docs2/data2/visualizationConsole_view.ipynb +++ /dev/null @@ -1,116 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "9d15670f-4ae1-48f5-801c-7a1a84cafba3", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from typing import List, Optional, Set\n", - "from modelsMaze import Maze\n", - "from modelsCell import Cell\n", - "from visualizationObserver import Observer\n", - "\n", - "class ConsoleView(Observer):\n", - " \"\"\"Консольная визуализация лабиринта.\"\"\"\n", - " \n", - " # Символы для отображения\n", - " SYMBOLS = {\n", - " 'wall': '█',\n", - " 'path': '·',\n", - " 'start': 'S',\n", - " 'exit': 'E',\n", - " 'player': 'P',\n", - " 'solution': '★'\n", - " }\n", - " \n", - " def __init__(self, maze: Maze):\n", - " self.maze = maze\n", - " self.player_pos: Optional[Cell] = None\n", - " self.solution_path: Set[Cell] = set()\n", - " self.messages: List[str] = []\n", - " \n", - " def update(self, event: str) -> None:\n", - " \"\"\"Обработка событий от MazeSolver.\"\"\"\n", - " self.messages.append(f\"[СОБЫТИЕ] {event}\")\n", - " self.render()\n", - " \n", - " def set_solution_path(self, path: List[Cell]) -> None:\n", - " \"\"\"Установить найденный путь для отображения.\"\"\"\n", - " self.solution_path = set(path)\n", - " \n", - " def set_player_position(self, cell: Cell) -> None:\n", - " \"\"\"Установить позицию игрока.\"\"\"\n", - " self.player_pos = cell\n", - " \n", - " def render(self) -> None:\n", - " \"\"\"Отрисовать лабиринт в консоли.\"\"\"\n", - " # Очистка консоли (опционально)\n", - " # os.system('cls' if os.name == 'nt' else 'clear')\n", - " \n", - " print(\"\\n\" + \"=\" * (self.maze.width * 2 + 4))\n", - " print(f\"Лабиринт {self.maze.width}×{self.maze.height}\")\n", - " print(\"=\" * (self.maze.width * 2 + 4))\n", - " \n", - " for y in range(self.maze.height):\n", - " row = \"\"\n", - " for x in range(self.maze.width):\n", - " cell = self.maze.get_cell(x, y)\n", - " if not cell:\n", - " row += \" \"\n", - " continue\n", - " \n", - " if self.player_pos and cell == self.player_pos:\n", - " row += self.SYMBOLS['player'] + \" \"\n", - " elif cell.is_start:\n", - " row += self.SYMBOLS['start'] + \" \"\n", - " elif cell.is_exit:\n", - " row += self.SYMBOLS['exit'] + \" \"\n", - " elif cell in self.solution_path:\n", - " row += self.SYMBOLS['solution'] + \" \"\n", - " elif cell.is_wall:\n", - " row += self.SYMBOLS['wall'] * 2\n", - " else:\n", - " row += self.SYMBOLS['path'] * 2\n", - " print(row)\n", - " \n", - " print(\"-\" * (self.maze.width * 2 + 4))\n", - " \n", - " # Показать последние сообщения\n", - " if self.messages:\n", - " print(\"Последние события:\")\n", - " for msg in self.messages[-3:]:\n", - " print(f\" {msg}\")\n", - " \n", - " print()\n", - " \n", - " def clear_messages(self) -> None:\n", - " \"\"\"Очистить сообщения.\"\"\"\n", - " self.messages.clear()" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/visualizationConsole_view.py b/MininaVD/docs2/data2/visualizationConsole_view.py new file mode 100644 index 0000000..088c34f --- /dev/null +++ b/MininaVD/docs2/data2/visualizationConsole_view.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +import os +from typing import List, Optional, Set +from modelsMaze import Maze +from modelsCell import Cell +from visualizationObserver import Observer + +class ConsoleView(Observer): + """Консольная визуализация лабиринта.""" + + # Символы для отображения + SYMBOLS = { + 'wall': '█', + 'path': '·', + 'start': 'S', + 'exit': 'E', + 'player': 'P', + 'solution': '★' + } + + def __init__(self, maze: Maze): + self.maze = maze + self.player_pos: Optional[Cell] = None + self.solution_path: Set[Cell] = set() + self.messages: List[str] = [] + + def update(self, event: str) -> None: + """Обработка событий от MazeSolver.""" + self.messages.append(f"[СОБЫТИЕ] {event}") + self.render() + + def set_solution_path(self, path: List[Cell]) -> None: + """Установить найденный путь для отображения.""" + self.solution_path = set(path) + + def set_player_position(self, cell: Cell) -> None: + """Установить позицию игрока.""" + self.player_pos = cell + + def render(self) -> None: + """Отрисовать лабиринт в консоли.""" + # Очистка консоли (опционально) + # os.system('cls' if os.name == 'nt' else 'clear') + + print("\n" + "=" * (self.maze.width * 2 + 4)) + print(f"Лабиринт {self.maze.width}×{self.maze.height}") + print("=" * (self.maze.width * 2 + 4)) + + for y in range(self.maze.height): + row = "" + for x in range(self.maze.width): + cell = self.maze.get_cell(x, y) + if not cell: + row += " " + continue + + if self.player_pos and cell == self.player_pos: + row += self.SYMBOLS['player'] + " " + elif cell.is_start: + row += self.SYMBOLS['start'] + " " + elif cell.is_exit: + row += self.SYMBOLS['exit'] + " " + elif cell in self.solution_path: + row += self.SYMBOLS['solution'] + " " + elif cell.is_wall: + row += self.SYMBOLS['wall'] * 2 + else: + row += self.SYMBOLS['path'] * 2 + print(row) + + print("-" * (self.maze.width * 2 + 4)) + + # Показать последние сообщения + if self.messages: + print("Последние события:") + for msg in self.messages[-3:]: + print(f" {msg}") + + print() + + def clear_messages(self) -> None: + """Очистить сообщения.""" + self.messages.clear() + diff --git a/MininaVD/docs2/data2/visualizationObserver.ipynb b/MininaVD/docs2/data2/visualizationObserver.ipynb deleted file mode 100644 index 41ff7bc..0000000 --- a/MininaVD/docs2/data2/visualizationObserver.ipynb +++ /dev/null @@ -1,43 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "8998420f-2c73-4f8a-a102-cc403777f3e9", - "metadata": {}, - "outputs": [], - "source": [ - "from abc import ABC, abstractmethod\n", - "\n", - "class Observer(ABC):\n", - " \"\"\"Интерфейс наблюдателя (паттерн Observer).\"\"\"\n", - " \n", - " @abstractmethod\n", - " def update(self, event: str) -> None:\n", - " \"\"\"Обработчик события.\"\"\"\n", - " pass" - ] - } - ], - "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 -} diff --git a/MininaVD/docs2/data2/visualizationObserver.py b/MininaVD/docs2/data2/visualizationObserver.py new file mode 100644 index 0000000..1f6b712 --- /dev/null +++ b/MininaVD/docs2/data2/visualizationObserver.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +from abc import ABC, abstractmethod + +class Observer(ABC): + """Интерфейс наблюдателя (паттерн Observer).""" + + @abstractmethod + def update(self, event: str) -> None: + """Обработчик события.""" + pass +