отчет 2(дополнил)

This commit is contained in:
FamutdinovMD 2026-05-25 02:50:22 +03:00
parent 8ce584051b
commit 275f6b7297

View File

@ -1,74 +1,239 @@
# Отчёт по лабораторной работе №2 # Отчёт по лабораторной работе №2
## Поиск выхода из лабиринта ## Поиск выхода из лабиринта (объектно-ориентированная реализация с паттернами)
## Цель работы
Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов с применением паттернов проектирования GoF. ---
## 1. Описание задачи
Разработать программу для поиска выхода из лабиринта с возможностью выбора алгоритма поиска, визуализации процесса и экспериментального сравнения алгоритмов. Программа должна загружать лабиринт из текстового файла, поддерживать алгоритмы BFS, DFS, A* и использовать паттерны проектирования GoF.
---
## 2. Выбранные паттерны
### 2.1 Builder (Строитель)
**Где:** `TextFileMazeBuilder`
**Зачем:** Сокрытие сложности создания лабиринта из файла
**Преимущество:** Легко добавить новый формат (JSON, XML)
### 2.2 Strategy (Стратегия)
**Где:** `BFSStrategy`, `DFSStrategy`, `AStarStrategy`
**Зачем:** Возможность переключения алгоритмов во время выполнения
**Преимущество:** Новый алгоритм добавляется без изменения кода
### 2.3 Observer (Наблюдатель)
**Где:** `ConsoleView`
**Зачем:** Отделение визуализации от логики поиска
**Преимущество:** Можно добавить GUI без изменения MazeSolver
### 2.4 Command (Команда)
**Где:** `MoveCommand`, `Player`
**Зачем:** Поддержка отмены действий при ручном управлении
**Преимущество:** История действий и возможность Undo
---
## 3. Диаграмма классов (Mermaid)
```python ```python
import sys classDiagram
sys.path.append('.') class Maze {
-width, height
-_cells[][]
-start, exit
+get_cell(x,y)
+get_neighbors(cell)
}
from builders import TextFileMazeBuilder class Cell {
from strategies import BFSStrategy, DFSStrategy, AStarStrategy -x, y
from solver import MazeSolver -is_wall
from observers import ConsoleView -is_start
import time -is_exit
import pandas as pd +is_passable()
import matplotlib.pyplot as plt }
builder = TextFileMazeBuilder()
maze = builder.build_from_file("mazes/small.txt") class MazeBuilder {
print(f"Размер лабиринта: {maze.width}×{maze.height}") <<interface>>
print(maze) +build_from_file(filename)
## Паттерн Builder (Строитель) }
class TextFileMazeBuilder {
+build_from_file(filename)
}
class PathFindingStrategy {
<<interface>>
+find_path(maze, start, exit)
}
class BFSStrategy
class DFSStrategy
class AStarStrategy
class MazeSolver {
-maze
-strategy
+set_strategy()
+solve()
}
class Observer {
<<interface>>
+update(event, data)
}
class ConsoleView {
+render(maze, path)
+update(event, data)
}
class Command {
<<interface>>
+execute()
+undo()
}
class MoveCommand {
-player
-direction
+execute()
+undo()
}
MazeBuilder <|.. TextFileMazeBuilder
PathFindingStrategy <|.. BFSStrategy
PathFindingStrategy <|.. DFSStrategy
PathFindingStrategy <|.. AStarStrategy
MazeSolver --> PathFindingStrategy
Observer <|.. ConsoleView
Command <|.. MoveCommand
```python
class TextFileMazeBuilder(MazeBuilder): class TextFileMazeBuilder(MazeBuilder):
WALL_CHAR = '#' WALL_CHAR = '#'
START_CHAR = 'S' START_CHAR = 'S'
EXIT_CHAR = 'E' EXIT_CHAR = 'E'
def build_from_file(self, filename: str) -> Maze: def build_from_file(self, filename: str) -> Maze:
with open(filename, 'r') as f: with open(filename, 'r', encoding='utf-8') as f:
lines = f.readlines() lines = [line.rstrip('\n') for line in f.readlines()]
# парсинг и создание лабиринта
height = len(lines)
width = max(len(line) for line in lines)
maze = Maze(width, height)
for y, line in enumerate(lines):
for x, ch in enumerate(line):
if x >= width:
continue
cell = Cell(x, y)
if ch == self.WALL_CHAR:
cell.is_wall = True
elif ch == self.START_CHAR:
cell.is_start = True
elif ch == self.EXIT_CHAR:
cell.is_exit = True
maze.set_cell(x, y, cell)
if maze.start is None:
raise ValueError("Нет стартовой клетки (S)")
if maze.exit is None:
raise ValueError("Нет выхода (E)")
return maze return maze
class TextFileMazeBuilder(MazeBuilder):
WALL_CHAR = '#'
START_CHAR = 'S'
EXIT_CHAR = 'E'
**Ячейка 6 (паттерн Strategy):** def build_from_file(self, filename: str) -> Maze:
```markdown with open(filename, 'r', encoding='utf-8') as f:
## Паттерн Strategy (Стратегия) lines = [line.rstrip('\n') for line in f.readlines()]
Реализованы три алгоритма поиска пути: height = len(lines)
- **BFS** - поиск в ширину (гарантирует кратчайший путь) width = max(len(line) for line in lines)
- **DFS** - поиск в глубину (быстрый, но не оптимальный) maze = Maze(width, height)
- **A*** - с эвристикой (манхэттенское расстояние)
strategies = {
"BFS": BFSStrategy(),
"DFS": DFSStrategy(),
"A*": AStarStrategy()
}
results = [] for y, line in enumerate(lines):
for x, ch in enumerate(line):
if x >= width:
continue
cell = Cell(x, y)
if ch == self.WALL_CHAR:
cell.is_wall = True
elif ch == self.START_CHAR:
cell.is_start = True
elif ch == self.EXIT_CHAR:
cell.is_exit = True
maze.set_cell(x, y, cell)
for name, strategy in strategies.items(): if maze.start is None:
solver = MazeSolver(maze, strategy) raise ValueError("Нет стартовой клетки (S)")
start_time = time.perf_counter() if maze.exit is None:
path, stats = solver.solve() raise ValueError("Нет выхода (E)")
end_time = time.perf_counter()
results.append({ return maze
"Алгоритм": name, class BFSStrategy(PathFindingStrategy):
"Время (мс)": stats.time_ms, def find_path(self, maze, start, exit_cell):
"Длина пути": stats.path_length, queue = deque([start])
"Посещено клеток": stats.visited_cells visited = {start}
}) parent = {start: None}
print(f"{name}: {stats}") while queue:
current = queue.popleft()
if current == exit_cell:
return self._reconstruct_path(parent, current)
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
queue.append(neighbor)
return []
df = pd.DataFrame(results) def _reconstruct_path(self, parent, current):
df path = []
## Паттерн Observer (Наблюдатель) while current:
path.append(current)
current = parent[current]
return list(reversed(path))
class DFSStrategy(PathFindingStrategy):
def find_path(self, maze, start, exit_cell):
stack = [(start, [start])]
visited = {start}
```python while stack:
class ConsoleView(Observer): current, path = stack.pop()
if current == exit_cell:
return path
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
stack.append((neighbor, path + [neighbor]))
return []
class AStarStrategy(PathFindingStrategy):
def _heuristic(self, a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
def find_path(self, maze, start, exit_cell):
counter = 0
open_set = [(self._heuristic(start, exit_cell), counter, start)]
g_score = {start: 0}
parent = {start: None}
while open_set:
_, _, current = heappop(open_set)
if current == exit_cell:
return self._reconstruct_path(parent, current)
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]:
parent[neighbor] = current
g_score[neighbor] = tentative_g
counter += 1
f = tentative_g + self._heuristic(neighbor, exit_cell)
heappush(open_set, (f, counter, neighbor))
return []
class ConsoleView(Observer):
def render(self, maze, path=None): def render(self, maze, path=None):
path_set = set(path) if path else set() path_set = set(path) if path else set()
print("\n+" + "-" * maze.width + "+") print("\n+" + "-" * maze.width + "+")
@ -88,46 +253,56 @@ class ConsoleView(Observer):
row.append(' ') row.append(' ')
print("|" + ''.join(row) + "|") print("|" + ''.join(row) + "|")
print("+" + "-" * maze.width + "+") print("+" + "-" * maze.width + "+")
**Ячейка 9 (визуализация пути):**
```python
view = ConsoleView()
solver = MazeSolver(maze, BFSStrategy())
path, stats = solver.solve()
view.render(maze, path=path)
print(f"Найден путь длиной {len(path)} клеток")
data = pd.read_csv("experiment_results.csv")
data
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
algorithms = data['strategy'].unique() def update(self, event, data):
mazes = data['maze_file'].unique() if event == "maze_loaded":
self.render(data.get('maze'))
elif event == "path_found":
self.render(data.get('maze'), data.get('path'))
class MoveCommand(Command):
def __init__(self, player, maze, direction):
self.player = player
self.maze = maze
self.direction = direction
self.previous_cell = None
for algo in algorithms: def execute(self):
algo_data = data[data['strategy'] == algo] self.previous_cell = self.player.current_cell
axes[0].plot(algo_data['maze_file'], algo_data['time_mean'], marker='o', label=algo) dx, dy = self.direction
axes[1].plot(algo_data['maze_file'], algo_data['path_length_mean'], marker='o', label=algo) new_cell = self.maze.get_cell(
self.player.current_cell.x + dx,
self.player.current_cell.y + dy
)
if new_cell and new_cell.is_passable():
self.player.move_to(new_cell)
return True
return False
axes[0].set_title('Время выполнения (мс)') def undo(self):
axes[0].legend() if self.previous_cell:
axes[1].set_title('Длина пути') self.player.move_to(self.previous_cell)
axes[1].legend() class MazeSolver:
plt.xticks(rotation=45) def __init__(self, maze, strategy=None):
plt.tight_layout() self.maze = maze
plt.savefig('report_graphs.png') self._strategy = strategy
plt.show()
## Выводы
В ходе лабораторной работы были реализованы паттерны: def set_strategy(self, strategy):
self._strategy = strategy
| Паттерн | Где используется | Преимущество | def solve(self):
|---------|-----------------|--------------| if not self._strategy:
| **Builder** | `TextFileMazeBuilder` | Сокрытие сложности создания лабиринта | raise ValueError("Стратегия не установлена")
| **Strategy** | `BFS/DFS/A*` | Легкая смена алгоритмов |
| **Observer** | `ConsoleView` | Отделение визуализации от логики | start_time = time.perf_counter()
| **Command** | `MoveCommand` | Поддержка отмены действий | path = self._strategy.find_path(self.maze, self.maze.start, self.maze.exit)
end_time = time.perf_counter()
stats = SearchStats(
time_ms=(end_time - start_time) * 1000,
visited_cells=len(path) if path else 0,
path_length=len(path) if path else 0,
path_found=bool(path)
)
return path, stats
### Сравнение алгоритмов
- **BFS** - оптимальный по длине пути, стабильное время
- **DFS** - самый быстрый, но путь длиннее
- **A*** - баланс скорости и оптимальности