diff --git a/svetlakovkyu/02/codes/maze.py b/svetlakovkyu/02/codes/maze.py new file mode 100644 index 0000000..d436474 --- /dev/null +++ b/svetlakovkyu/02/codes/maze.py @@ -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, + ) diff --git a/svetlakovkyu/02/codes/maze_generator.py b/svetlakovkyu/02/codes/maze_generator.py new file mode 100644 index 0000000..4ecfe56 --- /dev/null +++ b/svetlakovkyu/02/codes/maze_generator.py @@ -0,0 +1,79 @@ +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}/") diff --git a/svetlakovkyu/02/docs/benchmark_plot.png b/svetlakovkyu/02/docs/benchmark_plot.png new file mode 100644 index 0000000..d103767 Binary files /dev/null and b/svetlakovkyu/02/docs/benchmark_plot.png differ diff --git a/svetlakovkyu/02/docs/mermaid.png b/svetlakovkyu/02/docs/mermaid.png new file mode 100644 index 0000000..6e835b6 Binary files /dev/null and b/svetlakovkyu/02/docs/mermaid.png differ diff --git a/svetlakovkyu/02/docs/report1.md b/svetlakovkyu/02/docs/report1.md new file mode 100644 index 0000000..4aa142f --- /dev/null +++ b/svetlakovkyu/02/docs/report1.md @@ -0,0 +1,207 @@ +# Отчёт: Поиск выхода из лабиринта + +## 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 пришлось бы напрямую вызывать функции отображения, что создало бы зависимость от конкретной реализации. + +Без применения паттернов код решал бы задачу, но любое изменение требовало бы правки в нескольких местах сразу. С паттернами каждый класс отвечает за одну задачу и не знает о деталях реализации соседних классов. diff --git a/svetlakovkyu/02/main.py b/svetlakovkyu/02/main.py new file mode 100644 index 0000000..8a358bd --- /dev/null +++ b/svetlakovkyu/02/main.py @@ -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() diff --git a/svetlakovkyu/02/mazes/empty.txt b/svetlakovkyu/02/mazes/empty.txt new file mode 100644 index 0000000..8a42a81 --- /dev/null +++ b/svetlakovkyu/02/mazes/empty.txt @@ -0,0 +1,21 @@ +################################################### +#S # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# # +# E# +################################################### diff --git a/svetlakovkyu/02/mazes/large.txt b/svetlakovkyu/02/mazes/large.txt new file mode 100644 index 0000000..6f97148 --- /dev/null +++ b/svetlakovkyu/02/mazes/large.txt @@ -0,0 +1,101 @@ +##################################################################################################### +#S # # # # # # # # # # # # # +### # # ##### # ### # # ##### ### ### ############# # # # # ##### # ######### ### # # ##### ### # ### +# # # # # # # # # # # # # # # # # # # # # # # # # # +# # # ##### ### ### # # ########### ####### # ####### ### ### # # # ####### ##### # # ### # # ##### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # +# ##### # ### ### # ### # ### # # ##### # ##### ####### ### ### # ### ### ### # ### ### # ##### # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # ### # # ### # ##### ##### # ### # # ##### # # ####### ######### ### # # ######### # # # ##### +# # # # # # # # # # # # # # # # # # # # # # # # # # +# # ##### ### # ##### # ##### ### # ##### ######### # # ####### ####### # ### # # ########### ##### # +# # # # # # # # # # # # # # # # # # # # # # +##### ##### ########### ### # # ########### # # # # ##### ####### # ####### # ##### ### ### ### ### # +# # # # # # # # # # # # # # # # # # # # # # # # +####### # ### # # ####### ##### ### ##### # # ##### # ##### ### # # # ######### # ### ### ### ### ### +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### # ########### # # # ### ### ##### ##### # # ##### # # # ##### # # ##### ##### # ##### ### ### # +# # # # # # # # # # # # # # # # # # # # # # # # +# # ### # ### # ### ### ### ####### # ########### ####### # # ##### # # ### ########### ### ### # # # +# # # # # # # # # # # # # # # # # # # # # # # # +# # ####### # # # # ##### ### # ####### # ##### ##### ##### # # # ### ### # # ##### ##### ### ####### +# # # # # # # # # # # # # # # # # # # # # # # # # # # +########### # # ##### # # # ####### # ### # ##### # ##### ### # # # # # ####### # ##### ### # # ### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### # # ### # # ##### # # # # # ##### # # # ############# # ##### # ############### ### # # ### # # +# # # # # # # # # # # # # # # # # # # # # # # +# # ####### ##### # ########### # # # ######### ### ############# # ####### ####################### # +# # # # # # # # # # # # # # # # # # # # # +# # ### ### # # # # # ### # ##### # ### # ####### # ##### # ######### ####### # ##### # ########### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# ##### ##### ######### ####### ##### # # # # # ### # ##### # ### # ### # ##### # # # # # ### # # ### +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### # # # ### ##### ### # # ### ##### ##### ####### # # ##### # ### ##### ####### # # # ####### # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # ### ####### # ### # # ### ### ##### ##### # # # # ### # # # ####### # # ### ##### # # ####### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +##### # # # # # ### # ### ### ### ### # ### # ### ##### # # ########### # # ### # # ##### ##### # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### ##### ### ### # # ### ### # # ##### ### # ##### ########### ### ### ### # # # # ### ######### # +# # # # # # # # # # # # # # # # # # # # # # # # # +# # ### # ### ### # # ####### # # ######### ### # ########### # ### ### ##### ### # ### # # ### ### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # ######### # ####### ##### ######### # ### ### # ##### # ### ### ### # ### ### # ##### ##### # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # ### ### # # # # # ##### # # # # ####### # # # ### # ### # ### ### ##### ### # # ### # # # ### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### # ### ######### # ##### # ##### # # ############# # # ### ####### # # ##### # # # # # # ### # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# ##### ####### ### # ### ##### # # # ##### # ####### ### # # ### # # # # ### ### # ########### # ### +# # # # # # # # # # # # # # # # # # # # # # # # # # +# ####### # ##### # ### # ##### # # ##### # ##### # ### ### ######### ##### ### # ##### # ######### # +# # # # # # # # # # # # # # # # # # # # # # # # # # +### # # # ### ####### # # # # ##### # ############### # # ##### ####### ##### # ### # # # # ### # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### ########### ### # ############# # # # ### # ##### ##### ### ####### # ### # # # ##### ### # ### +# # # # # # # # # # # # # # # # # # # # # # # # # +### ##### ##### ### ### ### # # # # ##### # # ####### # # # ####### # # ### # ### # # # # ### ##### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### # ### # # # ####### # # # # ### ####### # # # ##### ### # # ##### # ##### # # ### ### # # ### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # ### # ### # # # # ### ### ### # # ### ### # ##### ### # # ##### ####### # ##### # ##### ### # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### # # ### ####### ### # # ### ##### # # ######### ### ### # # # # # ### # ### # # ### ### # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # +### # # ### ### ##### ######### ######### # # # # # ########### ####### # # ### ########### ##### # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # ### ### ### # ### # # ######### # # # # ##### # # # ##### # ####### ####### # ##### # # ##### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# # ### ### ##### ####### # # ### ######### ### ### # # ########### # ##### # ### ##### ##### # ### # +# # # # # # # # # # # # # # # # # # # # # # # # +# ####### # # ##### ### # ### # ####### # # # ### ### # # ### # ####### # ##### ### # ######### ##### +# # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # # # # # ##### # ####### ####### # ########### ####### ######### # ### # ### # ### # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### # # ####### # ### ########### # # ######### ### # # # # # # ### # ######### # ### ########### # +# # # # # # # # # # # # # # # # # # # # # # # +### ####### # # # # # ### # # # ######### # # # ########################### # # ##### ####### # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # +# ### ### ############### ####### ##### # ########### # ### # # ##### ### # ##### # ##### # ### # ### +# # # # # # # # # # # # # # # # # # # # # # # # # +# ########### ##### ### ####### # # # ##### # # # # # ### # # ### ### # ### ### # ##### # # # # ### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # ##### # ### # ### # # ### # # ####### ####### # ### ##### ##### ##### ### ##### # ####### ####### +# # # # # # # # # # # # # # # # # # # # # # # # +# # # # # # # ##### ####### ### # # # ############# # # # # ##### ### ######### # ######### # # ### # +# # # # # # # # # # # # # # # # # # # # # # # # # +### # ##### ##### # ### # ### ####### # ####### # ### ##### # ######### ##### # # ####### ### ##### # +# # # # # # # # # # # # # # # # # # # # # # # # # +# ### # # # # ##### # ##### # ### # ### # ### ##### ### ##### ########### ### # ########### # # ##### +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # # # ##### # # # # # # # # ######### ### # ### # # ### # # # ### # # ### # # # # ### # # # # # # # +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # ### # ### ######### ### ### ######### # ### # # ### # ### # # ##### # # ######### ### ### ##### # +# # # # # # # # # # # # # # # # # # # # # # # # +# ##### ##### # # ####### ### ### # ##### ##### ##### # # ##### ##### ############# # # ### ### ### # +# # # # # # # # # # # # # # # # # # # # # # # # # # # +# ### # # # ### ### # # ### # # ####### ##### # # # # # ### ##### ##### # ### ##### # # ######### ### +# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +# # ##### ### ### # # ### ##### ##### ##### # ##### ### # ### # ### ##### ##### # ##### # # # # # # # +# # # # # # # # # # # # # E# +##################################################################################################### diff --git a/svetlakovkyu/02/mazes/medium.txt b/svetlakovkyu/02/mazes/medium.txt new file mode 100644 index 0000000..677cd38 --- /dev/null +++ b/svetlakovkyu/02/mazes/medium.txt @@ -0,0 +1,51 @@ +################################################### +#S# # # # # # # # +# ##### # # # # # # # ####### # ### # ### ##### # # +# # # # # # # # # # # # # # # # # # +### # ### # # ####### # ### # # # # ### # ### ### # +# # # # # # # # # # # # # # +# # # # # # ####### ####### # ### ######### ##### # +# # # # # # # # # # # # # # +# # # ##### # # # ### ####### ##### ##### # ### ### +# # # # # # # # # # # # # # # # # +# # ##### ### # ### ### # # ### # ##### # ### # # # +# # # # # # # # # # # # # # # # # +# # # # # # ### # ### ### ##### # # # ####### # # # +# # # # # # # # # # # # # # # +# ######### # # # # ### # # # ### # ####### # ### # +# # # # # # # # # # # # # # # # # # +# # ### # ### # # ### ### ### # ### # # # ### # # # +# # # # # # # # # # # # # # # +# ### ### # ### ####### ######### # ### ####### ### +# # # # # # # # # # # # # # # +# # ### ### # ### # ####### # # # # # ####### # # # +# # # # # # # # # # # # # # +# ####### # ############# ##### # # ####### # ### # +# # # # # # # # # # # +# # # # # ################# ########### # # ##### # +# # # # # # # # # # # +# ####### # ### ##### # # ### ### ######### # ##### +# # # # # # # # # # +####### # ####### ##### ### ##### # ############# # +# # # # # # # # # # # # +##### ####### # # ### # ##### # # ##### ##### # # # +# # # # # # # # # # # # # # # +# ##### # # # ##### ##### # ### ### # # # ##### # # +# # # # # # # # # # # # # # +# ####### # ### # ### # # # ################# ### # +# # # # # # # # # # +### ##### ########### # # ############# ### ### ### +# # # # # # # # # # # # +# ### ### # ####### # # ### # ####### # # ### ### # +# # # # # # # # # # # # # # # +# # ######### ### # # # # # ### ### ####### # ### # +# # # # # # # # # # # # # # +# ##### # ##### # ##### ##### ### ####### ##### ### +# # # # # # # # # # # # +# # # # # # ##### ### ########### # # ####### ### # +# # # # # # # # # # # # # # # +# # ### ##### # ### ##### ##### ##### # # ### # # # +# # # # # # # # # # # # # +### # ### ##### ####### ##### ##### ####### # # # # +# # # # # #E# +################################################### diff --git a/svetlakovkyu/02/mazes/no_exit.txt b/svetlakovkyu/02/mazes/no_exit.txt new file mode 100644 index 0000000..09c1301 --- /dev/null +++ b/svetlakovkyu/02/mazes/no_exit.txt @@ -0,0 +1,11 @@ +########### +#S# #E# +# ######### +# # # +##### # ### +# # # +# ####### # +# # # +### ### # # +# ### +########### diff --git a/svetlakovkyu/02/mazes/sample.txt b/svetlakovkyu/02/mazes/sample.txt new file mode 100644 index 0000000..119bed9 --- /dev/null +++ b/svetlakovkyu/02/mazes/sample.txt @@ -0,0 +1,15 @@ +############### +#S# # # +# ### # # ### # +# # # # # # +### ### ### # # +# # # # # # +# ### # # ### # +# # # # # +### ##### # # # +# # # # +# ### ######### +# # # # +# # ##### # # # +# # #E# +############### diff --git a/svetlakovkyu/02/mazes/small.txt b/svetlakovkyu/02/mazes/small.txt new file mode 100644 index 0000000..2fbbeb4 --- /dev/null +++ b/svetlakovkyu/02/mazes/small.txt @@ -0,0 +1,11 @@ +########### +#S # # +##### # # # +# # # # +# ####### # +# # # # +# ### # # # +# # # # +### # ### # +# # E# +########### diff --git a/svetlakovkyu/02/results/benchmark_plot.png b/svetlakovkyu/02/results/benchmark_plot.png new file mode 100644 index 0000000..d103767 Binary files /dev/null and b/svetlakovkyu/02/results/benchmark_plot.png differ diff --git a/svetlakovkyu/02/results/results_maze.csv b/svetlakovkyu/02/results/results_maze.csv new file mode 100644 index 0000000..5806c75 --- /dev/null +++ b/svetlakovkyu/02/results/results_maze.csv @@ -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.8986,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