Compare commits
No commits in common. "develop" and "develop" have entirely different histories.
5
.idea/.gitignore
vendored
5
.idea/.gitignore
vendored
|
|
@ -1,5 +0,0 @@
|
||||||
# Игнорируемые файлы по умолчанию
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# HTTP-клиент на основе редактора
|
|
||||||
/httpRequests/
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="jdk" jdkName="Python 3.14" jdkType="Python SDK" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Black">
|
|
||||||
<option name="sdkName" value="Python 3.14" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.14" project-jdk-type="Python SDK" />
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/2026-rff_mp.iml" filepath="$PROJECT_DIR$/.idea/2026-rff_mp.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<settings>
|
|
||||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
|
||||||
<version value="1.0" />
|
|
||||||
</settings>
|
|
||||||
</component>
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="PYTHON_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
<component name="PyDocumentationSettings">
|
|
||||||
<option name="format" value="PLAIN" />
|
|
||||||
<option name="myDocStringFormat" value="Plain" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (maze_project_submission) (2)" project-jdk-type="Python SDK" />
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/maze_project_submission.iml" filepath="$PROJECT_DIR$/.idea/maze_project_submission.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AutoImportSettings">
|
|
||||||
<option name="autoReloadType" value="SELECTIVE" />
|
|
||||||
</component>
|
|
||||||
<component name="ChangeListManager">
|
|
||||||
<list default="true" id="a6ff989d-c5f6-4522-8b0a-933849f2d044" name="Changes" comment="" />
|
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
||||||
</component>
|
|
||||||
<component name="MarkdownSettingsMigration">
|
|
||||||
<option name="stateVersion" value="1" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectColorInfo"><![CDATA[{
|
|
||||||
"associatedIndex": 2
|
|
||||||
}]]></component>
|
|
||||||
<component name="ProjectId" id="3EB20Mq0B865MSq8Kkl2evaRIZW" />
|
|
||||||
<component name="ProjectViewState">
|
|
||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
|
||||||
<option name="showLibraryContents" value="true" />
|
|
||||||
</component>
|
|
||||||
<component name="PropertiesComponent"><![CDATA[{
|
|
||||||
"keyToString": {
|
|
||||||
"RunOnceActivity.OpenProjectViewOnStart": "true",
|
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
||||||
"last_opened_file_path": "C:/Users/vaz21/Downloads/Task 2 GLOBAL/maze_project_submission"
|
|
||||||
}
|
|
||||||
}]]></component>
|
|
||||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
|
||||||
<component name="TaskManager">
|
|
||||||
<task active="true" id="Default" summary="Default task">
|
|
||||||
<changelist id="a6ff989d-c5f6-4522-8b0a-933849f2d044" name="Changes" comment="" />
|
|
||||||
<created>1779637417749</created>
|
|
||||||
<option name="number" value="Default" />
|
|
||||||
<option name="presentableId" value="Default" />
|
|
||||||
<updated>1779637417749</updated>
|
|
||||||
</task>
|
|
||||||
<servers />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
# Maze Solver Project
|
|
||||||
|
|
||||||
ООП-проект для поиска выхода из лабиринта с паттернами:
|
|
||||||
- Builder
|
|
||||||
- Strategy
|
|
||||||
- Observer
|
|
||||||
- Command
|
|
||||||
|
|
||||||
## Запуск
|
|
||||||
```bash
|
|
||||||
python main.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Эксперименты
|
|
||||||
```bash
|
|
||||||
python experiment.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Результаты сохраняются в папку `experiment_results/`.
|
|
||||||
|
|
||||||
## Требования
|
|
||||||
```bash
|
|
||||||
pip install -r requirements.txt
|
|
||||||
```
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
|
|
||||||
class MazeBuilder(ABC):
|
|
||||||
@abstractmethod
|
|
||||||
def buildFromFile(self, filename):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
from core.cell import Cell
|
|
||||||
from core.maze import Maze
|
|
||||||
from builders.maze_builder import MazeBuilder
|
|
||||||
|
|
||||||
|
|
||||||
class TextFileMazeBuilder(MazeBuilder):
|
|
||||||
def buildFromFile(self, filename):
|
|
||||||
with open(filename, "r", encoding="utf-8") as f:
|
|
||||||
lines = [line.rstrip("\n") for line in f]
|
|
||||||
|
|
||||||
if not lines:
|
|
||||||
raise ValueError("Maze file is empty")
|
|
||||||
|
|
||||||
width = max(len(line) for line in lines)
|
|
||||||
height = len(lines)
|
|
||||||
|
|
||||||
cells = []
|
|
||||||
startCell = None
|
|
||||||
exitCell = None
|
|
||||||
|
|
||||||
for y, line in enumerate(lines):
|
|
||||||
row = []
|
|
||||||
for x in range(width):
|
|
||||||
ch = line[x] if x < len(line) else "#"
|
|
||||||
|
|
||||||
if ch == "#":
|
|
||||||
cell = Cell(x, y, isWall=True)
|
|
||||||
elif ch == "S":
|
|
||||||
if startCell is not None:
|
|
||||||
raise ValueError("Multiple start cells found")
|
|
||||||
cell = Cell(x, y, isWall=False, isStart=True)
|
|
||||||
startCell = cell
|
|
||||||
elif ch == "E":
|
|
||||||
if exitCell is not None:
|
|
||||||
raise ValueError("Multiple exit cells found")
|
|
||||||
cell = Cell(x, y, isWall=False, isExit=True)
|
|
||||||
exitCell = cell
|
|
||||||
elif ch in (" ", "."):
|
|
||||||
cell = Cell(x, y, isWall=False)
|
|
||||||
elif ch.isdigit():
|
|
||||||
cell = Cell(x, y, isWall=False, weight=max(1, int(ch)))
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Unsupported symbol '{ch}' at ({x}, {y})")
|
|
||||||
row.append(cell)
|
|
||||||
cells.append(row)
|
|
||||||
|
|
||||||
if startCell is None:
|
|
||||||
raise ValueError("Start cell 'S' not found")
|
|
||||||
if exitCell is None:
|
|
||||||
raise ValueError("Exit cell 'E' not found")
|
|
||||||
|
|
||||||
return Maze(cells, width, height, startCell, exitCell)
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
from abc import ABC, abstractmethod
|
|
||||||
|
|
||||||
|
|
||||||
class Command(ABC):
|
|
||||||
@abstractmethod
|
|
||||||
def execute(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def undo(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
from commands.command import Command
|
|
||||||
|
|
||||||
|
|
||||||
class MoveCommand(Command):
|
|
||||||
DIRECTION_TO_DELTA = {
|
|
||||||
"W": (0, -1),
|
|
||||||
"A": (-1, 0),
|
|
||||||
"S": (0, 1),
|
|
||||||
"D": (1, 0),
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, player, maze, direction):
|
|
||||||
self.player = player
|
|
||||||
self.maze = maze
|
|
||||||
self.direction = direction.upper()
|
|
||||||
self.previousCell = None
|
|
||||||
|
|
||||||
def execute(self):
|
|
||||||
if self.direction not in self.DIRECTION_TO_DELTA:
|
|
||||||
return False
|
|
||||||
|
|
||||||
dx, dy = self.DIRECTION_TO_DELTA[self.direction]
|
|
||||||
current = self.player.currentCell
|
|
||||||
new_cell = self.maze.getCell(current.x + dx, current.y + dy)
|
|
||||||
|
|
||||||
if new_cell is None or not new_cell.isPassable():
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.previousCell = current
|
|
||||||
self.player.setCell(new_cell)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def undo(self):
|
|
||||||
if self.previousCell is None:
|
|
||||||
return False
|
|
||||||
self.player.setCell(self.previousCell)
|
|
||||||
return True
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
from commands.move_command import MoveCommand
|
|
||||||
|
|
||||||
|
|
||||||
class GameController:
|
|
||||||
def __init__(self, maze, player, view):
|
|
||||||
self.maze = maze
|
|
||||||
self.player = player
|
|
||||||
self.view = view
|
|
||||||
self.history = []
|
|
||||||
|
|
||||||
def move(self, direction):
|
|
||||||
command = MoveCommand(self.player, self.maze, direction)
|
|
||||||
if command.execute():
|
|
||||||
self.history.append(command)
|
|
||||||
self.view.update({"type": "move", "direction": direction})
|
|
||||||
self.view.render(self.maze, player_position=self.player.currentCell)
|
|
||||||
return True
|
|
||||||
print("Cannot move there")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def undo(self):
|
|
||||||
if not self.history:
|
|
||||||
print("Nothing to undo")
|
|
||||||
return False
|
|
||||||
command = self.history.pop()
|
|
||||||
if command.undo():
|
|
||||||
self.view.update({"type": "undo"})
|
|
||||||
self.view.render(self.maze, player_position=self.player.currentCell)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Cell:
|
|
||||||
x: int
|
|
||||||
y: int
|
|
||||||
isWall: bool = False
|
|
||||||
isStart: bool = False
|
|
||||||
isExit: bool = False
|
|
||||||
weight: int = 1
|
|
||||||
|
|
||||||
def isPassable(self):
|
|
||||||
return not self.isWall
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
parts = [f"Cell({self.x}, {self.y}"]
|
|
||||||
if self.isWall:
|
|
||||||
parts.append("WALL")
|
|
||||||
if self.isStart:
|
|
||||||
parts.append("START")
|
|
||||||
if self.isExit:
|
|
||||||
parts.append("EXIT")
|
|
||||||
if self.weight != 1:
|
|
||||||
parts.append(f"w={self.weight}")
|
|
||||||
return ", ".join(parts) + ")"
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
class Maze:
|
|
||||||
def __init__(self, cells, width, height, startCell=None, exitCell=None):
|
|
||||||
self.cells = cells
|
|
||||||
self.width = width
|
|
||||||
self.height = height
|
|
||||||
self.startCell = startCell
|
|
||||||
self.exitCell = exitCell
|
|
||||||
|
|
||||||
def getCell(self, x, y):
|
|
||||||
if 0 <= x < self.width and 0 <= y < self.height:
|
|
||||||
return self.cells[y][x]
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getNeighbors(self, cell):
|
|
||||||
neighbors = []
|
|
||||||
for dx, dy in ((0, -1), (0, 1), (-1, 0), (1, 0)):
|
|
||||||
nx, ny = cell.x + dx, cell.y + dy
|
|
||||||
neighbor = self.getCell(nx, ny)
|
|
||||||
if neighbor is not None and neighbor.isPassable():
|
|
||||||
neighbors.append(neighbor)
|
|
||||||
return neighbors
|
|
||||||
|
|
||||||
def render_lines(self, player_position=None, path=None):
|
|
||||||
path_set = {(c.x, c.y) for c in path} if path else set()
|
|
||||||
player_pos = None if player_position is None else (player_position.x, player_position.y)
|
|
||||||
lines = []
|
|
||||||
for y in range(self.height):
|
|
||||||
row = []
|
|
||||||
for x in range(self.width):
|
|
||||||
cell = self.cells[y][x]
|
|
||||||
if player_pos == (x, y):
|
|
||||||
row.append("P")
|
|
||||||
elif cell.isStart:
|
|
||||||
row.append("S")
|
|
||||||
elif cell.isExit:
|
|
||||||
row.append("E")
|
|
||||||
elif cell.isWall:
|
|
||||||
row.append("#")
|
|
||||||
elif (x, y) in path_set:
|
|
||||||
row.append("*")
|
|
||||||
elif cell.weight > 1:
|
|
||||||
row.append(str(cell.weight))
|
|
||||||
else:
|
|
||||||
row.append(" ")
|
|
||||||
lines.append("".join(row))
|
|
||||||
return lines
|
|
||||||
|
|
||||||
def render(self, player_position=None, path=None):
|
|
||||||
return "\n".join(self.render_lines(player_position=player_position, path=path))
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
class Player:
|
|
||||||
def __init__(self, currentCell):
|
|
||||||
self.currentCell = currentCell
|
|
||||||
|
|
||||||
def setCell(self, cell):
|
|
||||||
self.currentCell = cell
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
from dataclasses import dataclass, field
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class SearchStats:
|
|
||||||
timeMs: float
|
|
||||||
visitedCells: int
|
|
||||||
pathLength: int
|
|
||||||
path: list = field(default_factory=list)
|
|
||||||
found: bool = False
|
|
||||||
algorithm: str = ""
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Place report files and experiment outputs here.
|
|
||||||
|
|
@ -1,249 +0,0 @@
|
||||||
# Отчёт по работе «Поиск выхода из лабиринта»
|
|
||||||
|
|
||||||
## 1. Цель работы
|
|
||||||
Разработать гибкую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В работе использованы паттерны проектирования, чтобы отделить логику представления лабиринта, его загрузки, поиска пути и вывода результатов.
|
|
||||||
|
|
||||||
## 2. Описание задачи
|
|
||||||
Лабиринт задаётся в текстовом файле символами:
|
|
||||||
- `#` — стена;
|
|
||||||
- пробел — проход;
|
|
||||||
- `S` — старт;
|
|
||||||
- `E` — выход.
|
|
||||||
|
|
||||||
Программа должна:
|
|
||||||
- загружать лабиринт;
|
|
||||||
- строить его внутреннюю модель;
|
|
||||||
- искать путь разными алгоритмами;
|
|
||||||
- собирать статистику поиска;
|
|
||||||
- визуализировать результат в консоли;
|
|
||||||
- сравнивать стратегии на разных типах лабиринтов.
|
|
||||||
|
|
||||||
## 3. Выбранные паттерны проектирования
|
|
||||||
|
|
||||||
### 3.1 Builder
|
|
||||||
Паттерн Builder используется для загрузки лабиринта из файла. Он скрывает детали парсинга и валидации, а клиент получает готовый объект `Maze`.
|
|
||||||
|
|
||||||
Преимущества:
|
|
||||||
- легко добавить новый формат загрузки;
|
|
||||||
- клиентский код не зависит от формата файла;
|
|
||||||
- создание лабиринта можно расширять без переписывания остальной программы.
|
|
||||||
|
|
||||||
### 3.2 Strategy
|
|
||||||
Паттерн Strategy используется для выбора алгоритма поиска пути. В программе реализованы `BFS`, `DFS`, `A*`, а при необходимости можно добавить Дейкстру или любую другую стратегию.
|
|
||||||
|
|
||||||
Преимущества:
|
|
||||||
- алгоритм можно менять во время выполнения;
|
|
||||||
- код оркестратора не зависит от конкретного метода поиска;
|
|
||||||
- новые алгоритмы добавляются без изменения существующего кода.
|
|
||||||
|
|
||||||
### 3.3 Observer
|
|
||||||
Паттерн Observer используется для обновления консольного интерфейса при изменении состояния программы: загрузка лабиринта, поиск пути, движение игрока.
|
|
||||||
|
|
||||||
Преимущества:
|
|
||||||
- вывод отделён от логики;
|
|
||||||
- можно заменить консольный интерфейс на графический без изменения поискового кода;
|
|
||||||
- упрощается расширение визуализации.
|
|
||||||
|
|
||||||
### 3.4 Command
|
|
||||||
Паттерн Command используется для пошагового перемещения игрока и отмены последнего хода.
|
|
||||||
|
|
||||||
Преимущества:
|
|
||||||
- каждое действие оформляется как отдельный объект;
|
|
||||||
- легко реализовать undo;
|
|
||||||
- история ходов хранится отдельно от логики перемещения.
|
|
||||||
|
|
||||||
## 4. Диаграмма классов
|
|
||||||
Ниже приведена упрощённая диаграмма классов в формате Mermaid:
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
classDiagram
|
|
||||||
class Cell {
|
|
||||||
+int x
|
|
||||||
+int y
|
|
||||||
+bool isWall
|
|
||||||
+bool isStart
|
|
||||||
+bool isExit
|
|
||||||
+isPassable()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Maze {
|
|
||||||
+cells
|
|
||||||
+width
|
|
||||||
+height
|
|
||||||
+startCell
|
|
||||||
+exitCell
|
|
||||||
+getCell(x, y)
|
|
||||||
+getNeighbors(cell)
|
|
||||||
}
|
|
||||||
|
|
||||||
class MazeBuilder {
|
|
||||||
<<interface>>
|
|
||||||
+buildFromFile(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
class TextFileMazeBuilder {
|
|
||||||
+buildFromFile(filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
class PathFindingStrategy {
|
|
||||||
<<interface>>
|
|
||||||
+findPath(maze, start, exitCell)
|
|
||||||
}
|
|
||||||
|
|
||||||
class BFSStrategy {
|
|
||||||
+findPath(maze, start, exitCell)
|
|
||||||
}
|
|
||||||
|
|
||||||
class DFSStrategy {
|
|
||||||
+findPath(maze, start, exitCell)
|
|
||||||
}
|
|
||||||
|
|
||||||
class AStarStrategy {
|
|
||||||
+findPath(maze, start, exitCell)
|
|
||||||
}
|
|
||||||
|
|
||||||
class SearchStats {
|
|
||||||
+timeMs
|
|
||||||
+visitedCells
|
|
||||||
+pathLength
|
|
||||||
+path
|
|
||||||
}
|
|
||||||
|
|
||||||
class MazeSolver {
|
|
||||||
+maze
|
|
||||||
+strategy
|
|
||||||
+setStrategy(strategy)
|
|
||||||
+solve()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Observer {
|
|
||||||
<<interface>>
|
|
||||||
+update(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConsoleView {
|
|
||||||
+update(event)
|
|
||||||
+render(maze, player_position, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Command {
|
|
||||||
<<interface>>
|
|
||||||
+execute()
|
|
||||||
+undo()
|
|
||||||
}
|
|
||||||
|
|
||||||
class MoveCommand {
|
|
||||||
+execute()
|
|
||||||
+undo()
|
|
||||||
}
|
|
||||||
|
|
||||||
class Player {
|
|
||||||
+currentCell
|
|
||||||
+setCell(cell)
|
|
||||||
}
|
|
||||||
|
|
||||||
Maze <|-- TextFileMazeBuilder : creates
|
|
||||||
MazeBuilder <|.. TextFileMazeBuilder
|
|
||||||
PathFindingStrategy <|.. BFSStrategy
|
|
||||||
PathFindingStrategy <|.. DFSStrategy
|
|
||||||
PathFindingStrategy <|.. AStarStrategy
|
|
||||||
MazeSolver --> Maze
|
|
||||||
MazeSolver --> PathFindingStrategy
|
|
||||||
MazeSolver --> SearchStats
|
|
||||||
Observer <|.. ConsoleView
|
|
||||||
Command <|.. MoveCommand
|
|
||||||
MoveCommand --> Player
|
|
||||||
MoveCommand --> Maze
|
|
||||||
ConsoleView --> Maze
|
|
||||||
Maze --> Cell
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. Ключевые классы и их роль
|
|
||||||
|
|
||||||
### Cell
|
|
||||||
Хранит координаты клетки и её тип. Позволяет быстро проверять, является ли клетка проходимой.
|
|
||||||
|
|
||||||
### Maze
|
|
||||||
Содержит двумерную карту клеток, размер лабиринта, а также ссылки на старт и выход. Даёт доступ к соседним клеткам по четырём направлениям.
|
|
||||||
|
|
||||||
### TextFileMazeBuilder
|
|
||||||
Читает текстовый файл, создаёт объекты `Cell`, определяет старт и выход, затем возвращает готовый `Maze`.
|
|
||||||
|
|
||||||
### BFSStrategy
|
|
||||||
Ищет кратчайший путь по числу шагов. Подходит для случая, когда все переходы одинаковой стоимости.
|
|
||||||
|
|
||||||
### DFSStrategy
|
|
||||||
Быстро исследует пространство, но не гарантирует кратчайший путь. Полезен как сравнительный алгоритм.
|
|
||||||
|
|
||||||
### AStarStrategy
|
|
||||||
Использует эвристику Манхэттенского расстояния. Обычно посещает меньше клеток, чем BFS, если эвристика удачно направляет поиск к цели.
|
|
||||||
|
|
||||||
### MazeSolver
|
|
||||||
Оркестратор, который хранит лабиринт и текущую стратегию. Вызывает поиск, измеряет время и собирает статистику.
|
|
||||||
|
|
||||||
### SearchStats
|
|
||||||
Содержит итог поиска: время выполнения, количество посещённых клеток и длину пути.
|
|
||||||
|
|
||||||
### ConsoleView
|
|
||||||
Реализует наблюдателя и умеет выводить лабиринт и найденный путь в консоль.
|
|
||||||
|
|
||||||
### MoveCommand
|
|
||||||
Оформляет ход игрока как объект-команду. Поддерживает отмену последнего перемещения.
|
|
||||||
|
|
||||||
## 6. Экспериментальная часть
|
|
||||||
|
|
||||||
### 6.1 Подготовка тестовых лабиринтов
|
|
||||||
Для сравнения стратегий использовались следующие типы лабиринтов:
|
|
||||||
- маленький 10×10 с простым путём;
|
|
||||||
- средний 50×50 с тупиками;
|
|
||||||
- большой 100×100 со сложной структурой;
|
|
||||||
- пустой лабиринт без стен;
|
|
||||||
- лабиринт без выхода.
|
|
||||||
|
|
||||||
### 6.2 Методика измерений
|
|
||||||
Для каждой стратегии и каждого лабиринта поиск запускался несколько раз, после чего вычислялись средние значения:
|
|
||||||
- время поиска в миллисекундах;
|
|
||||||
- количество посещённых клеток;
|
|
||||||
- длина найденного пути.
|
|
||||||
|
|
||||||
Результаты сохранялись в CSV-файл в двух вариантах:
|
|
||||||
- сырой набор измерений;
|
|
||||||
- усреднённая таблица.
|
|
||||||
|
|
||||||
## 7. Анализ эффективности
|
|
||||||
|
|
||||||
### BFS
|
|
||||||
BFS гарантирует кратчайший путь по числу шагов, если все переходы имеют одинаковую стоимость. На простых и пустых лабиринтах работает стабильно и предсказуемо. Минус — может посещать много клеток, особенно на больших лабиринтах.
|
|
||||||
|
|
||||||
### DFS
|
|
||||||
DFS может быстро найти какой-то путь, но он не обязательно будет кратчайшим. На сложных лабиринтах иногда работает быстро, но на других может уйти далеко от цели и пройти лишние области.
|
|
||||||
|
|
||||||
### A*
|
|
||||||
A* использует эвристику и обычно показывает хороший баланс между скоростью и качеством пути. На больших и запутанных лабиринтах часто посещает меньше клеток, чем BFS, потому что поиск направлен в сторону выхода.
|
|
||||||
|
|
||||||
### Лабиринт без пути
|
|
||||||
Если пути нет, все алгоритмы вынуждены исследовать доступную область. В этом случае длина пути равна 0, а различия между алгоритмами проявляются в количестве просмотренных клеток и времени выполнения.
|
|
||||||
|
|
||||||
### Вывод по выбору алгоритма
|
|
||||||
- BFS стоит выбирать, когда нужен гарантированно кратчайший путь и веса переходов одинаковы.
|
|
||||||
- DFS полезен как простой и быстрый по реализации вариант, но без гарантии оптимальности.
|
|
||||||
- A* подходит для практических задач, где нужно ускорить поиск и сократить число посещённых клеток.
|
|
||||||
- При взвешенных переходах лучше использовать Дейкстру или взвешенный A*.
|
|
||||||
|
|
||||||
## 8. Роль ООП и паттернов
|
|
||||||
ООП и паттерны сделали код более гибким и расширяемым. Благодаря этому:
|
|
||||||
- можно заменить алгоритм поиска без переписывания логики программы;
|
|
||||||
- можно добавить новый формат загрузки лабиринта;
|
|
||||||
- можно поменять способ визуализации;
|
|
||||||
- можно расширить управление игроком и добавить отмену действий.
|
|
||||||
|
|
||||||
Без паттернов пришлось бы связывать загрузку, поиск, отображение и управление в один большой блок кода. Это усложнило бы отладку и дальнейшие изменения.
|
|
||||||
|
|
||||||
## 9. Вывод
|
|
||||||
В ходе работы была создана расширяемая программа для поиска пути в лабиринте. Использование паттернов Builder, Strategy, Observer и Command позволило разделить обязанности между классами, упростить поддержку кода и сделать архитектуру удобной для дальнейшего развития. Эксперименты показали, что выбор алгоритма сильно зависит от типа лабиринта: BFS даёт кратчайший путь, DFS иногда быстрее в реализации, а A* чаще всего наиболее практичен на больших картах.
|
|
||||||
|
|
||||||
## 10. Приложения
|
|
||||||
- Листинги ключевых классов.
|
|
||||||
- CSV-файлы с результатами экспериментов.
|
|
||||||
- Графики сравнений.
|
|
||||||
- Файлы с тестовыми лабиринтами.
|
|
||||||
|
|
@ -1,225 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
from statistics import mean
|
|
||||||
import csv
|
|
||||||
import random
|
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
from core.cell import Cell
|
|
||||||
from core.maze import Maze
|
|
||||||
from solver.maze_solver import MazeSolver
|
|
||||||
from strategies.astar_strategy import AStarStrategy
|
|
||||||
from strategies.bfs_strategy import BFSStrategy
|
|
||||||
from strategies.dfs_strategy import DFSStrategy
|
|
||||||
from strategies.dijkstra_strategy import DijkstraStrategy
|
|
||||||
|
|
||||||
|
|
||||||
BASE_DIR = Path(__file__).resolve().parent
|
|
||||||
OUT_DIR = BASE_DIR / "experiment_results"
|
|
||||||
|
|
||||||
|
|
||||||
def build_maze_from_symbols(lines):
|
|
||||||
height = len(lines)
|
|
||||||
width = max(len(line) for line in lines)
|
|
||||||
cells = []
|
|
||||||
start = None
|
|
||||||
exit_cell = None
|
|
||||||
for y, line in enumerate(lines):
|
|
||||||
row = []
|
|
||||||
for x in range(width):
|
|
||||||
ch = line[x] if x < len(line) else "#"
|
|
||||||
if ch == "#":
|
|
||||||
cell = Cell(x, y, isWall=True)
|
|
||||||
elif ch == "S":
|
|
||||||
cell = Cell(x, y, isWall=False, isStart=True)
|
|
||||||
start = cell
|
|
||||||
elif ch == "E":
|
|
||||||
cell = Cell(x, y, isWall=False, isExit=True)
|
|
||||||
exit_cell = cell
|
|
||||||
elif ch == " " or ch == ".":
|
|
||||||
cell = Cell(x, y, isWall=False)
|
|
||||||
elif ch.isdigit():
|
|
||||||
cell = Cell(x, y, isWall=False, weight=int(ch))
|
|
||||||
else:
|
|
||||||
raise ValueError(f"Unknown symbol '{ch}' at {x},{y}")
|
|
||||||
row.append(cell)
|
|
||||||
cells.append(row)
|
|
||||||
return Maze(cells, width, height, start, exit_cell)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_empty_maze(width, height):
|
|
||||||
lines = [" " * width for _ in range(height)]
|
|
||||||
lines = [list(row) for row in lines]
|
|
||||||
lines[1][1] = "S"
|
|
||||||
lines[height - 2][width - 2] = "E"
|
|
||||||
return build_maze_from_symbols(["".join(row) for row in lines])
|
|
||||||
|
|
||||||
|
|
||||||
def generate_simple_maze(width, height):
|
|
||||||
grid = [["#" for _ in range(width)] for _ in range(height)]
|
|
||||||
for x in range(1, width - 1):
|
|
||||||
grid[1][x] = " "
|
|
||||||
for y in range(1, height - 1):
|
|
||||||
grid[y][width - 2] = " "
|
|
||||||
grid[1][1] = "S"
|
|
||||||
grid[height - 2][width - 2] = "E"
|
|
||||||
return build_maze_from_symbols(["".join(row) for row in grid])
|
|
||||||
|
|
||||||
|
|
||||||
def generate_branching_maze(width, height, seed=42, wall_density=0.30):
|
|
||||||
rng = random.Random(seed)
|
|
||||||
grid = [["#" for _ in range(width)] for _ in range(height)]
|
|
||||||
x, y = 1, 1
|
|
||||||
grid[y][x] = "S"
|
|
||||||
while (x, y) != (width - 2, height - 2):
|
|
||||||
candidates = []
|
|
||||||
for dx, dy in [(1, 0), (0, 1)]:
|
|
||||||
nx, ny = x + dx, y + dy
|
|
||||||
if 1 <= nx < width - 1 and 1 <= ny < height - 1:
|
|
||||||
candidates.append((nx, ny))
|
|
||||||
if not candidates:
|
|
||||||
break
|
|
||||||
x, y = rng.choice(candidates)
|
|
||||||
grid[y][x] = " "
|
|
||||||
grid[height - 2][width - 2] = "E"
|
|
||||||
|
|
||||||
# carve extra corridors and dead ends
|
|
||||||
for yy in range(1, height - 1):
|
|
||||||
for xx in range(1, width - 1):
|
|
||||||
if grid[yy][xx] == "#" and rng.random() > wall_density:
|
|
||||||
grid[yy][xx] = " "
|
|
||||||
grid[1][1] = "S"
|
|
||||||
grid[height - 2][width - 2] = "E"
|
|
||||||
return build_maze_from_symbols(["".join(row) for row in grid])
|
|
||||||
|
|
||||||
|
|
||||||
def generate_no_path_maze(width, height):
|
|
||||||
grid = [[" " for _ in range(width)] for _ in range(height)]
|
|
||||||
for x in range(width):
|
|
||||||
grid[height // 2][x] = "#"
|
|
||||||
grid[1][1] = "S"
|
|
||||||
grid[height - 2][width - 2] = "E"
|
|
||||||
return build_maze_from_symbols(["".join(row) for row in grid])
|
|
||||||
|
|
||||||
|
|
||||||
def generate_weighted_maze(width, height, seed=123):
|
|
||||||
rng = random.Random(seed)
|
|
||||||
grid = [[" " for _ in range(width)] for _ in range(height)]
|
|
||||||
for y in range(height):
|
|
||||||
for x in range(width):
|
|
||||||
r = rng.random()
|
|
||||||
if r < 0.12:
|
|
||||||
grid[y][x] = "#"
|
|
||||||
elif r < 0.25:
|
|
||||||
grid[y][x] = "3"
|
|
||||||
elif r < 0.40:
|
|
||||||
grid[y][x] = "2"
|
|
||||||
else:
|
|
||||||
grid[y][x] = "1"
|
|
||||||
# ensure path-ish
|
|
||||||
for x in range(width):
|
|
||||||
grid[1][x] = "1"
|
|
||||||
for y in range(1, height):
|
|
||||||
grid[y][width - 2] = "1"
|
|
||||||
grid[1][1] = "S"
|
|
||||||
grid[height - 2][width - 2] = "E"
|
|
||||||
return build_maze_from_symbols(["".join(row) for row in grid])
|
|
||||||
|
|
||||||
|
|
||||||
def bench_one_maze(maze_name, maze, strategies, repeats=5):
|
|
||||||
summary_rows = []
|
|
||||||
raw_rows = []
|
|
||||||
for strategy_name, strategy_factory in strategies:
|
|
||||||
times, visiteds, lengths = [], [], []
|
|
||||||
for run in range(1, repeats + 1):
|
|
||||||
solver = MazeSolver(maze)
|
|
||||||
solver.setStrategy(strategy_factory())
|
|
||||||
stats = solver.solve()
|
|
||||||
raw_rows.append([maze_name, strategy_name, run, f"{stats.timeMs:.6f}", stats.visitedCells, stats.pathLength])
|
|
||||||
times.append(stats.timeMs)
|
|
||||||
visiteds.append(stats.visitedCells)
|
|
||||||
lengths.append(stats.pathLength)
|
|
||||||
summary_rows.append([maze_name, strategy_name, f"{mean(times):.6f}", f"{mean(visiteds):.2f}", f"{mean(lengths):.2f}", repeats])
|
|
||||||
return summary_rows, raw_rows
|
|
||||||
|
|
||||||
|
|
||||||
def save_csv(path, rows):
|
|
||||||
with open(path, "w", newline="", encoding="utf-8") as f:
|
|
||||||
csv.writer(f).writerows(rows)
|
|
||||||
|
|
||||||
|
|
||||||
def plot_summary(summary_rows):
|
|
||||||
by_maze = {}
|
|
||||||
for row in summary_rows[1:]:
|
|
||||||
maze_name, strategy, avg_time, avg_visited, avg_len, runs = row
|
|
||||||
by_maze.setdefault(maze_name, []).append((strategy, float(avg_time), float(avg_visited), float(avg_len)))
|
|
||||||
|
|
||||||
for maze_name, items in by_maze.items():
|
|
||||||
items.sort(key=lambda t: t[0])
|
|
||||||
strategies = [i[0] for i in items]
|
|
||||||
x = list(range(len(strategies)))
|
|
||||||
|
|
||||||
plt.figure(figsize=(8, 4))
|
|
||||||
plt.bar(x, [i[1] for i in items])
|
|
||||||
plt.xticks(x, strategies)
|
|
||||||
plt.ylabel("ms")
|
|
||||||
plt.title(f"{maze_name} — avg time")
|
|
||||||
plt.tight_layout()
|
|
||||||
plt.savefig(OUT_DIR / f"{maze_name}_time.png", dpi=150)
|
|
||||||
plt.close()
|
|
||||||
|
|
||||||
plt.figure(figsize=(8, 4))
|
|
||||||
plt.bar(x, [i[2] for i in items])
|
|
||||||
plt.xticks(x, strategies)
|
|
||||||
plt.ylabel("cells")
|
|
||||||
plt.title(f"{maze_name} — visited cells")
|
|
||||||
plt.tight_layout()
|
|
||||||
plt.savefig(OUT_DIR / f"{maze_name}_visited.png", dpi=150)
|
|
||||||
plt.close()
|
|
||||||
|
|
||||||
plt.figure(figsize=(8, 4))
|
|
||||||
plt.bar(x, [i[3] for i in items])
|
|
||||||
plt.xticks(x, strategies)
|
|
||||||
plt.ylabel("cells")
|
|
||||||
plt.title(f"{maze_name} — path length")
|
|
||||||
plt.tight_layout()
|
|
||||||
plt.savefig(OUT_DIR / f"{maze_name}_length.png", dpi=150)
|
|
||||||
plt.close()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
OUT_DIR.mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
strategies = [
|
|
||||||
("BFS", BFSStrategy),
|
|
||||||
("DFS", DFSStrategy),
|
|
||||||
("A*", AStarStrategy),
|
|
||||||
("Dijkstra", DijkstraStrategy),
|
|
||||||
]
|
|
||||||
|
|
||||||
mazes = [
|
|
||||||
("small_10x10", generate_simple_maze(10, 10)),
|
|
||||||
("medium_50x50", generate_branching_maze(50, 50)),
|
|
||||||
("large_100x100", generate_branching_maze(100, 100, seed=99, wall_density=0.35)),
|
|
||||||
("empty_30x30", generate_empty_maze(30, 30)),
|
|
||||||
("no_path_30x30", generate_no_path_maze(30, 30)),
|
|
||||||
("weighted_30x30", generate_weighted_maze(30, 30)),
|
|
||||||
]
|
|
||||||
|
|
||||||
summary = [["maze", "strategy", "avg_time_ms", "avg_visited_cells", "avg_path_length", "runs"]]
|
|
||||||
raw = [["maze", "strategy", "run", "time_ms", "visited_cells", "path_length"]]
|
|
||||||
|
|
||||||
for maze_name, maze in mazes:
|
|
||||||
s_rows, r_rows = bench_one_maze(maze_name, maze, strategies, repeats=5)
|
|
||||||
summary.extend(s_rows)
|
|
||||||
raw.extend(r_rows)
|
|
||||||
|
|
||||||
save_csv(OUT_DIR / "summary.csv", summary)
|
|
||||||
save_csv(OUT_DIR / "raw.csv", raw)
|
|
||||||
plot_summary(summary)
|
|
||||||
|
|
||||||
print("Saved to", OUT_DIR.resolve())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,121 +0,0 @@
|
||||||
maze,strategy,run,time_ms,visited_cells,path_length
|
|
||||||
small_10x10,BFS,1,0.044300,15,15
|
|
||||||
small_10x10,BFS,2,0.022800,15,15
|
|
||||||
small_10x10,BFS,3,0.020400,15,15
|
|
||||||
small_10x10,BFS,4,0.020300,15,15
|
|
||||||
small_10x10,BFS,5,0.018700,15,15
|
|
||||||
small_10x10,DFS,1,0.031200,15,15
|
|
||||||
small_10x10,DFS,2,0.022000,15,15
|
|
||||||
small_10x10,DFS,3,0.021200,15,15
|
|
||||||
small_10x10,DFS,4,0.020800,15,15
|
|
||||||
small_10x10,DFS,5,0.020500,15,15
|
|
||||||
small_10x10,A*,1,0.048900,15,15
|
|
||||||
small_10x10,A*,2,0.034700,15,15
|
|
||||||
small_10x10,A*,3,0.029400,15,15
|
|
||||||
small_10x10,A*,4,0.029100,15,15
|
|
||||||
small_10x10,A*,5,0.029300,15,15
|
|
||||||
small_10x10,Dijkstra,1,0.037900,15,15
|
|
||||||
small_10x10,Dijkstra,2,0.028500,15,15
|
|
||||||
small_10x10,Dijkstra,3,0.026800,15,15
|
|
||||||
small_10x10,Dijkstra,4,0.026400,15,15
|
|
||||||
small_10x10,Dijkstra,5,0.026700,15,15
|
|
||||||
medium_50x50,BFS,1,2.105800,1579,95
|
|
||||||
medium_50x50,BFS,2,1.928700,1579,95
|
|
||||||
medium_50x50,BFS,3,1.969500,1579,95
|
|
||||||
medium_50x50,BFS,4,1.938800,1579,95
|
|
||||||
medium_50x50,BFS,5,1.943600,1579,95
|
|
||||||
medium_50x50,DFS,1,1.927300,1277,647
|
|
||||||
medium_50x50,DFS,2,1.856300,1277,647
|
|
||||||
medium_50x50,DFS,3,1.890100,1277,647
|
|
||||||
medium_50x50,DFS,4,1.868000,1277,647
|
|
||||||
medium_50x50,DFS,5,1.865500,1277,647
|
|
||||||
medium_50x50,A*,1,2.359000,927,95
|
|
||||||
medium_50x50,A*,2,2.193700,927,95
|
|
||||||
medium_50x50,A*,3,2.178400,927,95
|
|
||||||
medium_50x50,A*,4,2.181800,927,95
|
|
||||||
medium_50x50,A*,5,2.174500,927,95
|
|
||||||
medium_50x50,Dijkstra,1,3.534700,1579,95
|
|
||||||
medium_50x50,Dijkstra,2,3.435500,1579,95
|
|
||||||
medium_50x50,Dijkstra,3,3.457600,1579,95
|
|
||||||
medium_50x50,Dijkstra,4,3.417300,1579,95
|
|
||||||
medium_50x50,Dijkstra,5,3.538000,1579,95
|
|
||||||
large_100x100,BFS,1,8.624100,5566,195
|
|
||||||
large_100x100,BFS,2,7.706900,5566,195
|
|
||||||
large_100x100,BFS,3,9.723300,5566,195
|
|
||||||
large_100x100,BFS,4,7.585700,5566,195
|
|
||||||
large_100x100,BFS,5,8.031300,5566,195
|
|
||||||
large_100x100,DFS,1,5.512400,3543,1531
|
|
||||||
large_100x100,DFS,2,5.329300,3543,1531
|
|
||||||
large_100x100,DFS,3,5.223300,3543,1531
|
|
||||||
large_100x100,DFS,4,5.729900,3543,1531
|
|
||||||
large_100x100,DFS,5,5.497400,3543,1531
|
|
||||||
large_100x100,A*,1,2.101500,853,195
|
|
||||||
large_100x100,A*,2,2.264500,853,195
|
|
||||||
large_100x100,A*,3,2.064100,853,195
|
|
||||||
large_100x100,A*,4,2.031700,853,195
|
|
||||||
large_100x100,A*,5,2.046500,853,195
|
|
||||||
large_100x100,Dijkstra,1,25.021300,5571,195
|
|
||||||
large_100x100,Dijkstra,2,13.541100,5571,195
|
|
||||||
large_100x100,Dijkstra,3,12.884100,5571,195
|
|
||||||
large_100x100,Dijkstra,4,13.481800,5571,195
|
|
||||||
large_100x100,Dijkstra,5,12.748000,5571,195
|
|
||||||
empty_30x30,BFS,1,1.234300,896,55
|
|
||||||
empty_30x30,BFS,2,1.163400,896,55
|
|
||||||
empty_30x30,BFS,3,1.145700,896,55
|
|
||||||
empty_30x30,BFS,4,1.177300,896,55
|
|
||||||
empty_30x30,BFS,5,1.175100,896,55
|
|
||||||
empty_30x30,DFS,1,1.338000,842,815
|
|
||||||
empty_30x30,DFS,2,1.296500,842,815
|
|
||||||
empty_30x30,DFS,3,1.296700,842,815
|
|
||||||
empty_30x30,DFS,4,1.280100,842,815
|
|
||||||
empty_30x30,DFS,5,1.290800,842,815
|
|
||||||
empty_30x30,A*,1,2.183400,784,55
|
|
||||||
empty_30x30,A*,2,2.522900,784,55
|
|
||||||
empty_30x30,A*,3,1.985000,784,55
|
|
||||||
empty_30x30,A*,4,1.972100,784,55
|
|
||||||
empty_30x30,A*,5,2.088600,784,55
|
|
||||||
empty_30x30,Dijkstra,1,2.080400,896,55
|
|
||||||
empty_30x30,Dijkstra,2,2.100100,896,55
|
|
||||||
empty_30x30,Dijkstra,3,2.130700,896,55
|
|
||||||
empty_30x30,Dijkstra,4,2.073600,896,55
|
|
||||||
empty_30x30,Dijkstra,5,2.095900,896,55
|
|
||||||
no_path_30x30,BFS,1,0.645900,450,0
|
|
||||||
no_path_30x30,BFS,2,0.566600,450,0
|
|
||||||
no_path_30x30,BFS,3,0.566000,450,0
|
|
||||||
no_path_30x30,BFS,4,0.583500,450,0
|
|
||||||
no_path_30x30,BFS,5,0.568900,450,0
|
|
||||||
no_path_30x30,DFS,1,0.692100,450,0
|
|
||||||
no_path_30x30,DFS,2,0.676900,450,0
|
|
||||||
no_path_30x30,DFS,3,0.703500,450,0
|
|
||||||
no_path_30x30,DFS,4,0.722300,450,0
|
|
||||||
no_path_30x30,DFS,5,0.672000,450,0
|
|
||||||
no_path_30x30,A*,1,1.112700,450,0
|
|
||||||
no_path_30x30,A*,2,1.130000,450,0
|
|
||||||
no_path_30x30,A*,3,1.096100,450,0
|
|
||||||
no_path_30x30,A*,4,1.111400,450,0
|
|
||||||
no_path_30x30,A*,5,1.183500,450,0
|
|
||||||
no_path_30x30,Dijkstra,1,1.023300,450,0
|
|
||||||
no_path_30x30,Dijkstra,2,1.011700,450,0
|
|
||||||
no_path_30x30,Dijkstra,3,1.127200,450,0
|
|
||||||
no_path_30x30,Dijkstra,4,1.110200,450,0
|
|
||||||
no_path_30x30,Dijkstra,5,1.043900,450,0
|
|
||||||
weighted_30x30,BFS,1,1.074700,788,55
|
|
||||||
weighted_30x30,BFS,2,0.997700,788,55
|
|
||||||
weighted_30x30,BFS,3,0.992700,788,55
|
|
||||||
weighted_30x30,BFS,4,1.010800,788,55
|
|
||||||
weighted_30x30,BFS,5,1.035000,788,55
|
|
||||||
weighted_30x30,DFS,1,1.130200,693,479
|
|
||||||
weighted_30x30,DFS,2,1.057400,693,479
|
|
||||||
weighted_30x30,DFS,3,1.049900,693,479
|
|
||||||
weighted_30x30,DFS,4,1.051600,693,479
|
|
||||||
weighted_30x30,DFS,5,1.059100,693,479
|
|
||||||
weighted_30x30,A*,1,0.402200,126,55
|
|
||||||
weighted_30x30,A*,2,0.384100,126,55
|
|
||||||
weighted_30x30,A*,3,0.360000,126,55
|
|
||||||
weighted_30x30,A*,4,0.360700,126,55
|
|
||||||
weighted_30x30,A*,5,0.353500,126,55
|
|
||||||
weighted_30x30,Dijkstra,1,1.834900,781,55
|
|
||||||
weighted_30x30,Dijkstra,2,1.759000,781,55
|
|
||||||
weighted_30x30,Dijkstra,3,1.786300,781,55
|
|
||||||
weighted_30x30,Dijkstra,4,1.740500,781,55
|
|
||||||
weighted_30x30,Dijkstra,5,1.807100,781,55
|
|
||||||
|
Gitea Version: 1.22.0 |
