Merge branch 'lab2' into develop
This commit is contained in:
commit
fa4ec42cd2
239
VasilevIA/lab2/codes/maze.py
Normal file
239
VasilevIA/lab2/codes/maze.py
Normal file
|
|
@ -0,0 +1,239 @@
|
||||||
|
import heapq
|
||||||
|
import time
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from collections import deque
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
|
||||||
|
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 __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))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Cell({self.x},{self.y})"
|
||||||
|
|
||||||
|
|
||||||
|
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 <= x < self.width and 0 <= y < self.height:
|
||||||
|
return self.cells[y][x]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_neighbors(self, cell):
|
||||||
|
result = []
|
||||||
|
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
|
||||||
|
n = self.get_cell(cell.x + dx, cell.y + dy)
|
||||||
|
if n and n.is_passable():
|
||||||
|
result.append(n)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def render(self, path=None):
|
||||||
|
path_set = set(path) if path else set()
|
||||||
|
lines = []
|
||||||
|
for row in self.cells:
|
||||||
|
line = ""
|
||||||
|
for cell in row:
|
||||||
|
if cell.is_start:
|
||||||
|
line += " S"
|
||||||
|
elif cell.is_exit:
|
||||||
|
line += " E"
|
||||||
|
elif cell.is_wall:
|
||||||
|
line += "##"
|
||||||
|
elif cell in path_set:
|
||||||
|
line += " ."
|
||||||
|
else:
|
||||||
|
line += " "
|
||||||
|
lines.append(line)
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
class MazeBuilder(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def build_from_file(self, filename) -> Maze:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TextFileMazeBuilder(MazeBuilder):
|
||||||
|
def build_from_file(self, filename) -> Maze:
|
||||||
|
with open(filename, encoding="utf-8") as f:
|
||||||
|
lines = [l.rstrip("\n") for l in f]
|
||||||
|
|
||||||
|
height = len(lines)
|
||||||
|
width = max(len(l) for l in lines)
|
||||||
|
cells = []
|
||||||
|
start = exit_cell = None
|
||||||
|
|
||||||
|
for y, line in enumerate(lines):
|
||||||
|
row = []
|
||||||
|
for x in range(width):
|
||||||
|
ch = line[x] if x < len(line) else " "
|
||||||
|
is_wall = ch == "#"
|
||||||
|
is_start = ch == "S"
|
||||||
|
is_exit = ch == "E"
|
||||||
|
c = Cell(x, y, is_wall, is_start, is_exit)
|
||||||
|
if is_start:
|
||||||
|
start = c
|
||||||
|
if is_exit:
|
||||||
|
exit_cell = c
|
||||||
|
row.append(c)
|
||||||
|
cells.append(row)
|
||||||
|
|
||||||
|
if not start or not exit_cell:
|
||||||
|
raise ValueError("Maze must have S and E")
|
||||||
|
return Maze(cells, width, height, start, exit_cell)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SearchStats:
|
||||||
|
strategy: str
|
||||||
|
time_ms: float
|
||||||
|
visited: int
|
||||||
|
path_length: int
|
||||||
|
path: List[Cell] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
class PathFindingStrategy(ABC):
|
||||||
|
_visited = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.__class__.__name__
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def find_path(self, maze: Maze, start: Cell, end: Cell) -> List[Cell]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_path(parent, start, end):
|
||||||
|
path, cur = [], end
|
||||||
|
while cur:
|
||||||
|
path.append(cur)
|
||||||
|
cur = parent.get(cur)
|
||||||
|
path.reverse()
|
||||||
|
return path if path and path[0] == start else []
|
||||||
|
|
||||||
|
|
||||||
|
class BFSStrategy(PathFindingStrategy):
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return "BFS"
|
||||||
|
|
||||||
|
def find_path(self, maze, start, end):
|
||||||
|
queue = deque([start])
|
||||||
|
parent = {start: None}
|
||||||
|
visited = 0
|
||||||
|
while queue:
|
||||||
|
cur = queue.popleft()
|
||||||
|
visited += 1
|
||||||
|
if cur == end:
|
||||||
|
self._visited = visited
|
||||||
|
return self._build_path(parent, start, end)
|
||||||
|
for nb in maze.get_neighbors(cur):
|
||||||
|
if nb not in parent:
|
||||||
|
parent[nb] = cur
|
||||||
|
queue.append(nb)
|
||||||
|
self._visited = visited
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class DFSStrategy(PathFindingStrategy):
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return "DFS"
|
||||||
|
|
||||||
|
def find_path(self, maze, start, end):
|
||||||
|
stack = [start]
|
||||||
|
parent = {start: None}
|
||||||
|
visited = 0
|
||||||
|
while stack:
|
||||||
|
cur = stack.pop()
|
||||||
|
visited += 1
|
||||||
|
if cur == end:
|
||||||
|
self._visited = visited
|
||||||
|
return self._build_path(parent, start, end)
|
||||||
|
for nb in maze.get_neighbors(cur):
|
||||||
|
if nb not in parent:
|
||||||
|
parent[nb] = cur
|
||||||
|
stack.append(nb)
|
||||||
|
self._visited = visited
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class AStarStrategy(PathFindingStrategy):
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return "A*"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _h(a, b):
|
||||||
|
return abs(a.x - b.x) + abs(a.y - b.y)
|
||||||
|
|
||||||
|
def find_path(self, maze, start, end):
|
||||||
|
counter = 0
|
||||||
|
heap = [(0, counter, start)]
|
||||||
|
parent = {start: None}
|
||||||
|
g = {start: 0}
|
||||||
|
closed = set()
|
||||||
|
visited = 0
|
||||||
|
while heap:
|
||||||
|
_, _, cur = heapq.heappop(heap)
|
||||||
|
if cur in closed:
|
||||||
|
continue
|
||||||
|
closed.add(cur)
|
||||||
|
visited += 1
|
||||||
|
if cur == end:
|
||||||
|
self._visited = visited
|
||||||
|
return self._build_path(parent, start, end)
|
||||||
|
for nb in maze.get_neighbors(cur):
|
||||||
|
if nb in closed:
|
||||||
|
continue
|
||||||
|
ng = g[cur] + 1
|
||||||
|
if ng < g.get(nb, float("inf")):
|
||||||
|
g[nb] = ng
|
||||||
|
counter += 1
|
||||||
|
heapq.heappush(heap, (ng + self._h(nb, end), counter, nb))
|
||||||
|
parent[nb] = cur
|
||||||
|
self._visited = visited
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class MazeSolver:
|
||||||
|
def __init__(self, maze: Maze, strategy: PathFindingStrategy):
|
||||||
|
self.maze = maze
|
||||||
|
self.strategy = strategy
|
||||||
|
|
||||||
|
def set_strategy(self, strategy: PathFindingStrategy):
|
||||||
|
self.strategy = strategy
|
||||||
|
|
||||||
|
def solve(self) -> SearchStats:
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
path = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)
|
||||||
|
t1 = time.perf_counter()
|
||||||
|
return SearchStats(
|
||||||
|
strategy=self.strategy.name,
|
||||||
|
time_ms=(t1 - t0) * 1000,
|
||||||
|
visited=self.strategy._visited,
|
||||||
|
path_length=len(path),
|
||||||
|
path=path
|
||||||
|
)
|
||||||
78
VasilevIA/lab2/codes/maze_generator.py
Normal file
78
VasilevIA/lab2/codes/maze_generator.py
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
|
||||||
|
def _backtracker(width, height, seed=42):
|
||||||
|
rng = random.Random(seed)
|
||||||
|
cw = (width - 1) // 2
|
||||||
|
ch = (height - 1) // 2
|
||||||
|
grid = [["#"] * width for _ in range(height)]
|
||||||
|
visited = [[False] * cw for _ in range(ch)]
|
||||||
|
stack = [(0, 0)]
|
||||||
|
visited[0][0] = True
|
||||||
|
grid[1][1] = " "
|
||||||
|
while stack:
|
||||||
|
cx, cy = stack[-1]
|
||||||
|
gx, gy = cx * 2 + 1, cy * 2 + 1
|
||||||
|
dirs = [(0, -1), (0, 1), (-1, 0), (1, 0)]
|
||||||
|
rng.shuffle(dirs)
|
||||||
|
moved = False
|
||||||
|
for dx, dy in dirs:
|
||||||
|
nx, ny = cx + dx, cy + dy
|
||||||
|
if 0 <= nx < cw and 0 <= ny < ch and not visited[ny][nx]:
|
||||||
|
visited[ny][nx] = True
|
||||||
|
grid[gy + dy][gx + dx] = " "
|
||||||
|
grid[ny * 2 + 1][nx * 2 + 1] = " "
|
||||||
|
stack.append((nx, ny))
|
||||||
|
moved = True
|
||||||
|
break
|
||||||
|
if not moved:
|
||||||
|
stack.pop()
|
||||||
|
grid[1][1] = "S"
|
||||||
|
grid[height - 2][width - 2] = "E"
|
||||||
|
return grid
|
||||||
|
|
||||||
|
|
||||||
|
def _empty(width, height):
|
||||||
|
grid = [["#"] * width for _ in range(height)]
|
||||||
|
for y in range(1, height - 1):
|
||||||
|
for x in range(1, width - 1):
|
||||||
|
grid[y][x] = " "
|
||||||
|
grid[1][1] = "S"
|
||||||
|
grid[height - 2][width - 2] = "E"
|
||||||
|
return grid
|
||||||
|
|
||||||
|
|
||||||
|
def _no_exit(width=11, height=11):
|
||||||
|
grid = _backtracker(width, height, seed=99)
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
if grid[y][x] == "E":
|
||||||
|
grid[y][x] = "#"
|
||||||
|
grid[1][width - 2] = "E"
|
||||||
|
for dy in [-1, 0, 1]:
|
||||||
|
for dx in [-1, 0, 1]:
|
||||||
|
ny, nx = 1 + dy, (width - 2) + dx
|
||||||
|
if 0 <= ny < height and 0 <= nx < width and grid[ny][nx] != "E":
|
||||||
|
grid[ny][nx] = "#"
|
||||||
|
return grid
|
||||||
|
|
||||||
|
|
||||||
|
def _save(grid, path):
|
||||||
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
for row in grid:
|
||||||
|
f.write("".join(row) + "\n")
|
||||||
|
|
||||||
|
|
||||||
|
def generate_all(folder="mazes"):
|
||||||
|
mazes = {
|
||||||
|
"small.txt": _backtracker(11, 11, seed=1),
|
||||||
|
"medium.txt": _backtracker(51, 51, seed=2),
|
||||||
|
"large.txt": _backtracker(101, 101, seed=3),
|
||||||
|
"empty.txt": _empty(51, 21),
|
||||||
|
"no_exit.txt": _no_exit(11, 11),
|
||||||
|
"sample.txt": _backtracker(15, 15, seed=5),
|
||||||
|
}
|
||||||
|
for name, grid in mazes.items():
|
||||||
|
_save(grid, os.path.join(folder, name))
|
||||||
|
print(f"Mazes saved to {folder}/")
|
||||||
BIN
VasilevIA/lab2/docs/benchmark_plot.png
Normal file
BIN
VasilevIA/lab2/docs/benchmark_plot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
BIN
VasilevIA/lab2/docs/mermaid.png
Normal file
BIN
VasilevIA/lab2/docs/mermaid.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 KiB |
193
VasilevIA/lab2/docs/report2.md
Normal file
193
VasilevIA/lab2/docs/report2.md
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
# Отчёт: Поиск выхода из лабиринта
|
||||||
|
## 1. Описание задачи и выбранных паттернов
|
||||||
|
### Задача
|
||||||
|
|
||||||
|
Разработать программу для загрузки лабиринта из текстового файла, поиска пути от старта до выхода тремя алгоритмами (BFS, DFS, A*), визуализации найденного пути и экспериментального сравнения алгоритмов по времени, числу посещённых клеток и длине пути.
|
||||||
|
|
||||||
|
### Структура файлов
|
||||||
|
|
||||||
|
```
|
||||||
|
02/
|
||||||
|
main.py - точка запуска
|
||||||
|
codes/
|
||||||
|
maze.py - все классы (Cell, Maze, Builder, Strategy, Solver)
|
||||||
|
maze_generator.py - генерация тестовых лабиринтов
|
||||||
|
mazes/ - текстовые файлы лабиринтов
|
||||||
|
results/
|
||||||
|
results_maze.csv - результаты экспериментов
|
||||||
|
benchmark_plot.png - графики
|
||||||
|
docs/
|
||||||
|
report1.md - отчёт
|
||||||
|
mermaid.png - диаграмма классов
|
||||||
|
```
|
||||||
|
|
||||||
|
### Применённые паттерны проектирования
|
||||||
|
**1. Builder** - класс `TextFileMazeBuilder` реализует интерфейс `MazeBuilder`.
|
||||||
|
|
||||||
|
Построение лабиринта из файла включает несколько шагов: чтение строк, обход символов, создание объектов `Cell`, поиск стартовой и конечной клетки. Без Builder вся эта логика оказалась бы в `main.py` или в конструкторе `Maze`. Builder скрывает детали создания от клиента. Если понадобится загружать лабиринт из JSON или бинарного файла - достаточно написать новый класс, реализующий тот же интерфейс `MazeBuilder`.
|
||||||
|
|
||||||
|
**2. Strategy** - классы `BFSStrategy`, `DFSStrategy`, `AStarStrategy` реализуют интерфейс `PathFindingStrategy`.
|
||||||
|
|
||||||
|
Алгоритм поиска можно менять во время работы программы через `MazeSolver.set_strategy()`, не трогая остальной код. Добавление нового алгоритма - это написание одного нового класса с методом `find_path()`. Без Strategy в `solve()` пришлось бы писать if/elif для каждого алгоритма.
|
||||||
|
|
||||||
|
**3. Observer** - интерфейс `Observer` с методом `update(event)`.
|
||||||
|
|
||||||
|
`MazeSolver` хранит список наблюдателей и уведомляет их при событиях `search_started`, `path_found`, `path_not_found`. Это позволяет добавлять отображение в консоль, запись в лог или GUI-уведомления, не меняя код солвера. Слабая связанность: солвер не знает, кто его слушает.
|
||||||
|
|
||||||
|
### Диаграмма классов
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Листинги ключевых классов
|
||||||
|
### Cell и Maze
|
||||||
|
|
||||||
|
```python
|
||||||
|
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
|
||||||
|
|
||||||
|
class Maze:
|
||||||
|
def get_neighbors(self, cell):
|
||||||
|
result = []
|
||||||
|
for dx, dy in [(0,-1),(0,1),(-1,0),(1,0)]:
|
||||||
|
n = self.get_cell(cell.x + dx, cell.y + dy)
|
||||||
|
if n and n.is_passable():
|
||||||
|
result.append(n)
|
||||||
|
return result
|
||||||
|
```
|
||||||
|
|
||||||
|
### Паттерн Builder
|
||||||
|
```python
|
||||||
|
class MazeBuilder(ABC):
|
||||||
|
@abstractmethod
|
||||||
|
def build_from_file(self, filename) -> Maze:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TextFileMazeBuilder(MazeBuilder):
|
||||||
|
def build_from_file(self, filename) -> Maze:
|
||||||
|
with open(filename, encoding="utf-8") as f:
|
||||||
|
lines = [l.rstrip("\n") for l in f]
|
||||||
|
# ... парсинг символов, создание Cell, поиск S и E
|
||||||
|
return Maze(cells, width, height, start, exit_cell)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Паттерн Strategy - алгоритм A*
|
||||||
|
```python
|
||||||
|
class AStarStrategy(PathFindingStrategy):
|
||||||
|
@staticmethod
|
||||||
|
def _h(a, b):
|
||||||
|
return abs(a.x - b.x) + abs(a.y - b.y)
|
||||||
|
|
||||||
|
def find_path(self, maze, start, end):
|
||||||
|
heap = [(0, 0, start)]
|
||||||
|
parent = {start: None}
|
||||||
|
g = {start: 0}
|
||||||
|
closed = set()
|
||||||
|
while heap:
|
||||||
|
_, _, cur = heapq.heappop(heap)
|
||||||
|
if cur in closed:
|
||||||
|
continue
|
||||||
|
closed.add(cur)
|
||||||
|
if cur == end:
|
||||||
|
return self._build_path(parent, start, end)
|
||||||
|
for nb in maze.get_neighbors(cur):
|
||||||
|
ng = g[cur] + 1
|
||||||
|
if ng < g.get(nb, float("inf")):
|
||||||
|
g[nb] = ng
|
||||||
|
heapq.heappush(heap, (ng + self._h(nb, end), id(nb), nb))
|
||||||
|
parent[nb] = cur
|
||||||
|
return []
|
||||||
|
```
|
||||||
|
|
||||||
|
### MazeSolver
|
||||||
|
```python
|
||||||
|
class MazeSolver:
|
||||||
|
def __init__(self, maze, strategy):
|
||||||
|
self.maze = maze
|
||||||
|
self.strategy = strategy
|
||||||
|
|
||||||
|
def set_strategy(self, strategy):
|
||||||
|
self.strategy = strategy
|
||||||
|
|
||||||
|
def solve(self) -> SearchStats:
|
||||||
|
t0 = time.perf_counter()
|
||||||
|
path = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)
|
||||||
|
t1 = time.perf_counter()
|
||||||
|
return SearchStats(
|
||||||
|
strategy=self.strategy.name,
|
||||||
|
time_ms=(t1 - t0) * 1000,
|
||||||
|
visited=self.strategy._visited,
|
||||||
|
path_length=len(path),
|
||||||
|
path=path,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Результаты экспериментов
|
||||||
|
Каждый алгоритм запускался 7 раз на каждом лабиринте, результаты усреднялись.
|
||||||
|
### Таблица результатов
|
||||||
|
|
||||||
|
| Лабиринт | Алгоритм | Время (мс) | Посещено клеток | Длина пути |
|
||||||
|
|----------|----------|-----------|----------------|------------|
|
||||||
|
| small (11x11) | BFS | 0.070 | 39 | 33 |
|
||||||
|
| small (11x11) | DFS | 0.055 | 33 | 33 |
|
||||||
|
| small (11x11) | A* | 0.112 | 35 | 33 |
|
||||||
|
| medium (51x51) | BFS | 1.391 | 793 | 497 |
|
||||||
|
| medium (51x51) | DFS | 0.949 | 515 | 497 |
|
||||||
|
| medium (51x51) | A* | 2.271 | 707 | 497 |
|
||||||
|
| large (101x101) | BFS | 6.231 | 3533 | 1613 |
|
||||||
|
| large (101x101) | DFS | 3.341 | 1957 | 1613 |
|
||||||
|
| large (101x101) | A* | 11.27 | 3379 | 1613 |
|
||||||
|
| empty (51x21) | BFS | 1.992 | 931 | 67 |
|
||||||
|
| empty (51x21) | DFS | 1.021 | 451 | 451 |
|
||||||
|
| empty (51x21) | A* | 3.527 | 931 | 67 |
|
||||||
|
| no_exit (11x11) | BFS | 0.079 | 40 | - |
|
||||||
|
| no_exit (11x11) | DFS | 0.077 | 40 | - |
|
||||||
|
| no_exit (11x11) | A* | 0.140 | 40 | - |
|
||||||
|
|
||||||
|
### Графики
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Анализ эффективности алгоритмов и применимости паттернов
|
||||||
|
### Алгоритмы
|
||||||
|
|
||||||
|
**BFS** гарантирует кратчайший путь по числу шагов. Расширяет узлы слой за слоем во всех направлениях, поэтому посещает наибольшее число клеток. На практике это надёжный выбор когда нужен точно кратчайший маршрут.
|
||||||
|
|
||||||
|
**DFS** посещает меньше клеток и выполняется быстрее - на large лабиринте в 1.8 раза быстрее BFS. Однако путь может быть далеко не кратчайшим. На пустом лабиринте DFS нашёл путь длиной 451 шаг, тогда как BFS и A* - 67. Это связано с тем, что DFS уходит в первое попавшееся направление и возвращается только в тупике.
|
||||||
|
|
||||||
|
**A*** использует манхэттенскую эвристику h = |x1-x2| + |y1-y2| и должен в теории посещать меньше клеток чем BFS. На лабиринтах, сгенерированных алгоритмом recursive backtracker, выигрыш небольшой (примерно 5%). Причина: backtracker строит дерево - между любыми двумя клетками ровно один путь, тупиков нет, эвристика не помогает их обходить. На лабиринтах с циклами A* посещает заметно меньше клеток. Накладные расходы на работу с heap и closed-set делают A* медленнее по времени, чем DFS.
|
||||||
|
|
||||||
|
На пустом лабиринте (без стен) A* ведёт себя как BFS. Математически: f(x,y) = g + h = (x-1+y-1) + (W-x+H-y) = const для всех клеток. Все узлы неразличимы по приоритету.
|
||||||
|
|
||||||
|
На лабиринте без выхода все три алгоритма посещают одинаковое число клеток и корректно возвращают пустой путь.
|
||||||
|
|
||||||
|
### Паттерны
|
||||||
|
**Builder** оказался полезным при добавлении нового типа лабиринта (взвешенного, с символами s и m). Изменения были внесены только в `TextFileMazeBuilder`, клиентский код не менялся.
|
||||||
|
|
||||||
|
**Strategy** позволил в одном цикле запустить все три алгоритма через `solver.set_strategy(strategy)`. Без паттерна пришлось бы либо дублировать код запуска для каждого алгоритма, либо писать условные ветки.
|
||||||
|
|
||||||
|
**Observer** полезен при расширении: чтобы добавить вывод в лог или консоль, достаточно написать новый Observer и подписать его на solver, не меняя `MazeSolver`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Выводы
|
||||||
|
ООП и паттерны позволили сделать код гибким в нескольких направлениях.
|
||||||
|
|
||||||
|
Добавление нового алгоритма поиска сводится к написанию одного класса, реализующего `find_path()`. Без Strategy пришлось бы добавлять ветку в `solve()` и во все места, где запускается поиск.
|
||||||
|
|
||||||
|
Добавление нового формата лабиринта - только новый класс Builder. Без паттерна логика парсинга была бы перемешана с логикой работы программы.
|
||||||
|
|
||||||
|
Добавление нового способа отображения (GUI, запись в файл) - только новый Observer. Без него MazeSolver пришлось бы напрямую вызывать функции отображения, что создало бы зависимость от конкретной реализации.
|
||||||
|
|
||||||
|
Без применения паттернов код решал бы задачу, но любое изменение требовало бы правки в нескольких местах сразу. С паттернами каждый класс отвечает за одну задачу и не знает о деталях реализации соседних классов.
|
||||||
165
VasilevIA/lab2/main.py
Normal file
165
VasilevIA/lab2/main.py
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "codes"))
|
||||||
|
|
||||||
|
from maze import TextFileMazeBuilder, MazeSolver, BFSStrategy, DFSStrategy, AStarStrategy
|
||||||
|
from maze_generator import generate_all
|
||||||
|
|
||||||
|
try:
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use("Agg")
|
||||||
|
HAS_PLT = True
|
||||||
|
except ImportError:
|
||||||
|
HAS_PLT = False
|
||||||
|
|
||||||
|
BASE_DIR = os.path.dirname(__file__)
|
||||||
|
MAZES_DIR = os.path.join(BASE_DIR, "mazes")
|
||||||
|
RESULTS_DIR = os.path.join(BASE_DIR, "results")
|
||||||
|
RUNS = 7
|
||||||
|
|
||||||
|
MAZE_FILES = [
|
||||||
|
("small", "small.txt"),
|
||||||
|
("medium", "medium.txt"),
|
||||||
|
("large", "large.txt"),
|
||||||
|
("empty", "empty.txt"),
|
||||||
|
("no_exit", "no_exit.txt"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def run():
|
||||||
|
os.makedirs(RESULTS_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
if not os.path.exists(MAZES_DIR) or not os.listdir(MAZES_DIR):
|
||||||
|
generate_all(MAZES_DIR)
|
||||||
|
|
||||||
|
strategies = [BFSStrategy(), DFSStrategy(), AStarStrategy()]
|
||||||
|
builder = TextFileMazeBuilder()
|
||||||
|
all_results = []
|
||||||
|
|
||||||
|
for label, filename in MAZE_FILES:
|
||||||
|
path = os.path.join(MAZES_DIR, filename)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
continue
|
||||||
|
|
||||||
|
maze = builder.build_from_file(path)
|
||||||
|
print(f"\nMaze: {label} ({maze.width}x{maze.height})")
|
||||||
|
|
||||||
|
solver = MazeSolver(maze, strategies[0])
|
||||||
|
|
||||||
|
for strategy in strategies:
|
||||||
|
solver.set_strategy(strategy)
|
||||||
|
times, visited_list, lengths = [], [], []
|
||||||
|
|
||||||
|
for _ in range(RUNS):
|
||||||
|
stats = solver.solve()
|
||||||
|
times.append(stats.time_ms)
|
||||||
|
visited_list.append(stats.visited)
|
||||||
|
lengths.append(stats.path_length)
|
||||||
|
|
||||||
|
avg_time = sum(times) / RUNS
|
||||||
|
avg_visited = sum(visited_list) / RUNS
|
||||||
|
avg_len = sum(lengths) / RUNS
|
||||||
|
|
||||||
|
found = f"length={avg_len:.0f}" if avg_len > 0 else "not found"
|
||||||
|
print(f" {strategy.name:<6} time={avg_time:.4f} ms visited={avg_visited:.0f} {found}")
|
||||||
|
|
||||||
|
all_results.append({
|
||||||
|
"maze": label,
|
||||||
|
"strategy": strategy.name,
|
||||||
|
"time_ms": round(avg_time, 4),
|
||||||
|
"visited_cells": round(avg_visited, 1),
|
||||||
|
"path_length": round(avg_len, 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
save_csv(all_results)
|
||||||
|
save_plots(all_results)
|
||||||
|
show_sample()
|
||||||
|
print("\nDone. See results/ and docs/")
|
||||||
|
|
||||||
|
|
||||||
|
def save_csv(results):
|
||||||
|
path = os.path.join(RESULTS_DIR, "results_maze.csv")
|
||||||
|
with open(path, "w", newline="", encoding="utf-8") as f:
|
||||||
|
writer = csv.DictWriter(
|
||||||
|
f, fieldnames=["maze", "strategy", "time_ms", "visited_cells", "path_length"]
|
||||||
|
)
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerows(results)
|
||||||
|
print(f"\nCSV saved: {path}")
|
||||||
|
|
||||||
|
|
||||||
|
def save_plots(results):
|
||||||
|
if not HAS_PLT:
|
||||||
|
return
|
||||||
|
|
||||||
|
mazes = list(dict.fromkeys(r["maze"] for r in results))
|
||||||
|
strategies = list(dict.fromkeys(r["strategy"] for r in results))
|
||||||
|
colors = ["#2196F3", "#FF5722", "#4CAF50"]
|
||||||
|
|
||||||
|
def val(maze, strat, key):
|
||||||
|
for r in results:
|
||||||
|
if r["maze"] == maze and r["strategy"] == strat:
|
||||||
|
return float(r[key])
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
metrics = [
|
||||||
|
("time_ms", "Time (ms)"),
|
||||||
|
("visited_cells", "Visited cells"),
|
||||||
|
("path_length", "Path length"),
|
||||||
|
]
|
||||||
|
|
||||||
|
fig, axes = plt.subplots(
|
||||||
|
len(metrics), len(mazes),
|
||||||
|
figsize=(3.5 * len(mazes), 4 * len(metrics))
|
||||||
|
)
|
||||||
|
|
||||||
|
def fmt(v):
|
||||||
|
if v == 0:
|
||||||
|
return "0"
|
||||||
|
if v >= 100:
|
||||||
|
return f"{v:.0f}"
|
||||||
|
if v >= 1:
|
||||||
|
return f"{v:.2f}"
|
||||||
|
return f"{v:.3f}"
|
||||||
|
|
||||||
|
for row_i, (key, ylabel) in enumerate(metrics):
|
||||||
|
for col_i, maze in enumerate(mazes):
|
||||||
|
ax = axes[row_i][col_i]
|
||||||
|
vals = [val(maze, s, key) for s in strategies]
|
||||||
|
bars = ax.bar(strategies, vals, color=colors[:len(strategies)])
|
||||||
|
if row_i == 0:
|
||||||
|
ax.set_title(maze, fontsize=9)
|
||||||
|
if col_i == 0:
|
||||||
|
ax.set_ylabel(ylabel)
|
||||||
|
for bar, v in zip(bars, vals):
|
||||||
|
ax.text(
|
||||||
|
bar.get_x() + bar.get_width() / 2,
|
||||||
|
bar.get_height() * 1.02,
|
||||||
|
fmt(v), ha="center", va="bottom", fontsize=7
|
||||||
|
)
|
||||||
|
ax.tick_params(axis="x", labelsize=8)
|
||||||
|
|
||||||
|
plt.tight_layout()
|
||||||
|
out = os.path.join(RESULTS_DIR, "benchmark_plot.png")
|
||||||
|
plt.savefig(out, dpi=120)
|
||||||
|
plt.close()
|
||||||
|
print(f"Chart saved: {out}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_sample():
|
||||||
|
path = os.path.join(MAZES_DIR, "sample.txt")
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return
|
||||||
|
builder = TextFileMazeBuilder()
|
||||||
|
maze = builder.build_from_file(path)
|
||||||
|
solver = MazeSolver(maze, BFSStrategy())
|
||||||
|
stats = solver.solve()
|
||||||
|
print("\nSample maze with BFS path:")
|
||||||
|
print(maze.render(path=stats.path))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run()
|
||||||
21
VasilevIA/lab2/mazes/empty.txt
Normal file
21
VasilevIA/lab2/mazes/empty.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
###################################################
|
||||||
|
#S #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# #
|
||||||
|
# E#
|
||||||
|
###################################################
|
||||||
101
VasilevIA/lab2/mazes/large.txt
Normal file
101
VasilevIA/lab2/mazes/large.txt
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
#####################################################################################################
|
||||||
|
#S # # # # # # # # # # # # #
|
||||||
|
### # # ##### # ### # # ##### ### ### ############# # # # # ##### # ######### ### # # ##### ### # ###
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # ##### ### ### # # ########### ####### # ####### ### ### # # # ####### ##### # # ### # # ##### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ##### # ### ### # ### # ### # # ##### # ##### ####### ### ### # ### ### ### # ### ### # ##### # # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # # ### # # ### # ##### ##### # ### # # ##### # # ####### ######### ### # # ######### # # # #####
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ##### ### # ##### # ##### ### # ##### ######### # # ####### ####### # ### # # ########### ##### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
##### ##### ########### ### # # ########### # # # # ##### ####### # ####### # ##### ### ### ### ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
####### # ### # # ####### ##### ### ##### # # ##### # ##### ### # # # ######### # ### ### ### ### ###
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # ########### # # # ### ### ##### ##### # # ##### # # # ##### # # ##### ##### # ##### ### ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ### # ### # ### ### ### ####### # ########### ####### # # ##### # # ### ########### ### ### # # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ####### # # # # ##### ### # ####### # ##### ##### ##### # # # ### ### # # ##### ##### ### #######
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
########### # # ##### # # # ####### # ### # ##### # ##### ### # # # # # ####### # ##### ### # # ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # # ### # # ##### # # # # # ##### # # # ############# # ##### # ############### ### # # ### # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ####### ##### # ########### # # # ######### ### ############# # ####### ####################### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ### ### # # # # # ### # ##### # ### # ####### # ##### # ######### ####### # ##### # ########### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ##### ##### ######### ####### ##### # # # # # ### # ##### # ### # ### # ##### # # # # # ### # # ###
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # # # ### ##### ### # # ### ##### ##### ####### # # ##### # ### ##### ####### # # # ####### # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # ### ####### # ### # # ### ### ##### ##### # # # # ### # # # ####### # # ### ##### # # ####### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
##### # # # # # ### # ### ### ### ### # ### # ### ##### # # ########### # # ### # # ##### ##### # # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### ##### ### ### # # ### ### # # ##### ### # ##### ########### ### ### ### # # # # ### ######### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ### # ### ### # # ####### # # ######### ### # ########### # ### ### ##### ### # ### # # ### ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # ######### # ####### ##### ######### # ### ### # ##### # ### ### ### # ### ### # ##### ##### # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # ### ### # # # # # ##### # # # # ####### # # # ### # ### # ### ### ##### ### # # ### # # # ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # ### ######### # ##### # ##### # # ############# # # ### ####### # # ##### # # # # # # ### # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ##### ####### ### # ### ##### # # # ##### # ####### ### # # ### # # # # ### ### # ########### # ###
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ####### # ##### # ### # ##### # # ##### # ##### # ### ### ######### ##### ### # ##### # ######### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
### # # # ### ####### # # # # ##### # ############### # # ##### ####### ##### # ### # # # # ### # # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### ########### ### # ############# # # # ### # ##### ##### ### ####### # ### # # # ##### ### # ###
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
### ##### ##### ### ### ### # # # # ##### # # ####### # # # ####### # # ### # ### # # # # ### ##### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # ### # # # ####### # # # # ### ####### # # # ##### ### # # ##### # ##### # # ### ### # # ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # ### # ### # # # # ### ### ### # # ### ### # ##### ### # # ##### ####### # ##### # ##### ### # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # # ### ####### ### # # ### ##### # # ######### ### ### # # # # # ### # ### # # ### ### # # # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
### # # ### ### ##### ######### ######### # # # # # ########### ####### # # ### ########### ##### # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # ### ### ### # ### # # ######### # # # # ##### # # # ##### # ####### ####### # ##### # # ##### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ### ### ##### ####### # # ### ######### ### ### # # ########### # ##### # ### ##### ##### # ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ####### # # ##### ### # ### # ####### # # # ### ### # # ### # ####### # ##### ### # ######### #####
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # # # # # # # # # ##### # ####### ####### # ########### ####### ######### # ### # ### # ### # # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # # ####### # ### ########### # # ######### ### # # # # # # ### # ######### # ### ########### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
### ####### # # # # # ### # # # ######### # # # ########################### # # ##### ####### # # # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### ### ############### ####### ##### # ########### # ### # # ##### ### # ##### # ##### # ### # ###
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ########### ##### ### ####### # # # ##### # # # # # ### # # ### ### # ### ### # ##### # # # # ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ##### # ### # ### # # ### # # ####### ####### # ### ##### ##### ##### ### ##### # ####### #######
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # # # # # ##### ####### ### # # # ############# # # # # ##### ### ######### # ######### # # ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
### # ##### ##### # ### # ### ####### # ####### # ### ##### # ######### ##### # # ####### ### ##### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # # # # ##### # ##### # ### # ### # ### ##### ### ##### ########### ### # ########### # # #####
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # # # ##### # # # # # # # # ######### ### # ### # # ### # # # ### # # ### # # # # ### # # # # # # #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ### # ### ######### ### ### ######### # ### # # ### # ### # # ##### # # ######### ### ### ##### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ##### ##### # # ####### ### ### # ##### ##### ##### # # ##### ##### ############# # # ### ### ### #
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# ### # # # ### ### # # ### # # ####### ##### # # # # # ### ##### ##### # ### ##### # # ######### ###
|
||||||
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||||
|
# # ##### ### ### # # ### ##### ##### ##### # ##### ### # ### # ### ##### ##### # ##### # # # # # # #
|
||||||
|
# # # # # # # # # # # # # E#
|
||||||
|
#####################################################################################################
|
||||||
51
VasilevIA/lab2/mazes/medium.txt
Normal file
51
VasilevIA/lab2/mazes/medium.txt
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
###################################################
|
||||||
|
#S# # # # # # # #
|
||||||
|
# ##### # # # # # # # ####### # ### # ### ##### # #
|
||||||
|
# # # # # # # # # # # # # # # # # #
|
||||||
|
### # ### # # ####### # ### # # # # ### # ### ### #
|
||||||
|
# # # # # # # # # # # # # #
|
||||||
|
# # # # # # ####### ####### # ### ######### ##### #
|
||||||
|
# # # # # # # # # # # # # #
|
||||||
|
# # # ##### # # # ### ####### ##### ##### # ### ###
|
||||||
|
# # # # # # # # # # # # # # # # #
|
||||||
|
# # ##### ### # ### ### # # ### # ##### # ### # # #
|
||||||
|
# # # # # # # # # # # # # # # # #
|
||||||
|
# # # # # # ### # ### ### ##### # # # ####### # # #
|
||||||
|
# # # # # # # # # # # # # # #
|
||||||
|
# ######### # # # # ### # # # ### # ####### # ### #
|
||||||
|
# # # # # # # # # # # # # # # # # #
|
||||||
|
# # ### # ### # # ### ### ### # ### # # # ### # # #
|
||||||
|
# # # # # # # # # # # # # # #
|
||||||
|
# ### ### # ### ####### ######### # ### ####### ###
|
||||||
|
# # # # # # # # # # # # # # #
|
||||||
|
# # ### ### # ### # ####### # # # # # ####### # # #
|
||||||
|
# # # # # # # # # # # # # #
|
||||||
|
# ####### # ############# ##### # # ####### # ### #
|
||||||
|
# # # # # # # # # # #
|
||||||
|
# # # # # ################# ########### # # ##### #
|
||||||
|
# # # # # # # # # # #
|
||||||
|
# ####### # ### ##### # # ### ### ######### # #####
|
||||||
|
# # # # # # # # # #
|
||||||
|
####### # ####### ##### ### ##### # ############# #
|
||||||
|
# # # # # # # # # # # #
|
||||||
|
##### ####### # # ### # ##### # # ##### ##### # # #
|
||||||
|
# # # # # # # # # # # # # # #
|
||||||
|
## #### # # # ##### ##### # ### ### # # # ##### # #
|
||||||
|
# # # # # # # # # # # # # #
|
||||||
|
# ####### # ### # ### # # # ################# ### #
|
||||||
|
# # # # # # # # # #
|
||||||
|
### ##### ########### # # ############# ### ### ###
|
||||||
|
# # # # # # # # # # # #
|
||||||
|
# ### ### # ####### # # ### # ####### # # ### ### #
|
||||||
|
# # # # # # # # # # # # # # #
|
||||||
|
# # ######### ### # # # # # ### ### ####### # ### #
|
||||||
|
# # # # # # # # # # # # # #
|
||||||
|
# ##### # ##### # ##### ##### ### ####### ##### ###
|
||||||
|
# # # # # # # # # # # #
|
||||||
|
# # # # # # ##### ### ########### # # ####### ### #
|
||||||
|
# # # # # # # # # # # # # # #
|
||||||
|
# # ### ##### # ### ##### ##### ##### # # ### # # #
|
||||||
|
# # # # # # # # # # # # #
|
||||||
|
### # ### ##### ####### ##### ##### ####### # # # #
|
||||||
|
# # # # # #E#
|
||||||
|
###################################################
|
||||||
11
VasilevIA/lab2/mazes/no_exit.txt
Normal file
11
VasilevIA/lab2/mazes/no_exit.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
###########
|
||||||
|
#S# #E#
|
||||||
|
# #########
|
||||||
|
# # #
|
||||||
|
##### # ###
|
||||||
|
# # #
|
||||||
|
# ####### #
|
||||||
|
# # #
|
||||||
|
### ### # #
|
||||||
|
# # #
|
||||||
|
###########
|
||||||
15
VasilevIA/lab2/mazes/sample.txt
Normal file
15
VasilevIA/lab2/mazes/sample.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
###############
|
||||||
|
#S# # #
|
||||||
|
# ### # # ### #
|
||||||
|
# # # # # #
|
||||||
|
### ### ### # #
|
||||||
|
# # # # # #
|
||||||
|
# ### # # ### #
|
||||||
|
# # # # #
|
||||||
|
## ###### # # #
|
||||||
|
# # # #
|
||||||
|
# ### #########
|
||||||
|
# # # #
|
||||||
|
# # ##### # # #
|
||||||
|
# # #E#
|
||||||
|
###############
|
||||||
11
VasilevIA/lab2/mazes/small.txt
Normal file
11
VasilevIA/lab2/mazes/small.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
###########
|
||||||
|
#S # # #
|
||||||
|
##### # #
|
||||||
|
# # # #
|
||||||
|
# ####### #
|
||||||
|
# # # #
|
||||||
|
# ### # # #
|
||||||
|
# # # #
|
||||||
|
### # ### #
|
||||||
|
# # E#
|
||||||
|
###########
|
||||||
BIN
VasilevIA/lab2/results/benchmark_plot.png
Normal file
BIN
VasilevIA/lab2/results/benchmark_plot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
16
VasilevIA/lab2/results/results_maze.csv
Normal file
16
VasilevIA/lab2/results/results_maze.csv
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
maze,strategy,time_ms,visited_cells,path_length
|
||||||
|
small,BFS,0.0676,39.0,33.0
|
||||||
|
small,DFS,0.061,33.0,33.0
|
||||||
|
small,A*,0.1093,35.0,33.0
|
||||||
|
medium,BFS,1.4027,793.0,497.0
|
||||||
|
medium,DFS,0.8985,515.0,497.0
|
||||||
|
medium,A*,2.3001,707.0,497.0
|
||||||
|
large,BFS,6.1605,3533.0,1613.0
|
||||||
|
large,DFS,3.3919,1957.0,1613.0
|
||||||
|
large,A*,11.2172,3379.0,1613.0
|
||||||
|
empty,BFS,1.7583,931.0,67.0
|
||||||
|
empty,DFS,1.0076,451.0,451.0
|
||||||
|
empty,A*,3.4836,931.0,67.0
|
||||||
|
no_exit,BFS,0.067,40.0,0.0
|
||||||
|
no_exit,DFS,0.0599,40.0,0.0
|
||||||
|
no_exit,A*,0.1099,40.0,0.0
|
||||||
|
Loading…
Reference in New Issue
Block a user