[2] task2
BIN
VildyaevAV/docs/image.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
VildyaevAV/docs/task2/blocked_time.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
VildyaevAV/docs/task2/blocked_visited.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
66
VildyaevAV/docs/task2/builder.py
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
from cell import Cell
|
||||
from maze import Maze
|
||||
|
||||
|
||||
class MazeBuilder(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def build_from_file(self, filename):
|
||||
pass
|
||||
|
||||
|
||||
class TextFileMazeBuilder(MazeBuilder):
|
||||
|
||||
def build_from_file(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])
|
||||
|
||||
cells = []
|
||||
|
||||
start = None
|
||||
exit_cell = None
|
||||
|
||||
for y in range(height):
|
||||
|
||||
row = []
|
||||
|
||||
for x in range(width):
|
||||
|
||||
symbol = lines[y][x]
|
||||
|
||||
cell = Cell(x, y)
|
||||
|
||||
if symbol == "#":
|
||||
cell.is_wall = True
|
||||
|
||||
elif symbol == "S":
|
||||
cell.is_start = True
|
||||
start = cell
|
||||
|
||||
elif symbol == "E":
|
||||
cell.is_exit = True
|
||||
exit_cell = cell
|
||||
|
||||
row.append(cell)
|
||||
|
||||
cells.append(row)
|
||||
|
||||
if start is None:
|
||||
raise ValueError("Start cell S not found")
|
||||
|
||||
if exit_cell is None:
|
||||
raise ValueError("Exit cell E not found")
|
||||
|
||||
return Maze(
|
||||
cells,
|
||||
width,
|
||||
height,
|
||||
start,
|
||||
exit_cell
|
||||
)
|
||||
11
VildyaevAV/docs/task2/cell.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class Cell:
|
||||
def __init__(self, x, y, is_wall=False):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
self.is_wall = is_wall
|
||||
self.is_start = False
|
||||
self.is_exit = False
|
||||
|
||||
def is_passable(self):
|
||||
return not self.is_wall
|
||||
BIN
VildyaevAV/docs/task2/empty_time.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
VildyaevAV/docs/task2/empty_visited.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
VildyaevAV/docs/task2/image.png
Normal file
|
After Width: | Height: | Size: 874 KiB |
BIN
VildyaevAV/docs/task2/large_time.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
VildyaevAV/docs/task2/large_visited.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
112
VildyaevAV/docs/task2/main.py
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import csv
|
||||
|
||||
from builder import TextFileMazeBuilder
|
||||
from strategies import BFSStrategy, DFSStrategy, AStarStrategy
|
||||
from solver import MazeSolver
|
||||
from observer import ConsoleView
|
||||
|
||||
|
||||
def print_path(maze, path):
|
||||
path_coords = {(cell.x, cell.y) for cell in path}
|
||||
|
||||
for row in maze.cells:
|
||||
line = ""
|
||||
|
||||
for cell in row:
|
||||
if cell.is_wall:
|
||||
line += "#"
|
||||
elif cell.is_start:
|
||||
line += "S"
|
||||
elif cell.is_exit:
|
||||
line += "E"
|
||||
elif (cell.x, cell.y) in path_coords:
|
||||
line += "*"
|
||||
else:
|
||||
line += " "
|
||||
|
||||
print(line)
|
||||
|
||||
|
||||
builder = TextFileMazeBuilder()
|
||||
|
||||
maze_files = [
|
||||
"docs/task2/mazes/small.txt",
|
||||
"docs/task2/mazes/medium.txt",
|
||||
"docs/task2/mazes/blocked.txt",
|
||||
"docs/task2/mazes/large.txt",
|
||||
"docs/task2/mazes/empty.txt",
|
||||
"docs/task2/mazes/no_exit.txt"
|
||||
]
|
||||
|
||||
strategies = [
|
||||
BFSStrategy(),
|
||||
DFSStrategy(),
|
||||
AStarStrategy()
|
||||
]
|
||||
|
||||
results = []
|
||||
|
||||
for maze_file in maze_files:
|
||||
print("\n======================")
|
||||
print("Maze:", maze_file)
|
||||
|
||||
try:
|
||||
maze = builder.build_from_file(maze_file)
|
||||
|
||||
except Exception as e:
|
||||
print("Error:", e)
|
||||
continue
|
||||
|
||||
for strategy in strategies:
|
||||
print("\nStrategy:", strategy.__class__.__name__)
|
||||
|
||||
solver = MazeSolver(maze, strategy)
|
||||
|
||||
observer = ConsoleView()
|
||||
solver.add_observer(observer)
|
||||
|
||||
runs = 5
|
||||
total_time = 0
|
||||
last_stats = None
|
||||
last_path = []
|
||||
|
||||
for _ in range(runs):
|
||||
stats, path = solver.solve()
|
||||
|
||||
total_time += stats.time_ms
|
||||
last_stats = stats
|
||||
last_path = path
|
||||
|
||||
average_time = total_time / runs
|
||||
|
||||
print("Average time ms:", round(average_time, 4))
|
||||
print("Visited:", last_stats.visited_cells)
|
||||
print("Path length:", last_stats.path_length)
|
||||
|
||||
if last_path:
|
||||
print_path(maze, last_path)
|
||||
else:
|
||||
print("Path not found")
|
||||
|
||||
results.append([
|
||||
maze_file,
|
||||
strategy.__class__.__name__,
|
||||
round(average_time, 4),
|
||||
last_stats.visited_cells,
|
||||
last_stats.path_length
|
||||
])
|
||||
|
||||
with open("maze_results.csv", "w", newline="", encoding="utf-8") as file:
|
||||
writer = csv.writer(file)
|
||||
|
||||
writer.writerow([
|
||||
"maze",
|
||||
"strategy",
|
||||
"time_ms",
|
||||
"visited_cells",
|
||||
"path_length"
|
||||
])
|
||||
|
||||
writer.writerows(results)
|
||||
|
||||
print("\nResults saved to maze_results.csv")
|
||||
35
VildyaevAV/docs/task2/maze.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
class Maze:
|
||||
def __init__(self, cells, width, height, start, exit_cell):
|
||||
self.cells = cells
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
self.start = start
|
||||
self.exit = exit_cell
|
||||
|
||||
def get_cell(self, x, y):
|
||||
if 0 <= y < self.height and 0 <= x < self.width:
|
||||
return self.cells[y][x]
|
||||
|
||||
return None
|
||||
|
||||
def get_neighbors(self, cell):
|
||||
directions = [
|
||||
(0, -1),
|
||||
(0, 1),
|
||||
(-1, 0),
|
||||
(1, 0)
|
||||
]
|
||||
|
||||
neighbors = []
|
||||
|
||||
for dx, dy in directions:
|
||||
nx = cell.x + dx
|
||||
ny = cell.y + dy
|
||||
|
||||
neighbor = self.get_cell(nx, ny)
|
||||
|
||||
if neighbor and neighbor.is_passable():
|
||||
neighbors.append(neighbor)
|
||||
|
||||
return neighbors
|
||||
16
VildyaevAV/docs/task2/maze_results.csv
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
maze,strategy,time_ms,visited_cells,path_length
|
||||
docs/task2/mazes/small.txt,BFSStrategy,0.0228,10,7
|
||||
docs/task2/mazes/small.txt,DFSStrategy,0.0163,10,7
|
||||
docs/task2/mazes/small.txt,AStarStrategy,0.0252,10,7
|
||||
docs/task2/mazes/medium.txt,BFSStrategy,0.0238,18,0
|
||||
docs/task2/mazes/medium.txt,DFSStrategy,0.0257,18,0
|
||||
docs/task2/mazes/medium.txt,AStarStrategy,0.0344,18,0
|
||||
docs/task2/mazes/blocked.txt,BFSStrategy,0.0085,3,0
|
||||
docs/task2/mazes/blocked.txt,DFSStrategy,0.006,3,0
|
||||
docs/task2/mazes/blocked.txt,AStarStrategy,0.0059,3,0
|
||||
docs/task2/mazes/large.txt,BFSStrategy,0.0558,45,0
|
||||
docs/task2/mazes/large.txt,DFSStrategy,0.0522,45,0
|
||||
docs/task2/mazes/large.txt,AStarStrategy,0.0757,45,0
|
||||
docs/task2/mazes/empty.txt,BFSStrategy,0.0708,56,14
|
||||
docs/task2/mazes/empty.txt,DFSStrategy,0.039,49,28
|
||||
docs/task2/mazes/empty.txt,AStarStrategy,0.1058,56,14
|
||||
|
5
VildyaevAV/docs/task2/mazes/blocked.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#######
|
||||
#S# #E#
|
||||
# # # #
|
||||
# ### #
|
||||
#######
|
||||
9
VildyaevAV/docs/task2/mazes/empty.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
##########
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##########
|
||||
11
VildyaevAV/docs/task2/mazes/large.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
####################
|
||||
#S # # #
|
||||
##### ### ### ### ##
|
||||
# # # # #
|
||||
# ### # ##### ######
|
||||
# # # # #
|
||||
# # ####### ###### #
|
||||
# # # #
|
||||
# ####### ######## #
|
||||
# # #E#
|
||||
####################
|
||||
7
VildyaevAV/docs/task2/mazes/medium.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
############
|
||||
#S # #
|
||||
### ###### #
|
||||
# # #
|
||||
# #### # ###
|
||||
# # # E#
|
||||
############
|
||||
5
VildyaevAV/docs/task2/mazes/no_exit.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#######
|
||||
#S# #
|
||||
# ### #
|
||||
# #
|
||||
#######
|
||||
5
VildyaevAV/docs/task2/mazes/small.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#######
|
||||
#S ##
|
||||
# ### #
|
||||
# E#
|
||||
#######
|
||||
BIN
VildyaevAV/docs/task2/medium_time.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
VildyaevAV/docs/task2/medium_visited.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
14
VildyaevAV/docs/task2/observer.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class Observer(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def update(self, event):
|
||||
pass
|
||||
|
||||
|
||||
class ConsoleView(Observer):
|
||||
|
||||
def update(self, event):
|
||||
print(f"[Observer] {event}")
|
||||
50
VildyaevAV/docs/task2/plot_results.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
df = pd.read_csv("maze_results.csv")
|
||||
|
||||
mazes = df["maze"].unique()
|
||||
|
||||
for maze in mazes:
|
||||
maze_df = df[df["maze"] == maze]
|
||||
|
||||
plt.figure(figsize=(8, 5))
|
||||
|
||||
plt.bar(
|
||||
maze_df["strategy"],
|
||||
maze_df["time_ms"]
|
||||
)
|
||||
|
||||
plt.title(f"Time for {maze}")
|
||||
plt.ylabel("Time ms")
|
||||
plt.xlabel("Strategy")
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
filename = maze.split("/")[-1].replace(".txt", "_time.png")
|
||||
plt.savefig(filename)
|
||||
|
||||
plt.close()
|
||||
|
||||
for maze in mazes:
|
||||
maze_df = df[df["maze"] == maze]
|
||||
|
||||
plt.figure(figsize=(8, 5))
|
||||
|
||||
plt.bar(
|
||||
maze_df["strategy"],
|
||||
maze_df["visited_cells"]
|
||||
)
|
||||
|
||||
plt.title(f"Visited cells for {maze}")
|
||||
plt.ylabel("Visited cells")
|
||||
plt.xlabel("Strategy")
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
filename = maze.split("/")[-1].replace(".txt", "_visited.png")
|
||||
plt.savefig(filename)
|
||||
|
||||
plt.close()
|
||||
|
||||
print("Graphs created")
|
||||
BIN
VildyaevAV/docs/task2/small_time.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
VildyaevAV/docs/task2/small_visited.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
50
VildyaevAV/docs/task2/solver.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
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
|
||||
|
||||
|
||||
class MazeSolver:
|
||||
def __init__(self, maze, strategy):
|
||||
self.maze = maze
|
||||
self.strategy = strategy
|
||||
self.observers = []
|
||||
|
||||
def set_strategy(self, strategy):
|
||||
self.strategy = strategy
|
||||
|
||||
def add_observer(self, observer):
|
||||
self.observers.append(observer)
|
||||
|
||||
def notify(self, event):
|
||||
for observer in self.observers:
|
||||
observer.update(event)
|
||||
|
||||
def solve(self):
|
||||
self.notify("search_started")
|
||||
|
||||
start_time = time.perf_counter()
|
||||
|
||||
path, visited_cells = self.strategy.find_path(
|
||||
self.maze,
|
||||
self.maze.start,
|
||||
self.maze.exit
|
||||
)
|
||||
|
||||
end_time = time.perf_counter()
|
||||
|
||||
self.notify("search_finished")
|
||||
|
||||
time_ms = (end_time - start_time) * 1000
|
||||
|
||||
stats = SearchStats(
|
||||
time_ms=time_ms,
|
||||
visited_cells=visited_cells,
|
||||
path_length=len(path)
|
||||
)
|
||||
|
||||
return stats, path
|
||||
187
VildyaevAV/docs/task2/strategies.py
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
from collections import deque
|
||||
import heapq
|
||||
|
||||
|
||||
class PathFindingStrategy(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def find_path(self, maze, start, exit_cell):
|
||||
pass
|
||||
|
||||
|
||||
class BFSStrategy(PathFindingStrategy):
|
||||
|
||||
def find_path(self, maze, start, exit_cell):
|
||||
|
||||
queue = deque([start])
|
||||
|
||||
visited = set()
|
||||
visited.add((start.x, start.y))
|
||||
|
||||
parent = {}
|
||||
|
||||
while queue:
|
||||
|
||||
current = queue.popleft()
|
||||
|
||||
if current == exit_cell:
|
||||
return self.restore_path(parent, start, exit_cell), len(visited)
|
||||
|
||||
for neighbor in maze.get_neighbors(current):
|
||||
|
||||
key = (neighbor.x, neighbor.y)
|
||||
|
||||
if key not in visited:
|
||||
|
||||
visited.add(key)
|
||||
|
||||
parent[key] = current
|
||||
|
||||
queue.append(neighbor)
|
||||
|
||||
return [], len(visited)
|
||||
|
||||
def restore_path(self, parent, start, exit_cell):
|
||||
|
||||
path = []
|
||||
|
||||
current = exit_cell
|
||||
|
||||
while current != start:
|
||||
|
||||
path.append(current)
|
||||
|
||||
current = parent[(current.x, current.y)]
|
||||
|
||||
path.append(start)
|
||||
|
||||
path.reverse()
|
||||
|
||||
return path
|
||||
|
||||
|
||||
class DFSStrategy(PathFindingStrategy):
|
||||
|
||||
def find_path(self, maze, start, exit_cell):
|
||||
|
||||
stack = [start]
|
||||
|
||||
visited = set()
|
||||
visited.add((start.x, start.y))
|
||||
|
||||
parent = {}
|
||||
|
||||
while stack:
|
||||
|
||||
current = stack.pop()
|
||||
|
||||
if current == exit_cell:
|
||||
return self.restore_path(parent, start, exit_cell), len(visited)
|
||||
|
||||
for neighbor in maze.get_neighbors(current):
|
||||
|
||||
key = (neighbor.x, neighbor.y)
|
||||
|
||||
if key not in visited:
|
||||
|
||||
visited.add(key)
|
||||
|
||||
parent[key] = current
|
||||
|
||||
stack.append(neighbor)
|
||||
|
||||
return [], len(visited)
|
||||
|
||||
def restore_path(self, parent, start, exit_cell):
|
||||
|
||||
path = []
|
||||
|
||||
current = exit_cell
|
||||
|
||||
while current != start:
|
||||
|
||||
path.append(current)
|
||||
|
||||
current = parent[(current.x, current.y)]
|
||||
|
||||
path.append(start)
|
||||
|
||||
path.reverse()
|
||||
|
||||
return path
|
||||
|
||||
|
||||
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, exit_cell):
|
||||
|
||||
heap = []
|
||||
|
||||
heapq.heappush(heap, (0, start.x, start.y, start))
|
||||
|
||||
visited = set()
|
||||
|
||||
parent = {}
|
||||
|
||||
g_score = {
|
||||
(start.x, start.y): 0
|
||||
}
|
||||
|
||||
while heap:
|
||||
|
||||
_, _, _, current = heapq.heappop(heap)
|
||||
|
||||
key_current = (current.x, current.y)
|
||||
|
||||
if key_current in visited:
|
||||
continue
|
||||
|
||||
visited.add(key_current)
|
||||
|
||||
if current == exit_cell:
|
||||
return self.restore_path(parent, start, exit_cell), len(visited)
|
||||
|
||||
for neighbor in maze.get_neighbors(current):
|
||||
|
||||
key = (neighbor.x, neighbor.y)
|
||||
|
||||
tentative = g_score[key_current] + 1
|
||||
|
||||
if key not in g_score or tentative < g_score[key]:
|
||||
|
||||
g_score[key] = tentative
|
||||
|
||||
priority = tentative + self.heuristic(neighbor, exit_cell)
|
||||
|
||||
heapq.heappush(
|
||||
heap,
|
||||
(priority, neighbor.x, neighbor.y, neighbor)
|
||||
)
|
||||
|
||||
parent[key] = current
|
||||
|
||||
return [], len(visited)
|
||||
|
||||
def restore_path(self, parent, start, exit_cell):
|
||||
|
||||
path = []
|
||||
|
||||
current = exit_cell
|
||||
|
||||
while current != start:
|
||||
|
||||
path.append(current)
|
||||
|
||||
current = parent[(current.x, current.y)]
|
||||
|
||||
path.append(start)
|
||||
|
||||
path.reverse()
|
||||
|
||||
return path
|
||||
243
VildyaevAV/docs/task2/task2_report.md
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
# Отчет по заданию 2
|
||||
|
||||
## Тема
|
||||
|
||||
Реализация поиска пути в лабиринте с использованием паттернов проектирования и различных алгоритмов поиска.
|
||||
|
||||
---
|
||||
|
||||
# Цель работы
|
||||
|
||||
Изучить применение ООП и паттернов проектирования при реализации алгоритмов поиска пути в лабиринте.
|
||||
|
||||
Реализовать:
|
||||
- BFS
|
||||
- DFS
|
||||
- A*
|
||||
|
||||
Сравнить эффективность алгоритмов по:
|
||||
- времени выполнения
|
||||
- количеству посещённых клеток
|
||||
- длине найденного пути
|
||||
|
||||
---
|
||||
|
||||
# Используемые паттерны
|
||||
|
||||
## Builder
|
||||
Используется для загрузки лабиринта из файла.
|
||||
|
||||
## Strategy
|
||||
Используется для переключения алгоритмов поиска пути.
|
||||
|
||||
## Observer
|
||||
Используется для уведомлений о начале и окончании поиска.
|
||||
|
||||
---
|
||||
|
||||
# Структура проекта
|
||||
|
||||
```text
|
||||
docs/task2/
|
||||
|
||||
├── mazes/
|
||||
│ ├── small.txt
|
||||
│ ├── medium.txt
|
||||
│ ├── blocked.txt
|
||||
│ ├── no_exit.txt
|
||||
│ ├── large.txt
|
||||
│ └── empty.txt
|
||||
│
|
||||
├── cell.py
|
||||
├── maze.py
|
||||
├── builder.py
|
||||
├── strategies.py
|
||||
├── solver.py
|
||||
├── observer.py
|
||||
├── main.py
|
||||
├── plot_results.py
|
||||
├── maze_results.csv
|
||||
└── task2_report.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# UML диаграмма
|
||||
|
||||
В проекте была построена UML-диаграмма классов с использованием Mermaid.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Описание классов
|
||||
|
||||
## Cell
|
||||
Класс клетки лабиринта.
|
||||
|
||||
Хранит:
|
||||
- координаты
|
||||
- тип клетки
|
||||
- признаки стены, старта и выхода
|
||||
|
||||
---
|
||||
|
||||
## Maze
|
||||
Класс лабиринта.
|
||||
|
||||
Содержит:
|
||||
- двумерный массив клеток
|
||||
- размеры лабиринта
|
||||
- стартовую клетку
|
||||
- выход
|
||||
|
||||
---
|
||||
|
||||
## TextFileMazeBuilder
|
||||
Загружает лабиринт из текстового файла.
|
||||
|
||||
---
|
||||
|
||||
## BFSStrategy
|
||||
Алгоритм поиска в ширину.
|
||||
|
||||
Находит кратчайший путь.
|
||||
|
||||
---
|
||||
|
||||
## DFSStrategy
|
||||
Алгоритм поиска в глубину.
|
||||
|
||||
Работает быстро, но путь может быть не кратчайшим.
|
||||
|
||||
---
|
||||
|
||||
## AStarStrategy
|
||||
Эвристический алгоритм поиска.
|
||||
|
||||
Использует манхэттенское расстояние.
|
||||
|
||||
---
|
||||
|
||||
## MazeSolver
|
||||
Основной класс-оркестратор.
|
||||
|
||||
Запускает алгоритм поиска и собирает статистику.
|
||||
|
||||
---
|
||||
|
||||
# Результаты экспериментов
|
||||
|
||||
Результаты сохраняются в файл:
|
||||
|
||||
```text
|
||||
maze_results.csv
|
||||
```
|
||||
|
||||
Проводилось сравнение:
|
||||
- времени работы
|
||||
- количества посещённых клеток
|
||||
- длины пути
|
||||
|
||||
## Таблица результатов
|
||||
|
||||
| Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути |
|
||||
|---|---|---|---|---|
|
||||
| small | BFS | 0.0396 | 10 | 7 |
|
||||
| small | DFS | 0.0251 | 10 | 7 |
|
||||
| small | A* | 0.0359 | 10 | 7 |
|
||||
| medium | BFS | 0.0312 | 18 | 0 |
|
||||
| medium | DFS | 0.0277 | 18 | 0 |
|
||||
| medium | A* | 0.0359 | 18 | 0 |
|
||||
| blocked | BFS | 0.0123 | 3 | 0 |
|
||||
| blocked | DFS | 0.0089 | 3 | 0 |
|
||||
| blocked | A* | 0.0133 | 3 | 0 |
|
||||
| large | BFS | 0.0602 | 45 | 0 |
|
||||
| large | DFS | 0.0509 | 45 | 0 |
|
||||
| large | A* | 0.0682 | 45 | 0 |
|
||||
| empty | BFS | 0.0711 | 56 | 14 |
|
||||
| empty | DFS | 0.0419 | 49 | 28 |
|
||||
| empty | A* | 0.1144 | 56 | 14 |
|
||||
|
||||
---
|
||||
|
||||
## Графики
|
||||
|
||||
### Время выполнения
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
### Количество посещённых клеток
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
---
|
||||
|
||||
# Графики
|
||||
|
||||
Построены графики:
|
||||
- времени выполнения
|
||||
- количества посещённых клеток
|
||||
|
||||
Для каждого лабиринта.
|
||||
|
||||
---
|
||||
|
||||
# Анализ эффективности алгоритмов
|
||||
|
||||
В ходе экспериментов были сравнены алгоритмы BFS, DFS и A* на лабиринтах различной сложности.
|
||||
|
||||
## BFS
|
||||
|
||||
Алгоритм BFS гарантированно находит кратчайший путь, однако может посещать большое количество клеток. На больших лабиринтах время работы увеличивается.
|
||||
|
||||
## DFS
|
||||
|
||||
DFS работает быстрее других алгоритмов, так как уходит в глубину и не исследует все возможные пути. Однако найденный путь может быть не кратчайшим.
|
||||
|
||||
## A*
|
||||
|
||||
Алгоритм A* использует эвристику и старается двигаться к выходу наиболее оптимальным образом. На простых лабиринтах показывает хорошие результаты, однако на некоторых картах из-за вычисления эвристики работает медленнее DFS.
|
||||
|
||||
## Вывод по экспериментам
|
||||
|
||||
- DFS показал наименьшее время выполнения.
|
||||
- BFS обеспечивает поиск кратчайшего пути.
|
||||
- A* хорошо подходит для сложных лабиринтов с большим количеством вариантов движения.
|
||||
- На лабиринтах без выхода все алгоритмы посещают примерно одинаковое количество клеток.
|
||||
|
||||
# Выводы
|
||||
|
||||
В ходе работы была реализована система поиска пути в лабиринте с использованием объектно-ориентированного подхода и паттернов проектирования.
|
||||
|
||||
Были использованы паттерны:
|
||||
|
||||
- Builder — для загрузки лабиринта из файла.
|
||||
- Strategy — для переключения алгоритмов поиска пути.
|
||||
- Observer — для уведомлений о событиях поиска.
|
||||
|
||||
Использование паттернов позволило сделать архитектуру гибкой и расширяемой.
|
||||
|
||||
Например:
|
||||
- можно легко добавить новый алгоритм поиска пути;
|
||||
- можно реализовать другой способ загрузки лабиринта;
|
||||
- можно подключить новые способы отображения информации.
|
||||
|
||||
Без применения паттернов код был бы более связанным и сложным для расширения и поддержки.
|
||||