Merge pull request '[2] exit_from_maze' (#352) from pomelovsd/2026-rff_mp:exit_from_maze into develop
Reviewed-on: #352
This commit is contained in:
commit
a4d9a226c1
File diff suppressed because one or more lines are too long
28
pomelovsd/ExitMaze/Builder/Builder.py
Normal file
28
pomelovsd/ExitMaze/Builder/Builder.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
from Core.Cell import Cell
|
||||
from Core.Maze import Maze
|
||||
from Builder.BuilderInterface import MazeBuilders
|
||||
|
||||
class TextFileMazeBuilder(MazeBuilders):
|
||||
|
||||
def build_from_file(self, filename):
|
||||
grid = []
|
||||
start = None
|
||||
exit = None
|
||||
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
lines = [line.rstrip("\n") for line in f]
|
||||
|
||||
for y, line in enumerate(lines):
|
||||
row = []
|
||||
for x, ch in enumerate(line):
|
||||
cell = Cell(x, y, isWall = (ch == "#"), isStart = (ch == "S"), isExit = (ch == "E"))
|
||||
if (ch == "S"):
|
||||
start = cell
|
||||
if (ch == "E"):
|
||||
exit = cell
|
||||
|
||||
row.append(cell)
|
||||
|
||||
grid.append(row)
|
||||
|
||||
return Maze(grid, start, exit)
|
||||
6
pomelovsd/ExitMaze/Builder/BuilderInterface.py
Normal file
6
pomelovsd/ExitMaze/Builder/BuilderInterface.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
class MazeBuilders(ABC):
|
||||
@abstractmethod
|
||||
def build_from_file(self, filename):
|
||||
pass
|
||||
0
pomelovsd/ExitMaze/Builder/__init__.py
Normal file
0
pomelovsd/ExitMaze/Builder/__init__.py
Normal file
20
pomelovsd/ExitMaze/Core/Benchmark.py
Normal file
20
pomelovsd/ExitMaze/Core/Benchmark.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from MazeSolver.Solver import MazeSolver
|
||||
import csv
|
||||
|
||||
def RunBenchmark(maze, strategies, repeats = 5):
|
||||
rows = []
|
||||
|
||||
for name, strategy in strategies.items():
|
||||
|
||||
solver = MazeSolver(maze, strategy)
|
||||
|
||||
for _ in range(repeats):
|
||||
stats, _ = solver.solve()
|
||||
|
||||
rows.append([name, stats.time_ms, stats.visited_cells, stats.path_length])
|
||||
|
||||
with open("results.csv", "w", newline="") as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(["strategy", "time_ms", "visited", "path_length"])
|
||||
writer.writerows(rows)
|
||||
|
||||
12
pomelovsd/ExitMaze/Core/Cell.py
Normal file
12
pomelovsd/ExitMaze/Core/Cell.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
class Cell:
|
||||
def __init__(self, x = 0, y = 0, isWall = False, isStart = False, isExit = False):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.isWall = isWall
|
||||
self.isStart = isStart
|
||||
self.isExit = isExit
|
||||
|
||||
# Возращает True, если посаседству стена
|
||||
def isPassable(self):
|
||||
return not self.isWall
|
||||
|
||||
22
pomelovsd/ExitMaze/Core/Maze.py
Normal file
22
pomelovsd/ExitMaze/Core/Maze.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
class Maze:
|
||||
def __init__(self, grid, start=None, exit=None):
|
||||
self.grid = grid
|
||||
self.start = start
|
||||
self.exit = exit
|
||||
self.height = len(grid)
|
||||
self.width = len(grid[0]) if grid else 0
|
||||
|
||||
def getCell(self, x, y):
|
||||
if 0 <= x < self.width and 0 <= y < self.height:
|
||||
return self.grid[y][x]
|
||||
return None
|
||||
|
||||
def getNeighbors(self, cell):
|
||||
directions = [(0,1),(1,0),(0,-1),(-1,0)]
|
||||
result = []
|
||||
for dx, dy in directions:
|
||||
nx, ny = cell.x + dx, cell.y + dy
|
||||
neighbor = self.getCell(nx, ny)
|
||||
if neighbor and neighbor.isPassable():
|
||||
result.append(neighbor)
|
||||
return result
|
||||
0
pomelovsd/ExitMaze/Core/__init__.py
Normal file
0
pomelovsd/ExitMaze/Core/__init__.py
Normal file
5
pomelovsd/ExitMaze/MazeSolver/SearchStats.py
Normal file
5
pomelovsd/ExitMaze/MazeSolver/SearchStats.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class SearchStats:
|
||||
def __init__(self, time_ms, visited_cells, path_length):
|
||||
self.time_ms = time_ms
|
||||
self.visited_cells = visited_cells
|
||||
self.path_length = path_length
|
||||
20
pomelovsd/ExitMaze/MazeSolver/Solver.py
Normal file
20
pomelovsd/ExitMaze/MazeSolver/Solver.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from .SearchStats import SearchStats
|
||||
import time
|
||||
|
||||
class MazeSolver:
|
||||
|
||||
def __init__(self, maze, strategy):
|
||||
self.maze = maze
|
||||
self.strategy = strategy
|
||||
|
||||
def setStrategy(self, strategy):
|
||||
self.strategy = strategy
|
||||
|
||||
def solve(self):
|
||||
start = time.perf_counter()
|
||||
|
||||
path, visited = self.strategy.findPath(self.maze, self.maze.start, self.maze.exit)
|
||||
|
||||
end = time.perf_counter()
|
||||
|
||||
return SearchStats((end-start)*1000, visited, len(path)), path
|
||||
0
pomelovsd/ExitMaze/MazeSolver/__init__.py
Normal file
0
pomelovsd/ExitMaze/MazeSolver/__init__.py
Normal file
10
pomelovsd/ExitMaze/Mazes/empty.txt
Normal file
10
pomelovsd/ExitMaze/Mazes/empty.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
##########
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##########
|
||||
103
pomelovsd/ExitMaze/Mazes/large.txt
Normal file
103
pomelovsd/ExitMaze/Mazes/large.txt
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#######################################################################################################
|
||||
#S # # # # # # # # # # # # # #
|
||||
# ### ##### # ##### # # ### ### ####### ####### # # ### ### ### # ### ### ### ### # ### # # # #########
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ### ##### # ##### ### # # ##### ####### ### ### # # # ####### # ### # # ### # ### ### ##### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # # # # ### ### # ### ##### # ### # # # # # ####### # # ##### ### # ### ### # ##### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ####### # ######### ##### # # ### # ### ### ### # ####### # # ### # ##### ##### ########### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ### # ##### # ####### ### # # # ##### # ##### ##### # ######### # # # # # # ### ### # ### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ####### # ##### # ### ##### ##### # ####### ######### # ##### # # # # # ### # ### # # # ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ######### ##### # # # # # # ##### ##### ### # # # ### ####### ########### # # # # ### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
####### # ####### ### ##### # # # # # # # ######### ##### # # # # ####### # ### ### # ### ########### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ####### # # # ##### ### # ### # ### ### # ### ####### ### # # ### ##### ### ### ######### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # ##### # ### ####### # ####### ##### # # # # # # # # # # ######### # ########### # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ####### ### ####### ### # ##### # ##### ### # # # # # # # # ### # # ####### ### ### # ### ### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### ### ##### ####### # # ### ### ##### ### ############# ### ### ### ####### ##### ####### # # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ######### ##### # # ### ### ### ####### ##### # ### ### ### # # # # # # # ##### # # # # # # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### ### ### ### ##### # # # # ####### # ##### ############# ### ### ### # # # # # ######### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### ### # ### ##### ### # ##### # ####### # ### ### ##### ### # ######### # ### # ### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ### ######### # ##### # ### ### ### # # ##### # # # ### # # ####### # # ### ##### ### # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ### ##### # ########### ##### ####### ########### # ### ######### # ### # # # # ### # # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ##### # # ### # # ##### ### # ### # ##### # # # ##### ### # # # ##### ####### ##### ### # ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ############# ##### # # ### ### ### ### ### # # ####### # ### ### ##### ### ### ### ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### # # # # # # # ### ### # ### # ##### # ### ### ### ##### # ### ### ### ### ### ##### # ### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ### ### ### # ### ### ##### ### # # # # ### # ##### ##### # # # # ### ######### ### # # # ##### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ### # ########### # # ### ######### # # ### # # # ##### # ### ##### # # ### ##### ##### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### ### ### # # # ####### ############# ##### # ### ############### ##### # ##### ##### # # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ####### # ##### ### # # # # # # # # ##### # ##### ### ##### # # ##### # ### # # ### # # ### #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ##### # ### ### ### ##### # # # ### # ### ### ##### # ### ### ##### # # ####### # ### ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ####### # ### # # # ### ####### # ################# # # ##### # # ### # # ### # # # ##### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ##### # ### ### ### # # ########### # # # ##### ##### ##### ### ############# # # ### ### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### ### ##### ### ### # ############# ### # ##### # # # ######### ### ### ### ### # ##### ### #######
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
################# ### # ### # # ### # ####### ### ### ### ### # ####### # # # ### ##### # # ######### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
####### ##### ##### # ### ### ######### ### # # ######### ### ### ### ##### # ### # # # ### # ##### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ##### ##### ### # ### # # # # ### ### # # ##### ##### ### # # ##### # # # ######### # ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ####### ### # ##### ####### # ####### ####### # # ######### ### ####### ##### ### # ######### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ##### ##### # ##### ### # ### # ########### # ### # ### # ######### # ### # ##### ### ### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
### ### ####### # # # ##### ##### ########### # # # # ####### # # # # # ########### # # # ######### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # ### # # ### # ##### ### # ### ### ##### ### # ### ##### # ### # # ############### ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ##### # # # ##### # ######### # # ####### ##### ### # ### ### # # # ### # ####### ### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### ##### # ### # ### # ##### ##### ##### ##### ##### # # ########### ##### ############# ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
### ### # # ############### # # ### # # ####### ### ### # ### ### # # ##### # # # ####### ####### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### ##### # ### # # # ##### ### # ### # ### # ########### ### # # ##### # ##### ####### # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
########### # ######### # ### # ### ### ### # ### ####################### # ### # ####### # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ####### # ### ### ### ### ########### # ##### # ##### ### ### # ### # ##### # ########### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ### # ####### # # # ### ### # # # ### # # # ### # ##### # # ########### ######### # ### ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ####### # ### # # # ### # ### ### ### ######### # ### # ### # ### # # ### ##### ####### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ##### ##### # # # # # # ##### # ### # ### ####### ####### ##### ### # ### ##### # # ### # # #######
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # ####### # ####### # ##### # ### # ### # # ####### ##### # ##### ##### # ### # # ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ######### # ##### # ### ##### ### # ##### ##### ### # ##### ### # # ############# # # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # # ### # # ### ### ##### ### # # # ### ### # # # # # ### # ### # # # # # # ##### # ### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### ### # ### # ### # ### # # ####### # ##### # ### ### ##### # # # # ### # ### ### # ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ##### ### # # # ##### ############### ### # ### # # ### ##### ### # ##### ### ### ### ### # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ### ####### ### ### ##### # # # ##### ##### ######### ### ##### # ####### # # ### ### # ### #####
|
||||
# # # # # # # # # # # # # # # # E#
|
||||
#######################################################################################################
|
||||
53
pomelovsd/ExitMaze/Mazes/medium.txt
Normal file
53
pomelovsd/ExitMaze/Mazes/medium.txt
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#####################################################
|
||||
#S # # # # # # # # #
|
||||
# ### # ### ########### # ### ### # ### # ###########
|
||||
# # # # # # # # # #
|
||||
### ### # # ### ### ##### # ##### ### ### ##### ### #
|
||||
# # # # # # # # # # # # #
|
||||
# ######### ##### # # ### ####### # ####### # ##### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ### # ##### # # ### # ### # # ##### ##### # ##### #
|
||||
# # # # # # # # # # # # # # #
|
||||
### ### # # ### # ### ### # ### # # # ##### # # # ###
|
||||
# # # # # # # # # # # # # # #
|
||||
# ##### ####### ### ### ##### ### # ### # ### ### ###
|
||||
# # # # # # # # # # # # # #
|
||||
# ### ####### ### ### # ##### ######### ### ##### # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # ### ### # # # ####### # # # ######### ### ### #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
### ####### # # ### # # ### ### # ### # ##### # # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# ####### # # # ### # # # # # ### # # ### # # ### # #
|
||||
# # # # # # # # # # # # # # #
|
||||
### ### ### ##### ### ### ### ### ##### ### ####### #
|
||||
# # # # # # # # # # #
|
||||
##### # # # ### # # ####### ### # ##### ### # ### ###
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# ####### ### ### # ########### # # ##### # ### ### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # ### ### ### ### ### ### # # ### # # ### ##### # #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# ### # ##### ### ##### ##### # # ### # ### ### ### #
|
||||
# # # # # # # # # # # # #
|
||||
# ##### ######### ### # ### # ### # ### ####### #####
|
||||
# # # # # # # #
|
||||
### # # ##### ##### ########### # ### ### # ### #####
|
||||
# # # # # # # # # # # # # #
|
||||
# # # ### ##### ##### # ############# ##### # ##### #
|
||||
# # # # # # # # # # #
|
||||
# ### # # # ### ### ### ##### # # ##### ##### #######
|
||||
# # # # # # # # # # # # #
|
||||
### # ##### ### # ####### ####### # # # # ##### # ###
|
||||
# # # # # # # # # # # # # # # # #
|
||||
### ### ### ####### ####### # ### # ### # # ### ### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # # # ### # # # # ##### ### ####### # # # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### ### # ##### # ### # # ### # ##### # # ### #
|
||||
# # # # # # # # # # # #
|
||||
### # # # # # # ##### # ### ### ##### ### # ##### ###
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # # ### ### ####### # ### # ### # ### ### ### ### #
|
||||
# # # # # # # # # #E#
|
||||
#####################################################
|
||||
10
pomelovsd/ExitMaze/Mazes/no_exit.txt
Normal file
10
pomelovsd/ExitMaze/Mazes/no_exit.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
##########
|
||||
#S #
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# #
|
||||
##########
|
||||
10
pomelovsd/ExitMaze/Mazes/small.txt
Normal file
10
pomelovsd/ExitMaze/Mazes/small.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
##########
|
||||
#S #
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# # #
|
||||
# E#
|
||||
##########
|
||||
6
pomelovsd/ExitMaze/Observer_Command/Command.py
Normal file
6
pomelovsd/ExitMaze/Observer_Command/Command.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class Command:
|
||||
def execute(self):
|
||||
pass
|
||||
def undo(self):
|
||||
pass
|
||||
|
||||
31
pomelovsd/ExitMaze/Observer_Command/ConsoleView.py
Normal file
31
pomelovsd/ExitMaze/Observer_Command/ConsoleView.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from .Observer import Observer
|
||||
|
||||
class ConsoleView(Observer):
|
||||
|
||||
def update(self, event):
|
||||
print("[событие]", event)
|
||||
|
||||
def render(self, maze, path = None):
|
||||
path = set(path or [])
|
||||
|
||||
for row in maze.grid:
|
||||
line = ""
|
||||
|
||||
for cell in row:
|
||||
|
||||
if cell in path:
|
||||
line += "*"
|
||||
|
||||
elif cell.is_wall:
|
||||
line += "#"
|
||||
|
||||
elif cell.is_start:
|
||||
line += "S"
|
||||
|
||||
elif cell.is_exit:
|
||||
line += "E"
|
||||
|
||||
else:
|
||||
line += " "
|
||||
|
||||
print(line)
|
||||
15
pomelovsd/ExitMaze/Observer_Command/MoveCommand.py
Normal file
15
pomelovsd/ExitMaze/Observer_Command/MoveCommand.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
from .Command import Command
|
||||
|
||||
class MoveCommand(Command):
|
||||
|
||||
def __init__(self, player, target):
|
||||
self.player = player
|
||||
self.target = target
|
||||
self.previous = None
|
||||
|
||||
def execute(self):
|
||||
self.previous = self.player.current
|
||||
self.player.current = self.target
|
||||
|
||||
def undo(self):
|
||||
self.player.current = self.previous
|
||||
3
pomelovsd/ExitMaze/Observer_Command/Observer.py
Normal file
3
pomelovsd/ExitMaze/Observer_Command/Observer.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
class Observer:
|
||||
def update(self, event):
|
||||
pass
|
||||
0
pomelovsd/ExitMaze/Observer_Command/__init__.py
Normal file
0
pomelovsd/ExitMaze/Observer_Command/__init__.py
Normal file
41
pomelovsd/ExitMaze/Strategies/AStar.py
Normal file
41
pomelovsd/ExitMaze/Strategies/AStar.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from Strategies.strat import PathFindingStrategy
|
||||
from Strategies.path import restore
|
||||
import heapq
|
||||
|
||||
class AStar(PathFindingStrategy):
|
||||
|
||||
def heuristic(self, a, b):
|
||||
return abs(a.x - b.x) + abs(a.y - b.y)
|
||||
|
||||
def findPath(self, maze, start, exit):
|
||||
if exit is None:
|
||||
return [], 0
|
||||
|
||||
heap = []
|
||||
counter = 0
|
||||
heapq.heappush(heap, (0, counter, start))
|
||||
counter += 1
|
||||
|
||||
parent = {}
|
||||
g = {start: 0}
|
||||
visited = set()
|
||||
|
||||
while heap:
|
||||
_, _, current = heapq.heappop(heap)
|
||||
|
||||
if current == exit:
|
||||
break
|
||||
|
||||
visited.add(current)
|
||||
|
||||
for n in maze.getNeighbors(current):
|
||||
tentative = g[current] + 1
|
||||
|
||||
if n not in g or tentative < g[n]:
|
||||
g[n] = tentative
|
||||
priority = tentative + self.heuristic(n, exit)
|
||||
heapq.heappush(heap, (priority, counter, n))
|
||||
counter += 1
|
||||
parent[n] = current
|
||||
|
||||
return restore(parent, start, exit), len(visited)
|
||||
26
pomelovsd/ExitMaze/Strategies/BFS.py
Normal file
26
pomelovsd/ExitMaze/Strategies/BFS.py
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
from Strategies.strat import PathFindingStrategy
|
||||
from Strategies.path import restore
|
||||
from collections import deque
|
||||
|
||||
class BFS(PathFindingStrategy):
|
||||
def findPath(self, maze, start, exit):
|
||||
if exit is None:
|
||||
return [], 0
|
||||
|
||||
queue = deque([start])
|
||||
visited = {start}
|
||||
parent = {}
|
||||
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
|
||||
if current == exit:
|
||||
break
|
||||
|
||||
for n in maze.getNeighbors(current):
|
||||
if n not in visited:
|
||||
visited.add(n)
|
||||
parent[n] = current
|
||||
queue.append(n)
|
||||
|
||||
return restore(parent, start, exit), len(visited)
|
||||
25
pomelovsd/ExitMaze/Strategies/DFS.py
Normal file
25
pomelovsd/ExitMaze/Strategies/DFS.py
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
from Strategies.strat import PathFindingStrategy
|
||||
from Strategies.path import restore
|
||||
|
||||
class DFS(PathFindingStrategy):
|
||||
def findPath(self, maze, start, exit):
|
||||
if exit is None:
|
||||
return [], 0
|
||||
|
||||
stack = [start]
|
||||
visited = {start}
|
||||
parent = {}
|
||||
|
||||
while stack:
|
||||
current = stack.pop()
|
||||
|
||||
if current == exit:
|
||||
break
|
||||
|
||||
for n in maze.getNeighbors(current):
|
||||
if n not in visited:
|
||||
visited.add(n)
|
||||
parent[n] = current
|
||||
stack.append(n)
|
||||
|
||||
return restore(parent, start, exit), len(visited)
|
||||
0
pomelovsd/ExitMaze/Strategies/__init__.py
Normal file
0
pomelovsd/ExitMaze/Strategies/__init__.py
Normal file
14
pomelovsd/ExitMaze/Strategies/path.py
Normal file
14
pomelovsd/ExitMaze/Strategies/path.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
def restore(parent, start, exit):
|
||||
if exit not in parent and start != exit:
|
||||
return []
|
||||
|
||||
path = []
|
||||
current = exit
|
||||
|
||||
while current != start:
|
||||
path.append(current)
|
||||
current = parent[current]
|
||||
|
||||
path.append(start)
|
||||
path.reverse()
|
||||
return path
|
||||
6
pomelovsd/ExitMaze/Strategies/strat.py
Normal file
6
pomelovsd/ExitMaze/Strategies/strat.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
class PathFindingStrategy(ABC):
|
||||
@abstractmethod
|
||||
def findPath(self, maze, start, exit):
|
||||
pass
|
||||
BIN
pomelovsd/ExitMaze/analysis.png
Normal file
BIN
pomelovsd/ExitMaze/analysis.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
365
pomelovsd/ExitMaze/main.ipynb
Normal file
365
pomelovsd/ExitMaze/main.ipynb
Normal file
File diff suppressed because one or more lines are too long
BIN
pomelovsd/ExitMaze/mermaid.png
Normal file
BIN
pomelovsd/ExitMaze/mermaid.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 914 KiB |
173
pomelovsd/ExitMaze/result maze.md
Normal file
173
pomelovsd/ExitMaze/result maze.md
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
# Структура:
|
||||
- **Описание задачи и выбранных паттернов** (с диаграммой классов из Mermaid).
|
||||
- **Листинги ключевых классов** (можно выборочно) **или ссылка на репозиторий**.
|
||||
- **Результаты экспериментов** (таблицы, графики).
|
||||
- **Анализ эффективности алгоритмов и применимости паттернов**.
|
||||
- **Выводы: как ООП и паттерны помогли сделать код гибким и расширяемым. Что было бы сложно изменить без них**.
|
||||
### Выводы:
|
||||
#### 1) **Описание задачи и выбранных паттернов**
|
||||
![[mermaid.png]]
|
||||
>Диаграмма классов
|
||||
|
||||
| Паттерн | Реализация в проекте | Обоснование |
|
||||
| ------------ | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Builder** | `MazeBuilders` (интерфейс)<br>`TextFileMazeBuilder` | Создание лабиринта из текстового файла.<br>Позволяя легко добавить другие форматы без изменения `Maze` |
|
||||
| **Strategy** | `PathFindingStrategy` (интерфейс)<br>`BFS`<br>`DFS`<br>`AStar` | Алгоритмы поиска пути взаимозаменяемы<br>Strategy позволяет переключать их через `MazeSolver.setStrategy()` и добавлять новые (например, Дейкстра) без изменения кода |
|
||||
| **Observer** | `Observer` (интерфейс)<br>`ConsoleView` | Отделяет визуализацию от логики поиска<br>`MazeSolver` может уведомлять подписчиков о событиях (начало/конец поиска), а `ConsoleView` реагирует на них<br>Легко добавить другие виды отображения (GUI, лог-файл) |
|
||||
| **Command** | `Command` (интерфейс)<br>`Observer` (интерфейс)<br>`MoveCommand`<br>`ConsoleView` | Обеспечивает пошаговое наблюдение и отслеживание с возможностью отмены |
|
||||
| | | |
|
||||
|
||||
#### 2) **Листинги ключевых классов**:
|
||||
**Core:**
|
||||
```python
|
||||
class Cell:
|
||||
|
||||
def __init__(self, x = 0, y = 0, isWall = False, isStart = False, isExit = False):
|
||||
|
||||
self.x = x
|
||||
|
||||
self.y = y
|
||||
|
||||
self.isWall = isWall
|
||||
|
||||
self.isStart = isStart
|
||||
|
||||
self.isExit = isExit
|
||||
|
||||
# Возращает True, если посаседству стена
|
||||
|
||||
def isPassable(self):
|
||||
|
||||
return not self.isWall
|
||||
|
||||
class Maze:
|
||||
|
||||
def __init__(self, grid, start = None, exit = None):
|
||||
|
||||
self.grid = grid
|
||||
|
||||
self.start = start
|
||||
|
||||
self.exit = exit
|
||||
|
||||
self.height = len(grid)
|
||||
|
||||
self.width = len(grid[0]) if grid else 0
|
||||
|
||||
|
||||
|
||||
# Создание новой ячейки
|
||||
|
||||
def getCell(self, x, y):
|
||||
|
||||
if 0 <= x < self.height and 0 <= y < self.width:
|
||||
|
||||
return self.grid[x][y]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
# Ищет соседние проходимые клетки
|
||||
|
||||
def getNeighbors(self, cell):
|
||||
|
||||
directions = [(0,1),(1,0),(0,-1),(-1,0)]
|
||||
|
||||
result = []
|
||||
|
||||
|
||||
|
||||
for dx, dy in directions:
|
||||
|
||||
nx, ny = cell.x + dx, cell.y + dy
|
||||
|
||||
neighbor = self.getCell(nx, ny)
|
||||
|
||||
if neighbor and neighbor.isPassable():
|
||||
|
||||
result.append(neighbor)
|
||||
|
||||
|
||||
|
||||
return result
|
||||
```
|
||||
**Builder:**
|
||||
```python
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class MazeBuilders(ABC):
|
||||
|
||||
@abstractmethod
|
||||
|
||||
def build_from_file(self, filename):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
from Core.Cell import Cell
|
||||
|
||||
from Core.Maze import Maze
|
||||
|
||||
from Builder.BuilderInterface import MazeBuilders
|
||||
|
||||
|
||||
|
||||
class TextFileMazeBuilder(MazeBuilders):
|
||||
|
||||
|
||||
|
||||
def build_from_file(self, filename):
|
||||
|
||||
grid = []
|
||||
|
||||
start = None
|
||||
|
||||
exit = None
|
||||
|
||||
|
||||
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
|
||||
lines = [line.rstrip("\n") for line in f]
|
||||
|
||||
for y, line in enumerate(lines):
|
||||
|
||||
row = []
|
||||
|
||||
for x, ch in enumerate(line):
|
||||
|
||||
cell = Cell(x, y, isWall = (ch == "#"), isStart = (ch == "S"), isExit = (ch == "E"))
|
||||
|
||||
if (ch == "S"):
|
||||
|
||||
start = cell
|
||||
|
||||
if (ch == "E"):
|
||||
|
||||
exit = cell
|
||||
|
||||
row.append(cell)
|
||||
|
||||
|
||||
|
||||
grid.append(row)
|
||||
|
||||
return Maze(grid, start, exit)
|
||||
```
|
||||
|
||||
|
||||
#### 3) **Результаты экспериментов**:
|
||||
![[analysis 3.png]]
|
||||
>График созданный на основе 5 попыток замеров и их усреднения
|
||||
### 4) **Анализ эффективности алгоритмов и применимости паттернов:**
|
||||
- **BFS**
|
||||
Работает медленно и обходит гораздо больше клеток, но гарантирует кратчайший маршерут
|
||||
- **DFS**
|
||||
Работает быстро, но за это приходиться платить не самыми оптимальными путями и количеством обходимых маршерутов(из-за чего растёт время работы)
|
||||
- **A****
|
||||
Является золотой серединой между DFS и BFS ищет маршерут хуже BFS, но лучше чем DFS, обратная зависимость наблюдается в измерении времени
|
||||
#### 5)**Выводы: как ООП и паттерны помогли сделать код гибким и расширяемым? Что было бы сложно изменить без них?**
|
||||
ООП и паттерны помогли систематизировать код и написать единый код для 3 структур, так же легко масштабировать проект, за счёт единых функций применимых для разных алгоритмов
|
||||
Сложно было бы изменить файлы лабиринтов, алгоритмы поиска пути,
|
||||
16
pomelovsd/ExitMaze/results.csv
Normal file
16
pomelovsd/ExitMaze/results.csv
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
maze,strategy,time_ms,visited_cells,path_length
|
||||
small,BFS,0.379,58,15
|
||||
small,DFS,0.076,31,19
|
||||
small,A*,0.298,57,15
|
||||
medium,BFS,5.197,1263,173
|
||||
medium,DFS,3.898,1229,173
|
||||
medium,A*,4.109,806,173
|
||||
large,BFS,17.886,3918,269
|
||||
large,DFS,7.197,1905,269
|
||||
large,A*,10.377,2040,269
|
||||
empty,BFS,0.195,64,15
|
||||
empty,DFS,0.135,64,29
|
||||
empty,A*,0.288,63,15
|
||||
no_exit,BFS,0.007,0,0
|
||||
no_exit,DFS,0.007,0,0
|
||||
no_exit,A*,0.007,0,0
|
||||
|
Loading…
Reference in New Issue
Block a user