[1,2] Первая и вторая лаба #357

Merged
IvanBoy merged 4 commits from vasilevia/2026-rff_mp:develop into develop 2026-05-30 12:01:09 +00:00
14 changed files with 901 additions and 0 deletions
Showing only changes of commit fa4ec42cd2 - Show all commits

View 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
)

View 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}/")

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View 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-уведомления, не меняя код солвера. Слабая связанность: солвер не знает, кто его слушает.
### Диаграмма классов
![Диаграмма классов](mermaid.png)
---
## 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 | - |
### Графики
![Графики](../results/benchmark_plot.png)
---
## 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
View 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()

View File

@ -0,0 +1,21 @@
###################################################
#S #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# E#
###################################################

View File

@ -0,0 +1,101 @@
#####################################################################################################
#S # # # # # # # # # # # # #
### # # ##### # ### # # ##### ### ### ############# # # # # ##### # ######### ### # # ##### ### # ###
# # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ##### ### ### # # ########### ####### # ####### ### ### # # # ####### ##### # # ### # # ##### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ##### # ### ### # ### # ### # # ##### # ##### ####### ### ### # ### ### ### # ### ### # ##### # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # ### # # ### # ##### ##### # ### # # ##### # # ####### ######### ### # # ######### # # # #####
# # # # # # # # # # # # # # # # # # # # # # # # # #
# # ##### ### # ##### # ##### ### # ##### ######### # # ####### ####### # ### # # ########### ##### #
# # # # # # # # # # # # # # # # # # # # # #
##### ##### ########### ### # # ########### # # # # ##### ####### # ####### # ##### ### ### ### ### #
# # # # # # # # # # # # # # # # # # # # # # # #
####### # ### # # ####### ##### ### ##### # # ##### # ##### ### # # # ######### # ### ### ### ### ###
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # ########### # # # ### ### ##### ##### # # ##### # # # ##### # # ##### ##### # ##### ### ### #
# # # # # # # # # # # # # # # # # # # # # # # #
# # ### # ### # ### ### ### ####### # ########### ####### # # ##### # # ### ########### ### ### # # #
# # # # # # # # # # # # # # # # # # # # # # # #
# # ####### # # # # ##### ### # ####### # ##### ##### ##### # # # ### ### # # ##### ##### ### #######
# # # # # # # # # # # # # # # # # # # # # # # # # # #
########### # # ##### # # # ####### # ### # ##### # ##### ### # # # # # ####### # ##### ### # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # ### # # ##### # # # # # ##### # # # ############# # ##### # ############### ### # # ### # #
# # # # # # # # # # # # # # # # # # # # # # #
# # ####### ##### # ########### # # # ######### ### ############# # ####### ####################### #
# # # # # # # # # # # # # # # # # # # # #
# # ### ### # # # # # ### # ##### # ### # ####### # ##### # ######### ####### # ##### # ########### #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ##### ##### ######### ####### ##### # # # # # ### # ##### # ### # ### # ##### # # # # # ### # # ###
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # # ### ##### ### # # ### ##### ##### ####### # # ##### # ### ##### ####### # # # ####### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ### ####### # ### # # ### ### ##### ##### # # # # ### # # # ####### # # ### ##### # # ####### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
##### # # # # # ### # ### ### ### ### # ### # ### ##### # # ########### # # ### # # ##### ##### # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### ##### ### ### # # ### ### # # ##### ### # ##### ########### ### ### ### # # # # ### ######### #
# # # # # # # # # # # # # # # # # # # # # # # # #
# # ### # ### ### # # ####### # # ######### ### # ########### # ### ### ##### ### # ### # # ### ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ######### # ####### ##### ######### # ### ### # ##### # ### ### ### # ### ### # ##### ##### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ### ### # # # # # ##### # # # # ####### # # # ### # ### # ### ### ##### ### # # ### # # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # ### ######### # ##### # ##### # # ############# # # ### ####### # # ##### # # # # # # ### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ##### ####### ### # ### ##### # # # ##### # ####### ### # # ### # # # # ### ### # ########### # ###
# # # # # # # # # # # # # # # # # # # # # # # # # #
# ####### # ##### # ### # ##### # # ##### # ##### # ### ### ######### ##### ### # ##### # ######### #
# # # # # # # # # # # # # # # # # # # # # # # # # #
### # # # ### ####### # # # # ##### # ############### # # ##### ####### ##### # ### # # # # ### # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### ########### ### # ############# # # # ### # ##### ##### ### ####### # ### # # # ##### ### # ###
# # # # # # # # # # # # # # # # # # # # # # # # #
### ##### ##### ### ### ### # # # # ##### # # ####### # # # ####### # # ### # ### # # # # ### ##### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # ### # # # ####### # # # # ### ####### # # # ##### ### # # ##### # ##### # # ### ### # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ### # ### # # # # ### ### ### # # ### ### # ##### ### # # ##### ####### # ##### # ##### ### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # ### ####### ### # # ### ##### # # ######### ### ### # # # # # ### # ### # # ### ### # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # #
### # # ### ### ##### ######### ######### # # # # # ########### ####### # # ### ########### ##### # #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # ### ### ### # ### # # ######### # # # # ##### # # # ##### # ####### ####### # ##### # # ##### #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# # ### ### ##### ####### # # ### ######### ### ### # # ########### # ##### # ### ##### ##### # ### #
# # # # # # # # # # # # # # # # # # # # # # # #
# ####### # # ##### ### # ### # ####### # # # ### ### # # ### # ####### # ##### ### # ######### #####
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # ##### # ####### ####### # ########### ####### ######### # ### # ### # ### # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # ####### # ### ########### # # ######### ### # # # # # # ### # ######### # ### ########### #
# # # # # # # # # # # # # # # # # # # # # # #
### ####### # # # # # ### # # # ######### # # # ########################### # # ##### ####### # # # #
# # # # # # # # # # # # # # # # # # # # # # # # #
# ### ### ############### ####### ##### # ########### # ### # # ##### ### # ##### # ##### # ### # ###
# # # # # # # # # # # # # # # # # # # # # # # # #
# ########### ##### ### ####### # # # ##### # # # # # ### # # ### ### # ### ### # ##### # # # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # ##### # ### # ### # # ### # # ####### ####### # ### ##### ##### ##### ### ##### # ####### #######
# # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # ##### ####### ### # # # ############# # # # # ##### ### ######### # ######### # # ### #
# # # # # # # # # # # # # # # # # # # # # # # # #
### # ##### ##### # ### # ### ####### # ####### # ### ##### # ######### ##### # # ####### ### ##### #
# # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # # # ##### # ##### # ### # ### # ### ##### ### ##### ########### ### # ########### # # #####
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # ##### # # # # # # # # ######### ### # ### # # ### # # # ### # # ### # # # # ### # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # ### # ### ######### ### ### ######### # ### # # ### # ### # # ##### # # ######### ### ### ##### #
# # # # # # # # # # # # # # # # # # # # # # # #
# ##### ##### # # ####### ### ### # ##### ##### ##### # # ##### ##### ############# # # ### ### ### #
# # # # # # # # # # # # # # # # # # # # # # # # # # #
# ### # # # ### ### # # ### # # ####### ##### # # # # # ### ##### ##### # ### ##### # # ######### ###
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # ##### ### ### # # ### ##### ##### ##### # ##### ### # ### # ### ##### ##### # ##### # # # # # # #
# # # # # # # # # # # # # E#
#####################################################################################################

View File

@ -0,0 +1,51 @@
###################################################
#S# # # # # # # #
# ##### # # # # # # # ####### # ### # ### ##### # #
# # # # # # # # # # # # # # # # # #
### # ### # # ####### # ### # # # # ### # ### ### #
# # # # # # # # # # # # # #
# # # # # # ####### ####### # ### ######### ##### #
# # # # # # # # # # # # # #
# # # ##### # # # ### ####### ##### ##### # ### ###
# # # # # # # # # # # # # # # # #
# # ##### ### # ### ### # # ### # ##### # ### # # #
# # # # # # # # # # # # # # # # #
# # # # # # ### # ### ### ##### # # # ####### # # #
# # # # # # # # # # # # # # #
# ######### # # # # ### # # # ### # ####### # ### #
# # # # # # # # # # # # # # # # # #
# # ### # ### # # ### ### ### # ### # # # ### # # #
# # # # # # # # # # # # # # #
# ### ### # ### ####### ######### # ### ####### ###
# # # # # # # # # # # # # # #
# # ### ### # ### # ####### # # # # # ####### # # #
# # # # # # # # # # # # # #
# ####### # ############# ##### # # ####### # ### #
# # # # # # # # # # #
# # # # # ################# ########### # # ##### #
# # # # # # # # # # #
# ####### # ### ##### # # ### ### ######### # #####
# # # # # # # # # #
####### # ####### ##### ### ##### # ############# #
# # # # # # # # # # # #
##### ####### # # ### # ##### # # ##### ##### # # #
# # # # # # # # # # # # # # #
## #### # # # ##### ##### # ### ### # # # ##### # #
# # # # # # # # # # # # # #
# ####### # ### # ### # # # ################# ### #
# # # # # # # # # #
### ##### ########### # # ############# ### ### ###
# # # # # # # # # # # #
# ### ### # ####### # # ### # ####### # # ### ### #
# # # # # # # # # # # # # # #
# # ######### ### # # # # # ### ### ####### # ### #
# # # # # # # # # # # # # #
# ##### # ##### # ##### ##### ### ####### ##### ###
# # # # # # # # # # # #
# # # # # # ##### ### ########### # # ####### ### #
# # # # # # # # # # # # # # #
# # ### ##### # ### ##### ##### ##### # # ### # # #
# # # # # # # # # # # # #
### # ### ##### ####### ##### ##### ####### # # # #
# # # # # #E#
###################################################

View File

@ -0,0 +1,11 @@
###########
#S# #E#
# #########
# # #
##### # ###
# # #
# ####### #
# # #
### ### # #
# # #
###########

View File

@ -0,0 +1,15 @@
###############
#S# # #
# ### # # ### #
# # # # # #
### ### ### # #
# # # # # #
# ### # # ### #
# # # # #
## ###### # # #
# # # #
# ### #########
# # # #
# # ##### # # #
# # #E#
###############

View File

@ -0,0 +1,11 @@
###########
#S # # #
##### # #
# # # #
# ####### #
# # # #
# ### # # #
# # # #
### # ### #
# # E#
###########

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View 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
1 maze strategy time_ms visited_cells path_length
2 small BFS 0.0676 39.0 33.0
3 small DFS 0.061 33.0 33.0
4 small A* 0.1093 35.0 33.0
5 medium BFS 1.4027 793.0 497.0
6 medium DFS 0.8985 515.0 497.0
7 medium A* 2.3001 707.0 497.0
8 large BFS 6.1605 3533.0 1613.0
9 large DFS 3.3919 1957.0 1613.0
10 large A* 11.2172 3379.0 1613.0
11 empty BFS 1.7583 931.0 67.0
12 empty DFS 1.0076 451.0 451.0
13 empty A* 3.4836 931.0 67.0
14 no_exit BFS 0.067 40.0 0.0
15 no_exit DFS 0.0599 40.0 0.0
16 no_exit A* 0.1099 40.0 0.0