observer_command
This commit is contained in:
parent
d0b791287f
commit
797c260aaa
|
|
@ -8,63 +8,34 @@ class MazeBuilder(ABC):
|
|||
|
||||
class TextFileMazeBuilder(MazeBuilder):
|
||||
def buildFromFile(self, filename):
|
||||
with open(
|
||||
filename,
|
||||
"r",
|
||||
encoding="utf-8"
|
||||
) as file:
|
||||
lines = [
|
||||
line.rstrip("\n")
|
||||
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
|
||||
)
|
||||
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
|
||||
)
|
||||
cell = Cell(x, y, is_wall=True)
|
||||
elif symbol == "S":
|
||||
cell = Cell(
|
||||
x,
|
||||
y,
|
||||
is_start=True
|
||||
)
|
||||
cell = Cell(x, y, is_start=True)
|
||||
start_count += 1
|
||||
elif symbol == "E":
|
||||
cell = Cell(
|
||||
x,
|
||||
y,
|
||||
is_exit=True
|
||||
)
|
||||
cell = Cell(x, y, is_exit=True)
|
||||
exit_count += 1
|
||||
elif symbol == " ":
|
||||
cell = Cell(
|
||||
x,
|
||||
y
|
||||
)
|
||||
cell = Cell(x, y)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Неизвестный символ: {symbol}"
|
||||
)
|
||||
raise ValueError(f"Неизвестный символ: {symbol}")
|
||||
row.append(cell)
|
||||
maze.add_row(row)
|
||||
if start_count != 1:
|
||||
raise ValueError(
|
||||
"Должен быть ровно один старт S"
|
||||
)
|
||||
raise ValueError("Должен быть ровно один старт S")
|
||||
if exit_count != 1:
|
||||
raise ValueError(
|
||||
"Должен быть ровно один выход E"
|
||||
)
|
||||
raise ValueError("Должен быть ровно один выход E")
|
||||
return maze
|
||||
|
|
@ -1,14 +1,12 @@
|
|||
import model
|
||||
from builders import (TextFileMazeBuilder)
|
||||
from strategies import (
|
||||
BFSStrategy,
|
||||
DFSStrategy,
|
||||
AStarStrategy
|
||||
)
|
||||
from strategies import (BFSStrategy, DFSStrategy, AStarStrategy)
|
||||
from solver import (MazeSolver)
|
||||
from observer_command import ConsoleView, Player, MoveCommand
|
||||
import os
|
||||
|
||||
builder = TextFileMazeBuilder()
|
||||
maze = builder.buildFromFile("maze.txt")
|
||||
print("Лабиринт:\n")
|
||||
maze.printMaze()
|
||||
print("Выберете алгоритм")
|
||||
print("1 - BFS")
|
||||
|
|
@ -26,7 +24,45 @@ else:
|
|||
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)
|
||||
|
||||
|
|
@ -68,10 +68,7 @@ class Maze:
|
|||
nx = cell.x + dx
|
||||
ny = cell.y + dy
|
||||
neighbor = self.getCell(nx, ny)
|
||||
if (
|
||||
neighbor
|
||||
and neighbor.isPassable()
|
||||
):
|
||||
if (neighbor and neighbor.isPassable()):
|
||||
neighbors.append(neighbor)
|
||||
return neighbors
|
||||
def printMaze(self):
|
||||
|
|
|
|||
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
|
||||
|
|
@ -1,12 +1,7 @@
|
|||
import time
|
||||
|
||||
class SearchStats:
|
||||
def __init__(
|
||||
self,
|
||||
time_ms,
|
||||
visited_cells,
|
||||
path_length
|
||||
):
|
||||
def __init__(self, time_ms, visited_cells, path_length):
|
||||
self.time_ms = time_ms
|
||||
self.visited_cells = visited_cells
|
||||
self.path_length = path_length
|
||||
|
|
@ -23,40 +18,25 @@ class SearchStats:
|
|||
)
|
||||
|
||||
class MazeSolver:
|
||||
def __init__(
|
||||
self,
|
||||
maze,
|
||||
strategy
|
||||
):
|
||||
def __init__(self,maze, strategy):
|
||||
self.maze = maze
|
||||
self.strategy = strategy
|
||||
def setStrategy(
|
||||
self,
|
||||
strategy
|
||||
self.observers = []
|
||||
def setStrategy(self, strategy
|
||||
):
|
||||
self.strategy = strategy
|
||||
def solve(self):
|
||||
start_time = (
|
||||
time.perf_counter()
|
||||
)
|
||||
path, visited = (
|
||||
self.strategy.findPath(
|
||||
self.maze,
|
||||
self.maze.start,
|
||||
self.maze.exit
|
||||
)
|
||||
)
|
||||
end_time = (
|
||||
time.perf_counter()
|
||||
)
|
||||
time_ms = (
|
||||
(end_time-start_time)
|
||||
*1000
|
||||
)
|
||||
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
|
||||
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)
|
||||
|
|
@ -6,12 +6,7 @@ class PathFindingStrategy(ABC):
|
|||
@abstractmethod
|
||||
def findPath(self, maze, start, exit_cell):
|
||||
pass
|
||||
def restorePath(
|
||||
self,
|
||||
parent,
|
||||
start,
|
||||
exit_cell
|
||||
):
|
||||
def restorePath(self, parent, start, exit_cell):
|
||||
path = []
|
||||
current = exit_cell
|
||||
while current != start:
|
||||
|
|
@ -21,136 +16,58 @@ class PathFindingStrategy(ABC):
|
|||
path.reverse()
|
||||
return path
|
||||
class BFSStrategy(PathFindingStrategy):
|
||||
def findPath(
|
||||
self,
|
||||
maze,
|
||||
start,
|
||||
exit_cell
|
||||
):
|
||||
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)
|
||||
)
|
||||
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
|
||||
)
|
||||
visited.add(neighbor)
|
||||
parent[neighbor] = current
|
||||
queue.append(neighbor)
|
||||
return [], len(visited)
|
||||
|
||||
class DFSStrategy(PathFindingStrategy):
|
||||
def findPath(
|
||||
self,
|
||||
maze,
|
||||
start,
|
||||
exit_cell
|
||||
):
|
||||
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)
|
||||
)
|
||||
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
|
||||
)
|
||||
visited.add(neighbor)
|
||||
parent[neighbor] = current
|
||||
stack.append(neighbor)
|
||||
return [], len(visited)
|
||||
|
||||
class AStarStrategy(PathFindingStrategy):
|
||||
def heuristic(
|
||||
self,
|
||||
cell,
|
||||
exit_cell
|
||||
):
|
||||
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
|
||||
):
|
||||
def findPath(self, maze, start, exit_cell):
|
||||
pq = []
|
||||
heapq.heappush(
|
||||
pq,
|
||||
(
|
||||
0,
|
||||
id(start),
|
||||
start
|
||||
)
|
||||
)
|
||||
heapq.heappush(pq,(0, id(start), start))
|
||||
parent = {}
|
||||
g_score = {
|
||||
start: 0
|
||||
}
|
||||
g_score = {start: 0}
|
||||
visited = set()
|
||||
while pq:
|
||||
_, _, current = (
|
||||
heapq.heappop(
|
||||
pq
|
||||
)
|
||||
)
|
||||
_, _, current = (heapq.heappop(pq))
|
||||
if current in visited:
|
||||
continue
|
||||
visited.add(
|
||||
current
|
||||
)
|
||||
visited.add(current)
|
||||
if current == exit_cell:
|
||||
return (self.restorePath(
|
||||
parent,
|
||||
start,
|
||||
exit_cell),
|
||||
len(visited)
|
||||
)
|
||||
return (self.restorePath(parent, start, exit_cell), len(visited))
|
||||
for neighbor in maze.getNeighbors(current):
|
||||
new_cost = (
|
||||
g_score[current]
|
||||
+ 1
|
||||
)
|
||||
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
|
||||
)
|
||||
)
|
||||
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)
|
||||
Loading…
Reference in New Issue
Block a user