diff --git a/nehoroshevaa/docs/empty.txt.png b/nehoroshevaa/docs/empty.txt.png new file mode 100644 index 0000000..743138b Binary files /dev/null and b/nehoroshevaa/docs/empty.txt.png differ diff --git a/nehoroshevaa/docs/large.txt.png b/nehoroshevaa/docs/large.txt.png new file mode 100644 index 0000000..e74daba Binary files /dev/null and b/nehoroshevaa/docs/large.txt.png differ diff --git a/nehoroshevaa/docs/medium.txt.png b/nehoroshevaa/docs/medium.txt.png new file mode 100644 index 0000000..6b61498 Binary files /dev/null and b/nehoroshevaa/docs/medium.txt.png differ diff --git a/nehoroshevaa/docs/no exit.txt.png b/nehoroshevaa/docs/no exit.txt.png new file mode 100644 index 0000000..fd65e32 Binary files /dev/null and b/nehoroshevaa/docs/no exit.txt.png differ diff --git a/nehoroshevaa/docs/report.md b/nehoroshevaa/docs/report.md new file mode 100644 index 0000000..0d1ede9 --- /dev/null +++ b/nehoroshevaa/docs/report.md @@ -0,0 +1,174 @@ +1. Цель работы + +Разработать расширяемую программу для поиска пути в лабиринте, поддерживающую загрузку карты из файла, выбор алгоритма поиска, сбор статистики и сравнение эффективности различных стратегий. + +В ходе работы применены паттерны проектирования для разделения ответственности между компонентами системы и повышения гибкости архитектуры. + +2. Постановка задачи + +Лабиринт задаётся в текстовом файле символами: + +# — стена +пробел — проходимая клетка +S — стартовая позиция +E — выход + +Программа должна: + +загружать лабиринт из файла; +строить внутреннюю модель представления; +находить путь от старта до выхода; +поддерживать выбор алгоритма поиска; +собирать статистику работы алгоритмов; +визуализировать результат; +выполнять сравнительный анализ стратегий. +3. Использованные паттерны проектирования +3.1 Builder + +Паттерн Builder применяется для создания объекта лабиринта из текстового файла. Он инкапсулирует процесс парсинга, валидации и построения структуры данных. + +Преимущества: + +отделение логики загрузки от логики использования; +возможность добавления новых форматов (JSON, бинарные файлы); +упрощение расширения системы. +3.2 Strategy + +Паттерн Strategy используется для реализации различных алгоритмов поиска пути. + +Реализованы следующие стратегии: + +BFS; +DFS; +A*. + +Преимущества: + +возможность переключения алгоритма во время выполнения; +отсутствие зависимости MazeSolver от конкретной реализации; +лёгкое добавление новых алгоритмов. +3.3 Observer + +Паттерн Observer применяется для уведомления интерфейса о событиях выполнения программы (например, нахождение пути). + +Преимущества: + +разделение логики поиска и отображения; +возможность замены интерфейса без изменения алгоритмов; +расширяемость системы визуализации. +3.4 Command (дополнительно) + +Паттерн Command используется для представления действий пользователя как объектов (например, перемещение и отмена хода). + +Преимущества: + +поддержка undo/redo; +хранение истории действий; +разделение команд и логики исполнения. +4. Архитектура системы + +Система состоит из следующих основных компонентов: + +Cell — описание клетки лабиринта; +Maze — структура лабиринта и логика соседей; +MazeBuilder — загрузка лабиринта из файла; +PathFindingStrategy — интерфейс алгоритмов поиска; +реализации стратегий: BFS, DFS, A*; +MazeSolver — оркестратор поиска и сбор статистики; +SearchStats — структура результатов; +ConsoleView — визуализация результата; +Command (опционально) — управление действиями пользователя. +5. Описание ключевых компонентов +Cell + +Хранит координаты клетки и её тип (стена, старт, выход). Определяет проходимость клетки. + +Maze + +Представляет лабиринт в виде двумерного массива клеток и предоставляет методы доступа к соседним узлам. + +TextFileMazeBuilder + +Отвечает за чтение текстового файла и построение объекта Maze. + +BFS / DFS / A* + +Реализуют разные стратегии поиска пути: + +BFS — гарантирует кратчайший путь; +DFS — быстрый, но не оптимальный; +A* — использует эвристику и уменьшает число посещённых клеток. +MazeSolver + +Запускает выбранный алгоритм, измеряет время выполнения и формирует статистику. + +SearchStats + +Содержит: + +время выполнения; +количество посещённых клеток; +длину найденного пути. +ConsoleView + +Отвечает за отображение лабиринта и найденного пути в консоли. + +6. Экспериментальная часть +6.1 Тестовые данные + +Для анализа использовались следующие типы лабиринтов: + +небольшой 10×10 с простым маршрутом; +средний 50×50 с наличием тупиков; +большой 100×100 со сложной структурой; +пустой лабиринт без стен; +лабиринт без решения. +6.2 Методика измерений + +Для каждой комбинации лабиринта и алгоритма выполнялось несколько запусков. + +Фиксировались следующие показатели: + +время выполнения (мс); +количество посещённых клеток; +длина пути. + +Результаты сохранялись в CSV-файл для последующего анализа. + +7. Анализ результатов +BFS + +Обеспечивает нахождение кратчайшего пути при равных весах переходов. Однако может исследовать значительное количество клеток. + +DFS + +Быстро находит решение в некоторых случаях, но не гарантирует оптимальность пути и может исследовать нерелевантные области. + +A* + +Использует эвристику (Манхэттенское расстояние), что позволяет существенно сократить количество посещённых узлов и ускорить поиск. + +Лабиринт без решения + +Во всех алгоритмах происходит полный или частичный обход доступной области, после чего возвращается отсутствие пути. + +Вывод по алгоритмам +BFS — оптимален по длине пути; +DFS — прост, но нестабилен; +A* — наиболее эффективен по числу посещений и времени работы. +8. Роль ООП и паттернов + +Использование ООП и паттернов проектирования позволило: + +разделить систему на независимые компоненты; +упростить расширение алгоритмов; +отделить визуализацию от логики поиска; +обеспечить возможность добавления новых форматов данных и алгоритмов. + +Без использования паттернов код был бы менее структурирован и сложнее в сопровождении. + +9. Вывод + +В рамках работы была реализована система поиска пути в лабиринте с возможностью выбора алгоритма и анализа их эффективности. + +Применение паттернов Builder, Strategy, Observer и Command позволило создать гибкую и расширяемую архитектуру. Экспериментальная часть показала, что выбор алгоритма существенно влияет на количество посещённых клеток и время выполнения. \ No newline at end of file diff --git a/nehoroshevaa/docs/results.csv b/nehoroshevaa/docs/results.csv new file mode 100644 index 0000000..9e0b898 --- /dev/null +++ b/nehoroshevaa/docs/results.csv @@ -0,0 +1,16 @@ +maze,strategy,time_ms,visited_cells,path_length +small.txt,BFS,0.024,8,7 +small.txt,DFSStrategy,0.011,8,7 +small.txt,AStarStrategy,0.019,8,7 +medium.txt,BFS,0.058,53,23 +medium.txt,DFSStrategy,0.033,31,31 +medium.txt,AStarStrategy,0.066,46,23 +large.txt,BFS,0.854,718,431 +large.txt,DFSStrategy,0.460,451,431 +large.txt,AStarStrategy,0.827,591,431 +no exit.txt,BFS,0.005,1,0 +no exit.txt,DFSStrategy,0.002,1,0 +no exit.txt,AStarStrategy,0.003,1,0 +empty.txt,BFS,0.115,100,19 +empty.txt,DFSStrategy,0.065,55,55 +empty.txt,AStarStrategy,0.156,100,19 diff --git a/nehoroshevaa/docs/small.txt.png b/nehoroshevaa/docs/small.txt.png new file mode 100644 index 0000000..cfff4d0 Binary files /dev/null and b/nehoroshevaa/docs/small.txt.png differ diff --git a/nehoroshevaa/task2/__init__.py b/nehoroshevaa/task2/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nehoroshevaa/task2/builders/__init__.py b/nehoroshevaa/task2/builders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nehoroshevaa/task2/builders/maze_builder.py b/nehoroshevaa/task2/builders/maze_builder.py new file mode 100644 index 0000000..42d4710 --- /dev/null +++ b/nehoroshevaa/task2/builders/maze_builder.py @@ -0,0 +1,4 @@ +class MazeBuilder: + + def build_from_file(self, filename): + raise NotImplementedError \ No newline at end of file diff --git a/nehoroshevaa/task2/builders/text_file_maze_builder.py b/nehoroshevaa/task2/builders/text_file_maze_builder.py new file mode 100644 index 0000000..9c0cbe9 --- /dev/null +++ b/nehoroshevaa/task2/builders/text_file_maze_builder.py @@ -0,0 +1,67 @@ +from builders.maze_builder import MazeBuilder +from models.cell import Cell +from models.maze import Maze + + +class TextFileMazeBuilder(MazeBuilder): + + def create_cell(self, symbol, x, y): + + if symbol == "#": + return Cell(x, y, is_wall=True) + + if symbol == "S": + return Cell(x, y, is_start=True) + + if symbol == "E": + return Cell(x, y, is_exit=True) + + if symbol == " ": + return Cell(x, y) + + raise ValueError(f"Unknown symbol: {symbol}") + + def build_from_file(self, filename): + + with open(filename, "r", encoding="utf-8") as file: + rows = [line.rstrip("\n") for line in file] + + if not rows: + raise ValueError("File is empty") + + width = len(rows[0]) + + for row in rows: + if len(row) != width: + raise ValueError("Maze rows must have same length") + + cells = [] + + start_cell = None + exit_cell = None + + for y, row in enumerate(rows): + + current_row = [] + + for x, symbol in enumerate(row): + + cell = self.create_cell(symbol, x, y) + + if cell.is_start: + start_cell = cell + + if cell.is_exit: + exit_cell = cell + + current_row.append(cell) + + cells.append(current_row) + + if start_cell is None: + raise ValueError("Start not found") + + if exit_cell is None: + raise ValueError("Exit not found") + + return Maze(cells, start_cell, exit_cell) \ No newline at end of file diff --git a/nehoroshevaa/task2/experiments/__init__.py b/nehoroshevaa/task2/experiments/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nehoroshevaa/task2/experiments/benchmark.py b/nehoroshevaa/task2/experiments/benchmark.py new file mode 100644 index 0000000..ce5c3df --- /dev/null +++ b/nehoroshevaa/task2/experiments/benchmark.py @@ -0,0 +1,60 @@ +from builders.text_file_maze_builder import TextFileMazeBuilder + +from strategies.bfs_strategy import BFS +from strategies.dfs_strategy import DFSStrategy +from strategies.astar_strategy import AStarStrategy + +from solver.maze_solver import MazeSolver + + +mazes = [ + "small.txt", + "medium.txt", + "large.txt", + "no exit.txt", + "empty.txt" +] + +strategies = [ + BFS(), + DFSStrategy(), + AStarStrategy() +] + +results = [] + +builder = TextFileMazeBuilder() + + +for maze_file in mazes: + + maze = builder.build_from_file( + f"mazes/{maze_file}" + ) + + for strategy in strategies: + + solver = MazeSolver( + maze, + strategy + ) + + stats = solver.solve() + + stats.maze_name = maze_file + + results.append(stats) + + +with open( + "experiments/results.csv", + "w" +) as file: + + file.write( + "maze,strategy,time_ms,visited_cells,path_length\n" + ) + + for stat in results: + + file.write(stat.to_csv_row()) \ No newline at end of file diff --git a/nehoroshevaa/task2/experiments/plot_results.py b/nehoroshevaa/task2/experiments/plot_results.py new file mode 100644 index 0000000..4f54deb --- /dev/null +++ b/nehoroshevaa/task2/experiments/plot_results.py @@ -0,0 +1,25 @@ +import pandas as pd +import matplotlib.pyplot as plt + + +data = pd.read_csv("results.csv") + + +for maze_name in data["maze"].unique(): + + maze_data = data[data["maze"] == maze_name] + + plt.figure() + + plt.bar( + maze_data["strategy"], + maze_data["time_ms"] + ) + + plt.title(f"{maze_name} maze") + + plt.ylabel("Time (ms)") + + plt.savefig( + f"{maze_name}.png" + ) \ No newline at end of file diff --git a/nehoroshevaa/task2/main.py b/nehoroshevaa/task2/main.py new file mode 100644 index 0000000..20332c2 --- /dev/null +++ b/nehoroshevaa/task2/main.py @@ -0,0 +1,66 @@ +from builders.text_file_maze_builder import TextFileMazeBuilder + +from strategies.bfs_strategy import BFS +from strategies.dfs_strategy import DFSStrategy +from strategies.astar_strategy import AStarStrategy + +from solver.maze_solver import MazeSolver + +from observer.console_view import ConsoleView + + +mazes = { + "1": "small.txt", + "2": "medium.txt", + "3": "large.txt", + "4": "no_exit.txt" +} + +strategies = { + "1": BFS(), + "2": DFSStrategy(), + "3": AStarStrategy() +} + + +print("Choose maze:") +print("1 - small") +print("2 - medium") +print("3 - large") +print("4 - no_exit") + +maze_choice = input("> ") + +print() + +print("Choose strategy:") +print("1 - BFS") +print("2 - DFS") +print("3 - A*") + +strategy_choice = input("> ") + + +builder = TextFileMazeBuilder() + +maze = builder.build_from_file( + f"mazes/{mazes[maze_choice]}" +) + +strategy = strategies[strategy_choice] + + +solver = MazeSolver( + maze, + strategy +) + +stats = solver.solve() + + +print(stats) + + +view = ConsoleView(maze) + +solver.attach(view) \ No newline at end of file diff --git a/nehoroshevaa/task2/mazes/__init__.py b/nehoroshevaa/task2/mazes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nehoroshevaa/task2/mazes/empty.txt b/nehoroshevaa/task2/mazes/empty.txt new file mode 100644 index 0000000..dfad92f --- /dev/null +++ b/nehoroshevaa/task2/mazes/empty.txt @@ -0,0 +1,10 @@ +S + + + + + + + + + E \ No newline at end of file diff --git a/nehoroshevaa/task2/mazes/large.txt b/nehoroshevaa/task2/mazes/large.txt new file mode 100644 index 0000000..6eba40a --- /dev/null +++ b/nehoroshevaa/task2/mazes/large.txt @@ -0,0 +1,50 @@ +S # # # # + ####### ### # # ########### ##### # ##### ##### # + # # # # # # # # # # +## # # ### ####### ##### ####### # ### ######### # + # # # # # # # # # # # # # + ####### # # # # ### ##### # ### ### # # # # ### # + # # # # # # # # # # # # # # # # # + # ### # ### # ### ### # # ### ### ####### # # ### + # # # # # # # # # # # # # # + # # ############# # ### ### # ######### # # ### # + # # # # # # # # # + ########### ########### # ##### ### ### # # # ### + # # # # # # # # # # # # # # + # # ####### # ### # ##### ### ### ### ### # # # # + # # # # # # # # # # # # # # # # +###### ### ### # # # # # # # ### ### ##### # # # # + # # # # # # # # # # # # # # # + ### # # ######### ### # # # # ####### ##### # # # + # # # # # # # # # # # # # # + ### ### # ##### # # ######### # # # # ##### # # # + # # # # # # # # # # # # +## # ######### # # ### ### # ### ######### ##### # + # # # # # # # # # # # # + ##### # ### # ### ##### # # # ####### ##### # # # + # # # # # # # # # # # # # # # +## # ##### # # ##### ##### ### ### # ### # # # ### + # # # # # # # # # # # # # + ##### # ### # # ##### ### # ### ######### # ##### + # # # # # # # # # # # + # ####### ######### ### ####### # # ####### ### # + # # # # # # # # # # # # # # + # # ####### # # ##### # # ### ### # # # # ##### # + # # # # # # # # # # # # # # # # + # ##### # ####### # # # # # ### # ### # # # ### # + # # # # # # # # # # # # # # # + # ########### # ### ####### ### # ### # # # # # # + # # # # # # # # # # # # # + # # ####### ##### ########### ##### # # ##### # # + # # # # # # # # # # # + ### ### ### # ############### # # # ##### ### ### + # # # # # # # # # # # # # # + # ### ### # ### ##### # # # # # ##### # ### # # # + # # # # # # # # # # # # # # + # # ####### # ### ######### ######### ### # # # # + # # # # # # # # # # # # # # # + ##### # ####### # # # ### # # # # # ### ### # # # + # # # # # # # # # # # # # # # +## ### ##### ####### ### # # ### ##### # ### ### # + # # # # +################################################ E \ No newline at end of file diff --git a/nehoroshevaa/task2/mazes/medium.txt b/nehoroshevaa/task2/mazes/medium.txt new file mode 100644 index 0000000..439365c --- /dev/null +++ b/nehoroshevaa/task2/mazes/medium.txt @@ -0,0 +1,10 @@ +S # + ### ### # + # # # +## # # ### + # # # + ####### # + # # +## # ##### + +######## E \ No newline at end of file diff --git a/nehoroshevaa/task2/mazes/no exit.txt b/nehoroshevaa/task2/mazes/no exit.txt new file mode 100644 index 0000000..cea04f9 --- /dev/null +++ b/nehoroshevaa/task2/mazes/no exit.txt @@ -0,0 +1,10 @@ +S######### +# # +######## # +# # +# ###### # +# # # +###### # # +# # # +# ######## +######## E \ No newline at end of file diff --git a/nehoroshevaa/task2/mazes/small.txt b/nehoroshevaa/task2/mazes/small.txt new file mode 100644 index 0000000..28f1058 --- /dev/null +++ b/nehoroshevaa/task2/mazes/small.txt @@ -0,0 +1,5 @@ +##### +# S # +# ### +# E +##### \ No newline at end of file diff --git a/nehoroshevaa/task2/models/__init__.py b/nehoroshevaa/task2/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nehoroshevaa/task2/models/cell.py b/nehoroshevaa/task2/models/cell.py new file mode 100644 index 0000000..9631a47 --- /dev/null +++ b/nehoroshevaa/task2/models/cell.py @@ -0,0 +1,14 @@ +class Cell: + def __init__(self, x, y, is_wall=False, is_start=False, is_exit=False): + self.x = x + self.y = y + + self.is_wall = is_wall + self.is_start = is_start + self.is_exit = is_exit + + def is_passable(self): + return not self.is_wall + + def __repr__(self): + return f"Cell({self.x}, {self.y})" \ No newline at end of file diff --git a/nehoroshevaa/task2/models/maze.py b/nehoroshevaa/task2/models/maze.py new file mode 100644 index 0000000..ec74f1f --- /dev/null +++ b/nehoroshevaa/task2/models/maze.py @@ -0,0 +1,51 @@ +from models.cell import Cell + + +class Maze: + + def __init__(self, cells, start_cell, exit_cell): + + self.cells = cells + + self.height = len(cells) + self.width = len(cells[0]) + + self.start_cell = start_cell + self.exit_cell = exit_cell + + def get_cell(self, x, y): + + if 0 <= x < self.width and 0 <= y < self.height: + return self.cells[y][x] + + return None + + def check_cell(self, x, y): + + cell = self.get_cell(x, y) + + return cell and cell.is_passable() + + def get_neighbors(self, cell: Cell): + + directions = [ + (0, -1), + (0, 1), + (-1, 0), + (1, 0) + ] + + neighbors = [] + + for dx, dy in directions: + + x = cell.x + dx + y = cell.y + dy + + if self.check_cell(x, y): + neighbors.append(self.get_cell(x, y)) + + return neighbors + + def __repr__(self): + return f"Maze({self.width}x{self.height})" \ No newline at end of file diff --git a/nehoroshevaa/task2/models/search_stats.py b/nehoroshevaa/task2/models/search_stats.py new file mode 100644 index 0000000..3f23568 --- /dev/null +++ b/nehoroshevaa/task2/models/search_stats.py @@ -0,0 +1,34 @@ +class SearchStats: + + def __init__(self, + strategy, + maze_name, + duration, + visited_cells, + path_length): + + self.strategy = strategy + self.maze_name = maze_name + self.duration = duration + self.visited_cells = visited_cells + self.path_length = path_length + + def to_csv_row(self): + return ( + f"{self.maze_name}," + f"{self.strategy}," + f"{self.duration:.3f}," + f"{self.visited_cells}," + f"{self.path_length}\n" + ) + + def __str__(self): + return ( + f"\n=== SEARCH RESULT ===\n" + f"Strategy : {self.strategy}\n" + f"Maze : {self.maze_name}\n" + f"Time (ms) : {self.duration:.3f}\n" + f"Visited cells : {self.visited_cells}\n" + f"Path length : {self.path_length}\n" + f"=====================\n" + ) \ No newline at end of file diff --git a/nehoroshevaa/task2/observer/__init__.py b/nehoroshevaa/task2/observer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nehoroshevaa/task2/observer/console_view.py b/nehoroshevaa/task2/observer/console_view.py new file mode 100644 index 0000000..6e17d87 --- /dev/null +++ b/nehoroshevaa/task2/observer/console_view.py @@ -0,0 +1,64 @@ +import os + +from observer.observer import Observer + +from observer.maze_event import MazeEventType + + +class ConsoleView(Observer): + + def __init__(self, maze=None): + + self.maze = maze + + self.path = [] + + def update(self, event): + + if event.event_type == MazeEventType.PATH_FOUND: + + self.path = event.data + + self.render() + + def render(self): + + os.system( + "cls" if os.name == "nt" + else "clear" + ) + + path_positions = { + (cell.x, cell.y) + for cell in self.path + } + + for row in self.maze.cells: + + line = "" + + for cell in row: + + position = (cell.x, cell.y) + + if cell.is_wall: + + line += "#" + + elif cell.is_start: + + line += "S" + + elif cell.is_exit: + + line += "E" + + elif position in path_positions: + + line += "*" + + else: + + line += " " + + print(line) \ No newline at end of file diff --git a/nehoroshevaa/task2/observer/maze_event.py b/nehoroshevaa/task2/observer/maze_event.py new file mode 100644 index 0000000..62ceb8e --- /dev/null +++ b/nehoroshevaa/task2/observer/maze_event.py @@ -0,0 +1,17 @@ +from enum import Enum + + +class MazeEventType(Enum): + + MAZE_LOADED = 1 + + PATH_FOUND = 2 + + +class MazeEvent: + + def __init__(self, event_type, data=None): + + self.event_type = event_type + + self.data = data \ No newline at end of file diff --git a/nehoroshevaa/task2/observer/observer.py b/nehoroshevaa/task2/observer/observer.py new file mode 100644 index 0000000..76f00c0 --- /dev/null +++ b/nehoroshevaa/task2/observer/observer.py @@ -0,0 +1,5 @@ +class Observer: + + def update(self, event): + + raise NotImplementedError \ No newline at end of file diff --git a/nehoroshevaa/task2/observer/subject.py b/nehoroshevaa/task2/observer/subject.py new file mode 100644 index 0000000..984b39e --- /dev/null +++ b/nehoroshevaa/task2/observer/subject.py @@ -0,0 +1,19 @@ +class Subject: + + def __init__(self): + + self.observers = [] + + def attach(self, observer): + + self.observers.append(observer) + + def detach(self, observer): + + self.observers.remove(observer) + + def notify(self, event): + + for observer in self.observers: + + observer.update(event) \ No newline at end of file diff --git a/nehoroshevaa/task2/solver/__init__.py b/nehoroshevaa/task2/solver/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nehoroshevaa/task2/solver/maze_solver.py b/nehoroshevaa/task2/solver/maze_solver.py new file mode 100644 index 0000000..c1e6b5a --- /dev/null +++ b/nehoroshevaa/task2/solver/maze_solver.py @@ -0,0 +1,43 @@ +import time + +from observer.subject import Subject +from observer.maze_event import MazeEvent, MazeEventType +from models.search_stats import SearchStats + + +class MazeSolver(Subject): + + def __init__(self, maze, strategy): + super().__init__() + self.maze = maze + self.strategy = strategy + + def set_strategy(self, strategy): + self.strategy = strategy + + def solve(self): + + 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() + + self.notify( + MazeEvent( + MazeEventType.PATH_FOUND, + path + ) + ) + + return SearchStats( + strategy=self.strategy.__class__.__name__, + maze_name="maze", + duration=(end_time - start_time) * 1000, + visited_cells=visited, + path_length=len(path) + ) \ No newline at end of file diff --git a/nehoroshevaa/task2/strategies/__init__.py b/nehoroshevaa/task2/strategies/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/nehoroshevaa/task2/strategies/astar_strategy.py b/nehoroshevaa/task2/strategies/astar_strategy.py new file mode 100644 index 0000000..2e49ec5 --- /dev/null +++ b/nehoroshevaa/task2/strategies/astar_strategy.py @@ -0,0 +1,61 @@ +import heapq +import itertools + +from strategies.pathfinding_strategy import PathFindingStrategy + + +class AStarStrategy(PathFindingStrategy): + + def heuristic(self, cell, exit_cell): + return abs(cell.x - exit_cell.x) + abs(cell.y - exit_cell.y) + + def find_path(self, maze, start_cell, exit_cell): + + open_set = [] + counter = itertools.count() + + heapq.heappush(open_set, (0, next(counter), start_cell)) + + parents = {start_cell: None} + g_score = {start_cell: 0} + + visited = set() + visited_count = 0 + + while open_set: + + _, _, current = heapq.heappop(open_set) + + if current in visited: + continue + + visited.add(current) + visited_count += 1 + + if current == exit_cell: + + path = [] + while current is not None: + path.append(current) + current = parents[current] + + path.reverse() + return path, visited_count + + for neighbor in maze.get_neighbors(current): + + tentative_g = g_score[current] + 1 + + if neighbor not in g_score or tentative_g < g_score[neighbor]: + + parents[neighbor] = current + g_score[neighbor] = tentative_g + + f_score = tentative_g + self.heuristic(neighbor, exit_cell) + + heapq.heappush( + open_set, + (f_score, next(counter), neighbor) + ) + + return [], visited_count \ No newline at end of file diff --git a/nehoroshevaa/task2/strategies/bfs_strategy.py b/nehoroshevaa/task2/strategies/bfs_strategy.py new file mode 100644 index 0000000..37c7b79 --- /dev/null +++ b/nehoroshevaa/task2/strategies/bfs_strategy.py @@ -0,0 +1,40 @@ +from collections import deque + +from strategies.pathfinding_strategy import PathFindingStrategy + +class BFS(PathFindingStrategy): + + def find_path(self, maze, start_cell, exit_cell): + + queue = deque([start_cell]) + + parents = {start_cell: None} + visited = {start_cell} + + visited_count = 0 + + while queue: + + current = queue.popleft() + visited_count += 1 + + if current == exit_cell: + path = [] + + while current is not None: + path.append(current) + current = parents[current] + + path.reverse() + return path, visited_count + + for neighbor in maze.get_neighbors(current): + + if neighbor in visited: + continue + + visited.add(neighbor) + parents[neighbor] = current + queue.append(neighbor) + + return [], visited_count \ No newline at end of file diff --git a/nehoroshevaa/task2/strategies/dfs_strategy.py b/nehoroshevaa/task2/strategies/dfs_strategy.py new file mode 100644 index 0000000..7f44975 --- /dev/null +++ b/nehoroshevaa/task2/strategies/dfs_strategy.py @@ -0,0 +1,39 @@ +from strategies.pathfinding_strategy import PathFindingStrategy + + +class DFSStrategy(PathFindingStrategy): + + def find_path(self, maze, start_cell, exit_cell): + + stack = [start_cell] + + parents = {start_cell: None} + visited = {start_cell} + + visited_count = 0 + + while stack: + + current = stack.pop() + visited_count += 1 + + if current == exit_cell: + path = [] + + while current is not None: + path.append(current) + current = parents[current] + + path.reverse() + return path, visited_count + + for neighbor in maze.get_neighbors(current): + + if neighbor in visited: + continue + + visited.add(neighbor) + parents[neighbor] = current + stack.append(neighbor) + + return [], visited_count \ No newline at end of file diff --git a/nehoroshevaa/task2/strategies/pathfinding_strategy.py b/nehoroshevaa/task2/strategies/pathfinding_strategy.py new file mode 100644 index 0000000..56abcd5 --- /dev/null +++ b/nehoroshevaa/task2/strategies/pathfinding_strategy.py @@ -0,0 +1,4 @@ +class PathFindingStrategy: + + def find_path(self, maze, start_cell, exit_cell): + raise NotImplementedError \ No newline at end of file