Merge pull request '[2]task2' (#322) from romanovpv/2026-rff_mp:task2 into develop
Reviewed-on: #322
This commit is contained in:
commit
1f016b85f0
100
romanovpv/task 2/docs/data/big_maze.txt
Normal file
100
romanovpv/task 2/docs/data/big_maze.txt
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
####################################################################################################
|
||||
#S ### ## # ## ## # # # # ## # ## ## # #### ##### # ## ## # ## # ### #
|
||||
# # ### # ## # # # # ## # # # # # # ## ## # # ### ## # # #
|
||||
# # # # ## # # ## ## # # # ## # # # #### # # ### ### # ## # # # #
|
||||
# ### ### ## ## # ### # ## # # # # # # ## ## ## ## # #### ### ## # ##
|
||||
# # # # ## # # # # ## ## # # ## # #### ### #### # # ### ### # ###
|
||||
# ## # # # # # ## # ## ## ## ## # ### ## #### ## # # # # ## ##
|
||||
# ## # # # #### # # # # # # # ###### # # # # ## ## # # ## # ## ## ##
|
||||
# # # # # ## ## # # # ### # ## # # ### # # ### ## # ## # # # ## # # ## ### #
|
||||
# ## ## # # # # # ## #### ### # ## ##### # # # # # # # ##
|
||||
# # ### ## ## # ### # ## # # ##### # # # # ### ### # ### ###
|
||||
# # ## # ## ## # # # ## ## # # # #### ## # # # ## # ####
|
||||
# # ### # ## # # # # # # # # # # # # # ## # #### # # # # # # ##
|
||||
# ### # ## ## # # # ## # # # ## ### # ## # ## # ## ## # ## # ### ## ## ##
|
||||
# ## #### ###### # ## ## ## # # ## # #### # #### ## ## # # # # ## ## # ## #
|
||||
# ## # ### ### ## # ## ## # ## # # # # ## # # ## ## # # # # # ## #
|
||||
# ## ## ## # ### # ## # ## # # # ##### # #### # ## # # # ## # # ## ### ##
|
||||
# # # ## ## ## # #### ##### # ## ## # ### # # # # # # # ### ##
|
||||
# # # # # ## ## # ### # # ## # #### # ### ## # # ### ###### # ## #
|
||||
# ## # # # ### # # # ## # # # ## # # ### ## ## #
|
||||
# # # # # # ## # # # # ## # # # # # # # ### # ##### # # # # ### ## # # ##
|
||||
# ### # # # ## # ### # # # # ## ## # ### ## ### # # # # # # ## #
|
||||
# # ## ### # # # # ## ##### #### # # ### ### #### # ## # ###
|
||||
# # # # # # # ## # # ## ### # # ## # # ### # # # ## # # # #
|
||||
# # #### # # ##### # # ### ## # # # # # ## ### ## # # # ### # ## #
|
||||
# # ### ## # # ## # # ## ### # ## ## # # # # # # # ## # # #
|
||||
# # ## # ## # # # ### ## # ## # # ### # # # ### # # # # # ##
|
||||
# # # # # ## # # # # # ## # #### # # ## # # # ###### ### ###
|
||||
# # # # ##### ## # ## ### ## # ## ## # # ## # ##### ## ## # # ## ##
|
||||
# # # ### # # ## # ### # # # # ## # # # ## # # # # #### # # # #
|
||||
# ### # # ## # # ## # # ## # # # #### ### # # ### # # ### ### ####
|
||||
# ##### ## # # ## # # # # ## ### # # # ## # # ### ## # # # ## #
|
||||
# ## # ## # ## #### # ## # # ## # ## ## # # ## ## # ### # # # # #
|
||||
# # #### ## ### ## ### ## # # ## # ##### # # # ## ## ## ### ### # ##### # ## # #
|
||||
# # # # ###### ## ## ### # # ## # # ## # # ### ## #### # # ## # #
|
||||
# # # ## ### #### # # ## # # # ## # ## # # # ## # # # #### # # ## #
|
||||
# # # # # # ### ## # # ## # # ##### # # # # # ### ## ## ### # #
|
||||
# # ## # # # # ## ## # ## # # # #### ## # ## ##### # ## # # # ### # #
|
||||
# ## ## ## # ## # ## ### # # # # ## # ## # # # ### #### # # # #
|
||||
# ### ### # # ## ##### # # # #### # # # ## ## ## # ### ## # ### # ### # # #
|
||||
# # # ## ## # ## ## ## # # # # # ## # # ## ## ## # ## ## ## # # #
|
||||
# ## # # ### # # # # # ## # # # ##### # # # # # #### # # ###
|
||||
# ### # ## ## ## # # # # # # # # # #### ## # ## # # # # ## # ## #
|
||||
# # # # ### # # ## # # #### # # # # ## # ## ## ## ## # # ## # # # ## #
|
||||
# ## ## # ## # ### # ## # ##### # # # # ## # # # #### ## ##### #
|
||||
# # ## # # # ## # # #### # ## ## # # # ## ## ## #### #### ## # ###
|
||||
# # # ## ## # # # # ## # ## # # # # ## # # ## # # ## # ##
|
||||
# #### # # ## # ## # ### # # # # # #### # # # ### # # # # # # ##
|
||||
# # # # # # # ## # # ## # # # # ## # ## # # # # ### # ## # # ### #
|
||||
# ### # ##### ## # # ## ### ## # #### ## # ## ### ## # # # # # # ### ##
|
||||
# # ## #### ## # # # # # # # ## # ###### # ## #### ###### # ####
|
||||
# # # ##### # ## # ## # # # ## ### # # ## # # # # # # # #
|
||||
# # # # # # # # # # # ## ## # # # ## ### ### # # ### #
|
||||
# ## ## # # # # # # # # ## #### # # #### # #### # ## # # ## # ####
|
||||
# # # ## # # # ## ## ### # # # # # # # ### # # # # ## ## # # # ## # #
|
||||
# # ## #### # ## ## ## # # # ### # # ## ## ####### ### # # # ##
|
||||
# # ##### # # # # # # ## # # ### # #### # # # #### # # # # #
|
||||
# # ## # ### # # ## # # # # # ### ## # ## # # ## # # # ### # ## #####
|
||||
# # ## #### ## # # # ## ### # ## # ### # ## # #### ## # # ## # #
|
||||
# # ## ## ## # # ### # # # ### ### # # # ##### # # # # # ##
|
||||
# # ## # # # # # ### # ## # # # ## # # # ## # ### ### # # # ##
|
||||
# # ### ## ##### ## # ## # # ###### # ## ####### # # ## # ### ## ## # # ##### ##
|
||||
# ### # ### # # # # # # ##### # # # # ##### # ##### #
|
||||
# # # ## # # # #### ## # ## # # # # # ## ## ### # # # #### #
|
||||
# ### # ## # # # ## ## # # # ##### # # # ## # # # ## # ### ## # #
|
||||
# # # ## ## # # # # ## ## # ## # # ## ## # # ## # ##### # ####
|
||||
# ##### # #### # # # #### # # ## ## # # ## ## # # ##### # ##### # ## ##
|
||||
# ## # ## # # # ## # # # ### # ### ##### #### # # # # # ## ## # # #
|
||||
# # ### # # # #### # # ## # # ## # ### ### ## ## ## # # ## # # # # # ## # # #
|
||||
# # # # # # # # # # # ## # # ## # ## ### # # # # ### # # ## ### ## #
|
||||
# ## ## # # ### ##### ## # ### ## # # ## # # # # ##### # ### ### ## # #
|
||||
# ## ## ## ## # ## ### ## ### # # # # # # ## # # # # ##
|
||||
# # # #### # # # # # #### # # # # # # ### ## ## #
|
||||
# # ## ## ### # # # # ## # #### ## # ## # # # # ### ### ## # # # # ###
|
||||
# # # ## # ## ## ## ## # ## # # ### # # # # # # ### # #### # # # ## #
|
||||
# # ### # ## ## # # # ## # ## # # # # # # ## # # # # #
|
||||
# # # # # # # # ## #### # ### ## ## # ## # ### # ### ##
|
||||
# # # ## # # ### ## # # ### # ## ## # # ### # #### # # # # ## #
|
||||
# # # # # ### # # # # ### ### ## # # # ## ## # ## ### # # # ##
|
||||
# ## #### # ### # # ##### # # # # ## ## # #### #### #
|
||||
# # # # # # # ## # ## ## # # ## ## # ### # ### # #
|
||||
# # # # ## # # ## # ### ### # ### #### # # # ### ## # ## # ## # ### ##
|
||||
# ### ## ## # # # # # # # ### ### ## ## ### # ## ## ## ### ##
|
||||
# ## # ## ### ## # #### # ## # # ## ## # # ## ## ## # ## ## # #
|
||||
# # ## ## # ## # # # # #### # ## ## ## # ### # ### #
|
||||
# # # # # # ## # # # # # # ## ## # ##### # # # # ###### ### #
|
||||
# # # ## ### ## ### # # ## ## # # # # ### # # ## ### #### # #
|
||||
# ## # ## # ### # # # # ## # # ### # # # # #### # # ### # # # ## ####
|
||||
# # # # #### ## # ## # ### #### # # # # # # # # # # ## ### # ##
|
||||
# ## ## ### # ### ## ## ## #### #### ## ## ### # ## # # ## # # # # # # # #
|
||||
# # # # ### # # ## # # # # ## ### ## # ### # # # # ## # # # ###
|
||||
# # # #### # ## # # # ## ### # ## ## ## ## ## ### # ### # # # #
|
||||
# # # # # # ## # # ## ## ## # ##### # # # # ## ### # ##### # #
|
||||
# ## # ### # # # # # ## # ## # # ## # ## ## # # # # ## #### # # # # # # #
|
||||
# # ## ## ## #### # # ## # # # # ### #### # ### # # # # ### # #
|
||||
# # #### # ## # ## ## ## ## # #### # ## # # # ### ## ### # # #
|
||||
# # ### ## ## ## # ### ### # # # # # ## ## ## # ##### ## # # # #
|
||||
# ## # # ## # # # ## ## # # # # # # ## # ### # # # ## ### ## #
|
||||
# E#
|
||||
####################################################################################################
|
||||
41
romanovpv/task 2/docs/data/builders.py
Normal file
41
romanovpv/task 2/docs/data/builders.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from model import Maze, Cell
|
||||
|
||||
class MazeBuilder(ABC):
|
||||
@abstractmethod
|
||||
def buildFromFile(self, filename):
|
||||
pass
|
||||
|
||||
class TextFileMazeBuilder(MazeBuilder):
|
||||
def buildFromFile(self, filename):
|
||||
with open(filename, "r", encoding="utf-8") as file:
|
||||
lines = [line.rstrip("\n")
|
||||
for line in file
|
||||
]
|
||||
height = len(lines)
|
||||
width = len(lines[0])
|
||||
maze = Maze(width, height)
|
||||
start_count = 0
|
||||
exit_count = 0
|
||||
for x, line in enumerate(lines):
|
||||
row = []
|
||||
for y, symbol in enumerate(line):
|
||||
if symbol == "#":
|
||||
cell = Cell(x, y, is_wall=True)
|
||||
elif symbol == "S":
|
||||
cell = Cell(x, y, is_start=True)
|
||||
start_count += 1
|
||||
elif symbol == "E":
|
||||
cell = Cell(x, y, is_exit=True)
|
||||
exit_count += 1
|
||||
elif symbol == " ":
|
||||
cell = Cell(x, y)
|
||||
else:
|
||||
raise ValueError(f"Неизвестный символ: {symbol}")
|
||||
row.append(cell)
|
||||
maze.add_row(row)
|
||||
if start_count != 1:
|
||||
raise ValueError("Должен быть ровно один старт S")
|
||||
if exit_count != 1:
|
||||
raise ValueError("Должен быть ровно один выход E")
|
||||
return maze
|
||||
43
romanovpv/task 2/docs/data/generate_mazes.py
Normal file
43
romanovpv/task 2/docs/data/generate_mazes.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import random
|
||||
|
||||
def save_maze(filename, width, height, wall_probability):
|
||||
maze = []
|
||||
for i in range(height):
|
||||
row = ""
|
||||
for j in range(width):
|
||||
if i == 0 or i == height-1:
|
||||
row += "#"
|
||||
elif j == 0 or j == width-1:
|
||||
row += "#"
|
||||
else:
|
||||
if random.random() < wall_probability:
|
||||
row += "#"
|
||||
else:
|
||||
row += " "
|
||||
|
||||
maze.append(list(row))
|
||||
maze[1][1] = "S"
|
||||
maze[height-2][width-2] = "E"
|
||||
|
||||
for i in range(1, height-1):
|
||||
maze[i][1] = " "
|
||||
for j in range(1, width-1):
|
||||
maze[height-2][j] = " "
|
||||
maze[1][1] = "S"
|
||||
maze[height-2][width-2] = "E"
|
||||
with open(filename, "w", encoding="utf-8") as f:
|
||||
for row in maze:
|
||||
f.write("".join(row)+"\n")
|
||||
save_maze(
|
||||
"medium_maze.txt",
|
||||
50,
|
||||
50,
|
||||
0.30
|
||||
)
|
||||
save_maze(
|
||||
"big_maze.txt",
|
||||
100,
|
||||
100,
|
||||
0.40
|
||||
)
|
||||
print("Лабиринты созданы")
|
||||
50
romanovpv/task 2/docs/data/graphs.py
Normal file
50
romanovpv/task 2/docs/data/graphs.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
from result import results
|
||||
|
||||
df = pd.DataFrame(
|
||||
results[1:],
|
||||
columns=results[0]
|
||||
)
|
||||
|
||||
time_data = df.pivot(
|
||||
index="maze",
|
||||
columns="strategy",
|
||||
values="time_ms"
|
||||
)
|
||||
|
||||
time_data.plot(kind="bar")
|
||||
|
||||
plt.title("Время выполнения")
|
||||
plt.ylabel("мс")
|
||||
plt.xticks(rotation=0)
|
||||
|
||||
plt.show()
|
||||
|
||||
cells_data = df.pivot(
|
||||
index="maze",
|
||||
columns="strategy",
|
||||
values="cells visited"
|
||||
)
|
||||
|
||||
cells_data.plot(kind="bar")
|
||||
|
||||
plt.title("Количество посещённых клеток")
|
||||
plt.ylabel("клетки")
|
||||
plt.xticks(rotation=0)
|
||||
|
||||
plt.show()
|
||||
|
||||
path_data = df.pivot(
|
||||
index="maze",
|
||||
columns="strategy",
|
||||
values="path length"
|
||||
)
|
||||
|
||||
path_data.plot(kind="bar")
|
||||
|
||||
plt.title("Длина пути")
|
||||
plt.ylabel("шаги")
|
||||
plt.xticks(rotation=0)
|
||||
|
||||
plt.show()
|
||||
68
romanovpv/task 2/docs/data/main.py
Normal file
68
romanovpv/task 2/docs/data/main.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
from builders import (TextFileMazeBuilder)
|
||||
from strategies import (BFSStrategy, DFSStrategy, AStarStrategy)
|
||||
from solver import (MazeSolver)
|
||||
from observer_command import ConsoleView, Player, MoveCommand
|
||||
import os
|
||||
|
||||
builder = TextFileMazeBuilder()
|
||||
maze = builder.buildFromFile("no_exit_maze.txt")
|
||||
print("Лабиринт:\n")
|
||||
maze.printMaze()
|
||||
print("Выберете алгоритм")
|
||||
print("1 - BFS")
|
||||
print("2 - DFS")
|
||||
print("3 - A*")
|
||||
choice = input()
|
||||
if choice == "1":
|
||||
strategy = BFSStrategy()
|
||||
elif choice == "2":
|
||||
strategy = DFSStrategy()
|
||||
elif choice == "3":
|
||||
strategy = AStarStrategy()
|
||||
else:
|
||||
print("Неверный выбор")
|
||||
exit()
|
||||
|
||||
solver = MazeSolver(maze, strategy)
|
||||
view = ConsoleView()
|
||||
solver.addObserver(view)
|
||||
stats = solver.solve()
|
||||
print("Результат:")
|
||||
print(stats)
|
||||
path, _ = strategy.findPath(
|
||||
maze,
|
||||
maze.start,
|
||||
maze.exit
|
||||
)
|
||||
|
||||
if not path:
|
||||
print("\nПуть не найден")
|
||||
exit()
|
||||
|
||||
print("\nНайденный путь:")
|
||||
for cell in path:
|
||||
print(f"({cell.x}, {cell.y})")
|
||||
|
||||
print("\nПошаговое движение игрока")
|
||||
player = Player(maze.start)
|
||||
|
||||
history = []
|
||||
passed_path = [maze.start]
|
||||
|
||||
view.render(maze, player, passed_path)
|
||||
|
||||
for cell in path[1:]:
|
||||
|
||||
input("\nEnter -> следующий шаг")
|
||||
|
||||
command = MoveCommand(player, cell)
|
||||
command.execute()
|
||||
|
||||
history.append(command)
|
||||
|
||||
passed_path.append(cell)
|
||||
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
|
||||
view.render(maze, player, passed_path)
|
||||
|
||||
50
romanovpv/task 2/docs/data/medium_maze.txt
Normal file
50
romanovpv/task 2/docs/data/medium_maze.txt
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
##################################################
|
||||
#S## # ## # # # # # # ## #
|
||||
# # # # # # # ### # #### # #
|
||||
# ## ## # # ## ### # # #
|
||||
# ## #### ### ## ## ## # # #
|
||||
# # #### # ## # # # ## ## # #####
|
||||
# # ## # ### ## # # #### # # #
|
||||
# # # # ### # ## # ##### # ### # #
|
||||
# # # # # # # # # # ## ### ##
|
||||
# # # # # # ## # ## ## # # #
|
||||
# # # # ### #### ### # # ## ## #
|
||||
# # ## # ## # # ## # ## # # ## ##
|
||||
# # # # # # # ##### ## # ### # # ##
|
||||
# # # ## ### ## # # # ## #
|
||||
# ## # # ### # # # # ## # #
|
||||
# # #### ### ### #### # ## # #
|
||||
# # # # # # # ## # ## # #
|
||||
# ## ## # # # # # # ## ### #
|
||||
# ## # ## # ## #### ### # #### # # #
|
||||
# ## ### # # # # # # ## #
|
||||
# # # ## # ##### ### # #
|
||||
# # # ## # # ## # #
|
||||
# # ## # # # # ## # # #
|
||||
# # # # # #
|
||||
# # ## ## # ## ## #
|
||||
# # ## ### # # # # ## ##
|
||||
# # #### # # ## # # # ## # ##
|
||||
# # # # # ## ## # # # # ####
|
||||
# # # # # ## ##### # # # ##
|
||||
# # ## # ## ### # # ## # ## ## #
|
||||
# # # ### ## ###### # # ## # # # #
|
||||
# # # ## # # # # # ##
|
||||
# # # # # # ## ## # # # ## #
|
||||
# ##### # # ## ## ## # ## ### ##
|
||||
# ## ## # # # ## # ## ##
|
||||
# # # # # ### # # # ### # #
|
||||
# ## # ## ### ## # ##
|
||||
# # ### # ## ## # ## ## # # # #
|
||||
# ## # ## # # ## #### # ## ##
|
||||
# ## # # # # ## ## # # # #
|
||||
# # # # # # # # # ## # #
|
||||
# # # # # # ## # ### # ###
|
||||
# ## # ## # # # ## # # #
|
||||
# # # # # # # # # # ## #
|
||||
# ## # # # # ## ## # ## # # # #
|
||||
# # # ## # # # ## # ## #
|
||||
# ## ## # # # # # ## # # # ## # #
|
||||
# # ## ## # # ## # # # # #
|
||||
# E#
|
||||
##################################################
|
||||
86
romanovpv/task 2/docs/data/model.py
Normal file
86
romanovpv/task 2/docs/data/model.py
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
class Cell:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
x: int,
|
||||
y: int,
|
||||
is_wall=False,
|
||||
is_start=False,
|
||||
is_exit=False
|
||||
):
|
||||
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
self.isWall = is_wall
|
||||
self.isStart = is_start
|
||||
self.isExit = is_exit
|
||||
|
||||
def isPassable(self):
|
||||
return not self.isWall
|
||||
|
||||
def __repr__(self):
|
||||
return f"Cell({self.x},{self.y})"
|
||||
|
||||
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))
|
||||
|
||||
class Maze:
|
||||
|
||||
def __init__(self, width: int, height: int):
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
self.cells = []
|
||||
|
||||
self.start = None
|
||||
self.exit = None
|
||||
|
||||
def add_row(self, row):
|
||||
self.cells.append(row)
|
||||
for cell in row:
|
||||
if cell.isStart:
|
||||
self.start = cell
|
||||
if cell.isExit:
|
||||
self.exit = cell
|
||||
|
||||
def getCell(self, x, y):
|
||||
if 0 <= x < self.height and 0 <= y < self.width:
|
||||
return self.cells[x][y]
|
||||
return None
|
||||
|
||||
def getNeighbors(self, cell):
|
||||
directions = [
|
||||
(-1, 0), # вверх
|
||||
(1, 0), # вниз
|
||||
(0, -1), # влево
|
||||
(0, 1) # вправо
|
||||
]
|
||||
neighbors = []
|
||||
for dx, dy in directions:
|
||||
nx = cell.x + dx
|
||||
ny = cell.y + dy
|
||||
neighbor = self.getCell(nx, ny)
|
||||
if (neighbor and neighbor.isPassable()):
|
||||
neighbors.append(neighbor)
|
||||
return neighbors
|
||||
def printMaze(self):
|
||||
for row in self.cells:
|
||||
line = ""
|
||||
for cell in row:
|
||||
if cell.isStart:
|
||||
line += "S"
|
||||
elif cell.isExit:
|
||||
line += "E"
|
||||
elif cell.isWall:
|
||||
line += "#"
|
||||
else:
|
||||
line += " "
|
||||
print(line)
|
||||
10
romanovpv/task 2/docs/data/no_exit_maze.txt
Normal file
10
romanovpv/task 2/docs/data/no_exit_maze.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
##########
|
||||
#S # #
|
||||
# ###### #
|
||||
# ###### #
|
||||
# ###### #
|
||||
# ###### #
|
||||
# ###### #
|
||||
# ###### #
|
||||
# ######E#
|
||||
##########
|
||||
10
romanovpv/task 2/docs/data/no_wall_maze.txt
Normal file
10
romanovpv/task 2/docs/data/no_wall_maze.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
##########
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##########
|
||||
68
romanovpv/task 2/docs/data/observer_command.py
Normal file
68
romanovpv/task 2/docs/data/observer_command.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
class Observer(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def update(self, event):
|
||||
pass
|
||||
|
||||
class ConsoleView(Observer):
|
||||
|
||||
def update(self, event):
|
||||
print(f"\n[Событие] {event}")
|
||||
|
||||
def render(self, maze, player=None, path=None):
|
||||
|
||||
path = path or []
|
||||
|
||||
print()
|
||||
|
||||
for row in maze.cells:
|
||||
|
||||
line = ""
|
||||
|
||||
for cell in row:
|
||||
|
||||
if player and cell == player.position:
|
||||
line += "P"
|
||||
|
||||
elif cell.isStart:
|
||||
line += "S"
|
||||
|
||||
elif cell.isExit:
|
||||
line += "E"
|
||||
|
||||
elif cell.isWall:
|
||||
line += "#"
|
||||
|
||||
elif cell in path:
|
||||
line += "*"
|
||||
|
||||
else:
|
||||
line += " "
|
||||
|
||||
print(line)
|
||||
|
||||
|
||||
class Command(ABC):
|
||||
@abstractmethod
|
||||
def execute(self):
|
||||
pass
|
||||
@abstractmethod
|
||||
def undo(self):
|
||||
pass
|
||||
|
||||
class Player:
|
||||
def __init__(self, start_cell):
|
||||
self.position = start_cell
|
||||
|
||||
class MoveCommand(Command):
|
||||
def __init__(self, player, new_cell):
|
||||
self.player = player
|
||||
self.new_cell = new_cell
|
||||
self.old_cell = None
|
||||
def execute(self):
|
||||
self.old_cell = self.player.position
|
||||
self.player.position = self.new_cell
|
||||
def undo(self):
|
||||
self.player.position = self.old_cell
|
||||
21
romanovpv/task 2/docs/data/result.py
Normal file
21
romanovpv/task 2/docs/data/result.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import csv
|
||||
|
||||
results = [
|
||||
["maze", "strategy", "time_ms", "cells visited", "path length"],
|
||||
["small_maze", "BFS", 0.173, 15, 15],
|
||||
["small_maze", "DFS", 0.198, 15, 15],
|
||||
["small_maze", "A*", 0.195, 15, 15],
|
||||
["medium_maze", "BFS", 7.228, 95, 95],
|
||||
["medium_maze", "DFS", 1.361, 189, 189],
|
||||
["medium_maze", "A*", 3.050, 95, 95],
|
||||
["big_maze", "BFS", 18.487, 195, 195],
|
||||
["big_maze", "DFS", 10.021, 497, 497],
|
||||
["big_maze", "A*", 4.471, 195, 195],
|
||||
["no_wall_maze", "BFS", 0.325, 15, 15],
|
||||
["no_wall_maze", "DFS", 0.251, 29, 29],
|
||||
["no_wall_maze", "A*", 0.396, 15, 15],
|
||||
]
|
||||
|
||||
with open("results.csv", "w", newline="") as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerows(results)
|
||||
13
romanovpv/task 2/docs/data/results.csv
Normal file
13
romanovpv/task 2/docs/data/results.csv
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
maze,strategy,time_ms,cells visited,path length
|
||||
small_maze,BFS,0.173,15,15
|
||||
small_maze,DFS,0.198,15,15
|
||||
small_maze,A*,0.195,15,15
|
||||
medium_maze,BFS,7.228,95,95
|
||||
medium_maze,DFS,1.361,189,189
|
||||
medium_maze,A*,3.05,95,95
|
||||
big_maze,BFS,18.487,195,195
|
||||
big_maze,DFS,10.021,497,497
|
||||
big_maze,A*,4.471,195,195
|
||||
no_wall_maze,BFS,0.325,15,15
|
||||
no_wall_maze,DFS,0.251,29,29
|
||||
no_wall_maze,A*,0.396,15,15
|
||||
|
10
romanovpv/task 2/docs/data/small_maze.txt
Normal file
10
romanovpv/task 2/docs/data/small_maze.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
##########
|
||||
#S #
|
||||
# ###### #
|
||||
# # #
|
||||
###### # #
|
||||
# # #
|
||||
# ###### #
|
||||
# #
|
||||
# ######E#
|
||||
##########
|
||||
42
romanovpv/task 2/docs/data/solver.py
Normal file
42
romanovpv/task 2/docs/data/solver.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import time
|
||||
|
||||
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
|
||||
def __str__(self):
|
||||
return (
|
||||
f"Время: "
|
||||
f"{self.time_ms:.3f} мс\n"
|
||||
|
||||
f"Посещено клеток: "
|
||||
f"{self.visited_cells}\n"
|
||||
|
||||
f"Длина пути: "
|
||||
f"{self.path_length}"
|
||||
)
|
||||
|
||||
class MazeSolver:
|
||||
def __init__(self,maze, strategy):
|
||||
self.maze = maze
|
||||
self.strategy = strategy
|
||||
self.observers = []
|
||||
def setStrategy(self, strategy
|
||||
):
|
||||
self.strategy = strategy
|
||||
def solve(self):
|
||||
self.notify("Начат поиск")
|
||||
start_time = (time.perf_counter())
|
||||
path, visited = (self.strategy.findPath(self.maze,self.maze.start,self.maze.exit))
|
||||
end_time = (time.perf_counter())
|
||||
self.notify("Путь найден")
|
||||
time_ms = ((end_time-start_time)*1000)
|
||||
visited = len(path)
|
||||
stats = SearchStats(time_ms,visited,len(path))
|
||||
return stats
|
||||
def addObserver(self, observer):
|
||||
self.observers.append(observer)
|
||||
def notify(self, event):
|
||||
for observer in self.observers:
|
||||
observer.update(event)
|
||||
73
romanovpv/task 2/docs/data/strategies.py
Normal file
73
romanovpv/task 2/docs/data/strategies.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from collections import deque
|
||||
import heapq
|
||||
|
||||
class PathFindingStrategy(ABC):
|
||||
@abstractmethod
|
||||
def findPath(self, maze, start, exit_cell):
|
||||
pass
|
||||
def restorePath(self, parent, start, exit_cell):
|
||||
path = []
|
||||
current = exit_cell
|
||||
while current != start:
|
||||
path.append(current)
|
||||
current = parent[current]
|
||||
path.append(start)
|
||||
path.reverse()
|
||||
return path
|
||||
class BFSStrategy(PathFindingStrategy):
|
||||
def findPath( self, maze, start, exit_cell):
|
||||
queue = deque([start])
|
||||
visited = {start}
|
||||
parent = {}
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
if current == exit_cell:
|
||||
return (self.restorePath(parent, start, exit_cell), len(visited))
|
||||
for neighbor in maze.getNeighbors(current):
|
||||
if neighbor not in visited:
|
||||
visited.add(neighbor)
|
||||
parent[neighbor] = current
|
||||
queue.append(neighbor)
|
||||
return [], len(visited)
|
||||
|
||||
class DFSStrategy(PathFindingStrategy):
|
||||
def findPath(self, maze, start, exit_cell):
|
||||
stack = [start]
|
||||
visited = {start}
|
||||
parent = {}
|
||||
while stack:
|
||||
current = stack.pop()
|
||||
if current == exit_cell:
|
||||
return (self.restorePath(parent,start,exit_cell),len(visited))
|
||||
for neighbor in maze.getNeighbors(current):
|
||||
if neighbor not in visited:
|
||||
visited.add(neighbor)
|
||||
parent[neighbor] = current
|
||||
stack.append(neighbor)
|
||||
return [], len(visited)
|
||||
|
||||
class AStarStrategy(PathFindingStrategy):
|
||||
def heuristic(self,cell,exit_cell):
|
||||
return (abs(cell.x - exit_cell.x) + abs(cell.y - exit_cell.y))
|
||||
def findPath(self, maze, start, exit_cell):
|
||||
pq = []
|
||||
heapq.heappush(pq,(0, id(start), start))
|
||||
parent = {}
|
||||
g_score = {start: 0}
|
||||
visited = set()
|
||||
while pq:
|
||||
_, _, current = (heapq.heappop(pq))
|
||||
if current in visited:
|
||||
continue
|
||||
visited.add(current)
|
||||
if current == exit_cell:
|
||||
return (self.restorePath(parent, start, exit_cell), len(visited))
|
||||
for neighbor in maze.getNeighbors(current):
|
||||
new_cost = (g_score[current]+ 1)
|
||||
if (neighbor not in g_score or new_cost < g_score[neighbor] ):
|
||||
g_score[neighbor] = new_cost
|
||||
parent[neighbor] = current
|
||||
priority = (new_cost + self.heuristic(neighbor, exit_cell))
|
||||
heapq.heappush(pq,(priority,id(neighbor),neighbor))
|
||||
return [], len(visited)
|
||||
BIN
romanovpv/task 2/docs/Отчет.docx
Normal file
BIN
romanovpv/task 2/docs/Отчет.docx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user