diff --git a/romanovpv/task 2/docs/data/big_maze.txt b/romanovpv/task 2/docs/data/big_maze.txt new file mode 100644 index 0000000..0c56bf5 --- /dev/null +++ b/romanovpv/task 2/docs/data/big_maze.txt @@ -0,0 +1,100 @@ +#################################################################################################### +#S ### ## # ## ## # # # # ## # ## ## # #### ##### # ## ## # ## # ### # +# # ### # ## # # # # ## # # # # # # ## ## # # ### ## # # # +# # # # ## # # ## ## # # # ## # # # #### # # ### ### # ## # # # # +# ### ### ## ## # ### # ## # # # # # # ## ## ## ## # #### ### ## # ## +# # # # ## # # # # ## ## # # ## # #### ### #### # # ### ### # ### +# ## # # # # # ## # ## ## ## ## # ### ## #### ## # # # # ## ## +# ## # # # #### # # # # # # # ###### # # # # ## ## # # ## # ## ## ## +# # # # # ## ## # # # ### # ## # # ### # # ### ## # ## # # # ## # # ## ### # +# ## ## # # # # # ## #### ### # ## ##### # # # # # # # ## +# # ### ## ## # ### # ## # # ##### # # # # ### ### # ### ### +# # ## # ## ## # # # ## ## # # # #### ## # # # ## # #### +# # ### # ## # # # # # # # # # # # # # ## # #### # # # # # # ## +# ### # ## ## # # # ## # # # ## ### # ## # ## # ## ## # ## # ### ## ## ## +# ## #### ###### # ## ## ## # # ## # #### # #### ## ## # # # # ## ## # ## # +# ## # ### ### ## # ## ## # ## # # # # ## # # ## ## # # # # # ## # +# ## ## ## # ### # ## # ## # # # ##### # #### # ## # # # ## # # ## ### ## +# # # ## ## ## # #### ##### # ## ## # ### # # # # # # # ### ## +# # # # # ## ## # ### # # ## # #### # ### ## # # ### ###### # ## # +# ## # # # ### # # # ## # # # ## # # ### ## ## # +# # # # # # ## # # # # ## # # # # # # # ### # ##### # # # # ### ## # # ## +# ### # # # ## # ### # # # # ## ## # ### ## ### # # # # # # ## # +# # ## ### # # # # ## ##### #### # # ### ### #### # ## # ### +# # # # # # # ## # # ## ### # # ## # # ### # # # ## # # # # +# # #### # # ##### # # ### ## # # # # # ## ### ## # # # ### # ## # +# # ### ## # # ## # # ## ### # ## ## # # # # # # # ## # # # +# # ## # ## # # # ### ## # ## # # ### # # # ### # # # # # ## +# # # # # ## # # # # # ## # #### # # ## # # # ###### ### ### +# # # # ##### ## # ## ### ## # ## ## # # ## # ##### ## ## # # ## ## +# # # ### # # ## # ### # # # # ## # # # ## # # # # #### # # # # +# ### # # ## # # ## # # ## # # # #### ### # # ### # # ### ### #### +# ##### ## # # ## # # # # ## ### # # # ## # # ### ## # # # ## # +# ## # ## # ## #### # ## # # ## # ## ## # # ## ## # ### # # # # # +# # #### ## ### ## ### ## # # ## # ##### # # # ## ## ## ### ### # ##### # ## # # +# # # # ###### ## ## ### # # ## # # ## # # ### ## #### # # ## # # +# # # ## ### #### # # ## # # # ## # ## # # # ## # # # #### # # ## # +# # # # # # ### ## # # ## # # ##### # # # # # ### ## ## ### # # +# # ## # # # # ## ## # ## # # # #### ## # ## ##### # ## # # # ### # # +# ## ## ## # ## # ## ### # # # # ## # ## # # # ### #### # # # # +# ### ### # # ## ##### # # # #### # # # ## ## ## # ### ## # ### # ### # # # +# # # ## ## # ## ## ## # # # # # ## # # ## ## ## # ## ## ## # # # +# ## # # ### # # # # # ## # # # ##### # # # # # #### # # ### +# ### # ## ## ## # # # # # # # # # #### ## # ## # # # # ## # ## # +# # # # ### # # ## # # #### # # # # ## # ## ## ## ## # # ## # # # ## # +# ## ## # ## # ### # ## # ##### # # # # ## # # # #### ## ##### # +# # ## # # # ## # # #### # ## ## # # # ## ## ## #### #### ## # ### +# # # ## ## # # # # ## # ## # # # # ## # # ## # # ## # ## +# #### # # ## # ## # ### # # # # # #### # # # ### # # # # # # ## +# # # # # # # ## # # ## # # # # ## # ## # # # # ### # ## # # ### # +# ### # ##### ## # # ## ### ## # #### ## # ## ### ## # # # # # # ### ## +# # ## #### ## # # # # # # # ## # ###### # ## #### ###### # #### +# # # ##### # ## # ## # # # ## ### # # ## # # # # # # # # +# # # # # # # # # # # ## ## # # # ## ### ### # # ### # +# ## ## # # # # # # # # ## #### # # #### # #### # ## # # ## # #### +# # # ## # # # ## ## ### # # # # # # # ### # # # # ## ## # # # ## # # +# # ## #### # ## ## ## # # # ### # # ## ## ####### ### # # # ## +# # ##### # # # # # # ## # # ### # #### # # # #### # # # # # +# # ## # ### # # ## # # # # # ### ## # ## # # ## # # # ### # ## ##### +# # ## #### ## # # # ## ### # ## # ### # ## # #### ## # # ## # # +# # ## ## ## # # ### # # # ### ### # # # ##### # # # # # ## +# # ## # # # # # ### # ## # # # ## # # # ## # ### ### # # # ## +# # ### ## ##### ## # ## # # ###### # ## ####### # # ## # ### ## ## # # ##### ## +# ### # ### # # # # # # ##### # # # # ##### # ##### # +# # # ## # # # #### ## # ## # # # # # ## ## ### # # # #### # +# ### # ## # # # ## ## # # # ##### # # # ## # # # ## # ### ## # # +# # # ## ## # # # # ## ## # ## # # ## ## # # ## # ##### # #### +# ##### # #### # # # #### # # ## ## # # ## ## # # ##### # ##### # ## ## +# ## # ## # # # ## # # # ### # ### ##### #### # # # # # ## ## # # # +# # ### # # # #### # # ## # # ## # ### ### ## ## ## # # ## # # # # # ## # # # +# # # # # # # # # # # ## # # ## # ## ### # # # # ### # # ## ### ## # +# ## ## # # ### ##### ## # ### ## # # ## # # # # ##### # ### ### ## # # +# ## ## ## ## # ## ### ## ### # # # # # # ## # # # # ## +# # # #### # # # # # #### # # # # # # ### ## ## # +# # ## ## ### # # # # ## # #### ## # ## # # # # ### ### ## # # # # ### +# # # ## # ## ## ## ## # ## # # ### # # # # # # ### # #### # # # ## # +# # ### # ## ## # # # ## # ## # # # # # # ## # # # # # +# # # # # # # # ## #### # ### ## ## # ## # ### # ### ## +# # # ## # # ### ## # # ### # ## ## # # ### # #### # # # # ## # +# # # # # ### # # # # ### ### ## # # # ## ## # ## ### # # # ## +# ## #### # ### # # ##### # # # # ## ## # #### #### # +# # # # # # # ## # ## ## # # ## ## # ### # ### # # +# # # # ## # # ## # ### ### # ### #### # # # ### ## # ## # ## # ### ## +# ### ## ## # # # # # # # ### ### ## ## ### # ## ## ## ### ## +# ## # ## ### ## # #### # ## # # ## ## # # ## ## ## # ## ## # # +# # ## ## # ## # # # # #### # ## ## ## # ### # ### # +# # # # # # ## # # # # # # ## ## # ##### # # # # ###### ### # +# # # ## ### ## ### # # ## ## # # # # ### # # ## ### #### # # +# ## # ## # ### # # # # ## # # ### # # # # #### # # ### # # # ## #### +# # # # #### ## # ## # ### #### # # # # # # # # # # ## ### # ## +# ## ## ### # ### ## ## ## #### #### ## ## ### # ## # # ## # # # # # # # # +# # # # ### # # ## # # # # ## ### ## # ### # # # # ## # # # ### +# # # #### # ## # # # ## ### # ## ## ## ## ## ### # ### # # # # +# # # # # # ## # # ## ## ## # ##### # # # # ## ### # ##### # # +# ## # ### # # # # # ## # ## # # ## # ## ## # # # # ## #### # # # # # # # +# # ## ## ## #### # # ## # # # # ### #### # ### # # # # ### # # +# # #### # ## # ## ## ## ## # #### # ## # # # ### ## ### # # # +# # ### ## ## ## # ### ### # # # # # ## ## ## # ##### ## # # # # +# ## # # ## # # # ## ## # # # # # # ## # ### # # # ## ### ## # +# E# +#################################################################################################### diff --git a/romanovpv/task 2/docs/data/builders.py b/romanovpv/task 2/docs/data/builders.py new file mode 100644 index 0000000..c6d64aa --- /dev/null +++ b/romanovpv/task 2/docs/data/builders.py @@ -0,0 +1,41 @@ +from abc import ABC, abstractmethod +from model import Maze, Cell + +class MazeBuilder(ABC): + @abstractmethod + def buildFromFile(self, filename): + pass + +class TextFileMazeBuilder(MazeBuilder): + def buildFromFile(self, filename): + with open(filename, "r", encoding="utf-8") as file: + lines = [line.rstrip("\n") + for line in file + ] + height = len(lines) + width = len(lines[0]) + maze = Maze(width, height) + start_count = 0 + exit_count = 0 + for x, line in enumerate(lines): + row = [] + for y, symbol in enumerate(line): + if symbol == "#": + cell = Cell(x, y, is_wall=True) + elif symbol == "S": + cell = Cell(x, y, is_start=True) + start_count += 1 + elif symbol == "E": + cell = Cell(x, y, is_exit=True) + exit_count += 1 + elif symbol == " ": + cell = Cell(x, y) + else: + raise ValueError(f"Неизвестный символ: {symbol}") + row.append(cell) + maze.add_row(row) + if start_count != 1: + raise ValueError("Должен быть ровно один старт S") + if exit_count != 1: + raise ValueError("Должен быть ровно один выход E") + return maze \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/generate_mazes.py b/romanovpv/task 2/docs/data/generate_mazes.py new file mode 100644 index 0000000..faaaea5 --- /dev/null +++ b/romanovpv/task 2/docs/data/generate_mazes.py @@ -0,0 +1,43 @@ +import random + +def save_maze(filename, width, height, wall_probability): + maze = [] + for i in range(height): + row = "" + for j in range(width): + if i == 0 or i == height-1: + row += "#" + elif j == 0 or j == width-1: + row += "#" + else: + if random.random() < wall_probability: + row += "#" + else: + row += " " + + maze.append(list(row)) + maze[1][1] = "S" + maze[height-2][width-2] = "E" + + for i in range(1, height-1): + maze[i][1] = " " + for j in range(1, width-1): + maze[height-2][j] = " " + maze[1][1] = "S" + maze[height-2][width-2] = "E" + with open(filename, "w", encoding="utf-8") as f: + for row in maze: + f.write("".join(row)+"\n") +save_maze( + "medium_maze.txt", + 50, + 50, + 0.30 +) +save_maze( + "big_maze.txt", + 100, + 100, + 0.40 +) +print("Лабиринты созданы") \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/graphs.py b/romanovpv/task 2/docs/data/graphs.py new file mode 100644 index 0000000..1664162 --- /dev/null +++ b/romanovpv/task 2/docs/data/graphs.py @@ -0,0 +1,50 @@ +import pandas as pd +import matplotlib.pyplot as plt +from result import results + +df = pd.DataFrame( + results[1:], + columns=results[0] +) + +time_data = df.pivot( + index="maze", + columns="strategy", + values="time_ms" +) + +time_data.plot(kind="bar") + +plt.title("Время выполнения") +plt.ylabel("мс") +plt.xticks(rotation=0) + +plt.show() + +cells_data = df.pivot( + index="maze", + columns="strategy", + values="cells visited" +) + +cells_data.plot(kind="bar") + +plt.title("Количество посещённых клеток") +plt.ylabel("клетки") +plt.xticks(rotation=0) + +plt.show() + +path_data = df.pivot( + index="maze", + columns="strategy", + values="path length" +) + +path_data.plot(kind="bar") + +plt.title("Длина пути") +plt.ylabel("шаги") +plt.xticks(rotation=0) + +plt.show() \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/main.py b/romanovpv/task 2/docs/data/main.py new file mode 100644 index 0000000..8204df2 --- /dev/null +++ b/romanovpv/task 2/docs/data/main.py @@ -0,0 +1,68 @@ +from builders import (TextFileMazeBuilder) +from strategies import (BFSStrategy, DFSStrategy, AStarStrategy) +from solver import (MazeSolver) +from observer_command import ConsoleView, Player, MoveCommand +import os + +builder = TextFileMazeBuilder() +maze = builder.buildFromFile("no_exit_maze.txt") +print("Лабиринт:\n") +maze.printMaze() +print("Выберете алгоритм") +print("1 - BFS") +print("2 - DFS") +print("3 - A*") +choice = input() +if choice == "1": + strategy = BFSStrategy() +elif choice == "2": + strategy = DFSStrategy() +elif choice == "3": + strategy = AStarStrategy() +else: + print("Неверный выбор") + exit() + +solver = MazeSolver(maze, strategy) +view = ConsoleView() +solver.addObserver(view) +stats = solver.solve() +print("Результат:") +print(stats) +path, _ = strategy.findPath( + maze, + maze.start, + maze.exit +) + +if not path: + print("\nПуть не найден") + exit() + +print("\nНайденный путь:") +for cell in path: + print(f"({cell.x}, {cell.y})") + +print("\nПошаговое движение игрока") +player = Player(maze.start) + +history = [] +passed_path = [maze.start] + +view.render(maze, player, passed_path) + +for cell in path[1:]: + + input("\nEnter -> следующий шаг") + + command = MoveCommand(player, cell) + command.execute() + + history.append(command) + + passed_path.append(cell) + + os.system('cls' if os.name == 'nt' else 'clear') + + view.render(maze, player, passed_path) + \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/medium_maze.txt b/romanovpv/task 2/docs/data/medium_maze.txt new file mode 100644 index 0000000..31a144a --- /dev/null +++ b/romanovpv/task 2/docs/data/medium_maze.txt @@ -0,0 +1,50 @@ +################################################## +#S## # ## # # # # # # ## # +# # # # # # # ### # #### # # +# ## ## # # ## ### # # # +# ## #### ### ## ## ## # # # +# # #### # ## # # # ## ## # ##### +# # ## # ### ## # # #### # # # +# # # # ### # ## # ##### # ### # # +# # # # # # # # # # ## ### ## +# # # # # # ## # ## ## # # # +# # # # ### #### ### # # ## ## # +# # ## # ## # # ## # ## # # ## ## +# # # # # # # ##### ## # ### # # ## +# # # ## ### ## # # # ## # +# ## # # ### # # # # ## # # +# # #### ### ### #### # ## # # +# # # # # # # ## # ## # # +# ## ## # # # # # # ## ### # +# ## # ## # ## #### ### # #### # # # +# ## ### # # # # # # ## # +# # # ## # ##### ### # # +# # # ## # # ## # # +# # ## # # # # ## # # # +# # # # # # +# # ## ## # ## ## # +# # ## ### # # # # ## ## +# # #### # # ## # # # ## # ## +# # # # # ## ## # # # # #### +# # # # # ## ##### # # # ## +# # ## # ## ### # # ## # ## ## # +# # # ### ## ###### # # ## # # # # +# # # ## # # # # # ## +# # # # # # ## ## # # # ## # +# ##### # # ## ## ## # ## ### ## +# ## ## # # # ## # ## ## +# # # # # ### # # # ### # # +# ## # ## ### ## # ## +# # ### # ## ## # ## ## # # # # +# ## # ## # # ## #### # ## ## +# ## # # # # ## ## # # # # +# # # # # # # # # ## # # +# # # # # # ## # ### # ### +# ## # ## # # # ## # # # +# # # # # # # # # # ## # +# ## # # # # ## ## # ## # # # # +# # # ## # # # ## # ## # +# ## ## # # # # # ## # # # ## # # +# # ## ## # # ## # # # # # +# E# +################################################## diff --git a/romanovpv/task 2/docs/data/model.py b/romanovpv/task 2/docs/data/model.py new file mode 100644 index 0000000..8ea9f5b --- /dev/null +++ b/romanovpv/task 2/docs/data/model.py @@ -0,0 +1,86 @@ +class Cell: + + def __init__( + self, + x: int, + y: int, + is_wall=False, + is_start=False, + is_exit=False + ): + + self.x = x + self.y = y + + self.isWall = is_wall + self.isStart = is_start + self.isExit = is_exit + + def isPassable(self): + return not self.isWall + + def __repr__(self): + return f"Cell({self.x},{self.y})" + + def __eq__(self, other): + return ( + isinstance(other, Cell) + and self.x == other.x + and self.y == other.y + ) + + def __hash__(self): + return hash((self.x, self.y)) + +class Maze: + + def __init__(self, width: int, height: int): + self.width = width + self.height = height + + self.cells = [] + + self.start = None + self.exit = None + + def add_row(self, row): + self.cells.append(row) + for cell in row: + if cell.isStart: + self.start = cell + if cell.isExit: + self.exit = cell + + def getCell(self, x, y): + if 0 <= x < self.height and 0 <= y < self.width: + return self.cells[x][y] + return None + + def getNeighbors(self, cell): + directions = [ + (-1, 0), # вверх + (1, 0), # вниз + (0, -1), # влево + (0, 1) # вправо + ] + neighbors = [] + for dx, dy in directions: + nx = cell.x + dx + ny = cell.y + dy + neighbor = self.getCell(nx, ny) + if (neighbor and neighbor.isPassable()): + neighbors.append(neighbor) + return neighbors + def printMaze(self): + for row in self.cells: + line = "" + for cell in row: + if cell.isStart: + line += "S" + elif cell.isExit: + line += "E" + elif cell.isWall: + line += "#" + else: + line += " " + print(line) \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/no_exit_maze.txt b/romanovpv/task 2/docs/data/no_exit_maze.txt new file mode 100644 index 0000000..3c482a1 --- /dev/null +++ b/romanovpv/task 2/docs/data/no_exit_maze.txt @@ -0,0 +1,10 @@ +########## +#S # # +# ###### # +# ###### # +# ###### # +# ###### # +# ###### # +# ###### # +# ######E# +########## \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/no_wall_maze.txt b/romanovpv/task 2/docs/data/no_wall_maze.txt new file mode 100644 index 0000000..db91695 --- /dev/null +++ b/romanovpv/task 2/docs/data/no_wall_maze.txt @@ -0,0 +1,10 @@ +########## +#S # +# # +# # +# # +# # +# # +# # +# E# +########## \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/observer_command.py b/romanovpv/task 2/docs/data/observer_command.py new file mode 100644 index 0000000..9cc8e26 --- /dev/null +++ b/romanovpv/task 2/docs/data/observer_command.py @@ -0,0 +1,68 @@ +from abc import ABC, abstractmethod + +class Observer(ABC): + + @abstractmethod + def update(self, event): + pass + +class ConsoleView(Observer): + + def update(self, event): + print(f"\n[Событие] {event}") + + def render(self, maze, player=None, path=None): + + path = path or [] + + print() + + for row in maze.cells: + + line = "" + + for cell in row: + + if player and cell == player.position: + line += "P" + + elif cell.isStart: + line += "S" + + elif cell.isExit: + line += "E" + + elif cell.isWall: + line += "#" + + elif cell in path: + line += "*" + + else: + line += " " + + print(line) + + +class Command(ABC): + @abstractmethod + def execute(self): + pass + @abstractmethod + def undo(self): + pass + +class Player: + def __init__(self, start_cell): + self.position = start_cell + +class MoveCommand(Command): + def __init__(self, player, new_cell): + self.player = player + self.new_cell = new_cell + self.old_cell = None + def execute(self): + self.old_cell = self.player.position + self.player.position = self.new_cell + def undo(self): + self.player.position = self.old_cell \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/result.py b/romanovpv/task 2/docs/data/result.py new file mode 100644 index 0000000..e92ff90 --- /dev/null +++ b/romanovpv/task 2/docs/data/result.py @@ -0,0 +1,21 @@ +import csv + +results = [ + ["maze", "strategy", "time_ms", "cells visited", "path length"], + ["small_maze", "BFS", 0.173, 15, 15], + ["small_maze", "DFS", 0.198, 15, 15], + ["small_maze", "A*", 0.195, 15, 15], + ["medium_maze", "BFS", 7.228, 95, 95], + ["medium_maze", "DFS", 1.361, 189, 189], + ["medium_maze", "A*", 3.050, 95, 95], + ["big_maze", "BFS", 18.487, 195, 195], + ["big_maze", "DFS", 10.021, 497, 497], + ["big_maze", "A*", 4.471, 195, 195], + ["no_wall_maze", "BFS", 0.325, 15, 15], + ["no_wall_maze", "DFS", 0.251, 29, 29], + ["no_wall_maze", "A*", 0.396, 15, 15], +] + +with open("results.csv", "w", newline="") as f: + writer = csv.writer(f) + writer.writerows(results) \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/results.csv b/romanovpv/task 2/docs/data/results.csv new file mode 100644 index 0000000..4441a24 --- /dev/null +++ b/romanovpv/task 2/docs/data/results.csv @@ -0,0 +1,13 @@ +maze,strategy,time_ms,cells visited,path length +small_maze,BFS,0.173,15,15 +small_maze,DFS,0.198,15,15 +small_maze,A*,0.195,15,15 +medium_maze,BFS,7.228,95,95 +medium_maze,DFS,1.361,189,189 +medium_maze,A*,3.05,95,95 +big_maze,BFS,18.487,195,195 +big_maze,DFS,10.021,497,497 +big_maze,A*,4.471,195,195 +no_wall_maze,BFS,0.325,15,15 +no_wall_maze,DFS,0.251,29,29 +no_wall_maze,A*,0.396,15,15 diff --git a/romanovpv/task 2/docs/data/small_maze.txt b/romanovpv/task 2/docs/data/small_maze.txt new file mode 100644 index 0000000..93c9fd5 --- /dev/null +++ b/romanovpv/task 2/docs/data/small_maze.txt @@ -0,0 +1,10 @@ +########## +#S # +# ###### # +# # # +###### # # +# # # +# ###### # +# # +# ######E# +########## \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/solver.py b/romanovpv/task 2/docs/data/solver.py new file mode 100644 index 0000000..ec6d219 --- /dev/null +++ b/romanovpv/task 2/docs/data/solver.py @@ -0,0 +1,42 @@ +import time + +class SearchStats: + def __init__(self, time_ms, visited_cells, path_length): + self.time_ms = time_ms + self.visited_cells = visited_cells + self.path_length = path_length + def __str__(self): + return ( + f"Время: " + f"{self.time_ms:.3f} мс\n" + + f"Посещено клеток: " + f"{self.visited_cells}\n" + + f"Длина пути: " + f"{self.path_length}" + ) + +class MazeSolver: + def __init__(self,maze, strategy): + self.maze = maze + self.strategy = strategy + self.observers = [] + def setStrategy(self, strategy + ): + self.strategy = strategy + def solve(self): + self.notify("Начат поиск") + start_time = (time.perf_counter()) + path, visited = (self.strategy.findPath(self.maze,self.maze.start,self.maze.exit)) + end_time = (time.perf_counter()) + self.notify("Путь найден") + time_ms = ((end_time-start_time)*1000) + visited = len(path) + stats = SearchStats(time_ms,visited,len(path)) + return stats + def addObserver(self, observer): + self.observers.append(observer) + def notify(self, event): + for observer in self.observers: + observer.update(event) \ No newline at end of file diff --git a/romanovpv/task 2/docs/data/strategies.py b/romanovpv/task 2/docs/data/strategies.py new file mode 100644 index 0000000..d6a17f2 --- /dev/null +++ b/romanovpv/task 2/docs/data/strategies.py @@ -0,0 +1,73 @@ +from abc import ABC, abstractmethod +from collections import deque +import heapq + +class PathFindingStrategy(ABC): + @abstractmethod + def findPath(self, maze, start, exit_cell): + pass + def restorePath(self, parent, start, exit_cell): + path = [] + current = exit_cell + while current != start: + path.append(current) + current = parent[current] + path.append(start) + path.reverse() + return path +class BFSStrategy(PathFindingStrategy): + def findPath( self, maze, start, exit_cell): + queue = deque([start]) + visited = {start} + parent = {} + while queue: + current = queue.popleft() + if current == exit_cell: + return (self.restorePath(parent, start, exit_cell), len(visited)) + for neighbor in maze.getNeighbors(current): + if neighbor not in visited: + visited.add(neighbor) + parent[neighbor] = current + queue.append(neighbor) + return [], len(visited) + +class DFSStrategy(PathFindingStrategy): + def findPath(self, maze, start, exit_cell): + stack = [start] + visited = {start} + parent = {} + while stack: + current = stack.pop() + if current == exit_cell: + return (self.restorePath(parent,start,exit_cell),len(visited)) + for neighbor in maze.getNeighbors(current): + if neighbor not in visited: + visited.add(neighbor) + parent[neighbor] = current + stack.append(neighbor) + return [], len(visited) + +class AStarStrategy(PathFindingStrategy): + def heuristic(self,cell,exit_cell): + return (abs(cell.x - exit_cell.x) + abs(cell.y - exit_cell.y)) + def findPath(self, maze, start, exit_cell): + pq = [] + heapq.heappush(pq,(0, id(start), start)) + parent = {} + g_score = {start: 0} + visited = set() + while pq: + _, _, current = (heapq.heappop(pq)) + if current in visited: + continue + visited.add(current) + if current == exit_cell: + return (self.restorePath(parent, start, exit_cell), len(visited)) + for neighbor in maze.getNeighbors(current): + new_cost = (g_score[current]+ 1) + if (neighbor not in g_score or new_cost < g_score[neighbor] ): + g_score[neighbor] = new_cost + parent[neighbor] = current + priority = (new_cost + self.heuristic(neighbor, exit_cell)) + heapq.heappush(pq,(priority,id(neighbor),neighbor)) + return [], len(visited) \ No newline at end of file diff --git a/romanovpv/task 2/docs/Отчет.docx b/romanovpv/task 2/docs/Отчет.docx new file mode 100644 index 0000000..7d67466 Binary files /dev/null and b/romanovpv/task 2/docs/Отчет.docx differ