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