2026-rff_mp/AgapovaDS/docs/data/2-nd/maze.py

206 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import time
from collections import deque
import heapq
class Cell:
def __init__(self, x, y, is_wall=False, is_start=False, is_exit=False):
self.x = x
self.y = y
self.is_wall = is_wall
self.is_start = is_start
self.is_exit = is_exit
def is_passable(self):
return not self.is_wall
class Maze:
def __init__(self, width, height):
self.width = width
self.height = height
self.grid = [[Cell(x, y) for y in range(height)] for x in range(width)]
self.start_cell = None
self.exit_cell = None
def get_cell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.grid[x][y]
return None
def get_neighbors(self, cell):
neighbors = []
for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]:
nx, ny = cell.x + dx, cell.y + dy
neighbor = self.get_cell(nx, ny)
if neighbor and neighbor.is_passable():
neighbors.append(neighbor)
return neighbors
class MazeBuilder:
def build_from_file(self, filename):
raise NotImplementedError
class TextFileMazeBuilder(MazeBuilder):
def build_from_file(self, filename):
with open(filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
lines = [line.rstrip('\n') for line in lines if line.strip() != '']
if not lines:
raise ValueError("Файл пуст")
height = len(lines)
width = max(len(line) for line in lines)
maze = Maze(width, height)
for y, line in enumerate(lines):
for x, ch in enumerate(line):
if x >= width:
break
cell = maze.get_cell(x, y)
if ch == '#':
cell.is_wall = True
elif ch == 'S':
cell.is_start = True
maze.start_cell = cell
elif ch == 'E':
cell.is_exit = True
maze.exit_cell = cell
if maze.start_cell is None or maze.exit_cell is None:
raise ValueError("В лабиринте должны быть S и E")
return maze
class PathFindingStrategy:
def find_path(self, maze, start, exit):
raise NotImplementedError
class BFSStrategy(PathFindingStrategy):
def find_path(self, maze, start, exit):
if start == exit:
return [start]
queue = deque([start])
visited = {start}
parent = {start: None}
while queue:
current = queue.popleft()
if current == exit:
break
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
queue.append(neighbor)
if exit not in parent:
return []
path = []
step = exit
while step is not None:
path.append(step)
step = parent[step]
path.reverse()
return path
class DFSStrategy(PathFindingStrategy):
def find_path(self, maze, start, exit):
if start == exit:
return [start]
stack = [start]
visited = {start}
parent = {start: None}
while stack:
current = stack.pop()
if current == exit:
break
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
stack.append(neighbor)
if exit not in parent:
return []
path = []
step = exit
while step is not None:
path.append(step)
step = parent[step]
path.reverse()
return path
class AStarStrategy(PathFindingStrategy):
def heuristic(self, a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
def find_path(self, maze, start, exit):
if start == exit:
return [start]
open_set = []
heapq.heappush(open_set, (0, id(start), start))
came_from = {}
g_score = {start: 0}
f_score = {start: self.heuristic(start, exit)}
while open_set:
_, _, current = heapq.heappop(open_set)
if current == exit:
path = []
step = current
while step is not None:
path.append(step)
step = came_from.get(step)
path.reverse()
return path
for neighbor in maze.get_neighbors(current):
tentative_g = g_score[current] + 1
if neighbor not in g_score or tentative_g < g_score[neighbor]:
came_from[neighbor] = current
g_score[neighbor] = tentative_g
f_score[neighbor] = tentative_g + self.heuristic(neighbor, exit)
heapq.heappush(open_set, (f_score[neighbor], id(neighbor), neighbor))
return []
class MazeSolver:
def __init__(self, maze, strategy=None):
self.maze = maze
self.strategy = strategy
def set_strategy(self, strategy):
self.strategy = strategy
def solve(self):
if self.strategy is None:
raise ValueError("Стратегия не установлена")
start = self.maze.start_cell
exit_cell = self.maze.exit_cell
if start is None or exit_cell is None:
raise ValueError("Лабиринт не содержит старта или выхода")
start_time = time.perf_counter()
path = self.strategy.find_path(self.maze, start, exit_cell)
end_time = time.perf_counter()
elapsed_ms = (end_time - start_time) * 1000
return path, elapsed_ms
# Test search
if __name__ == '__main__':
builder = TextFileMazeBuilder()
maze = builder.build_from_file('test_maze.txt')
solver = MazeSolver(maze)
solver.set_strategy(BFSStrategy())
path, ms = solver.solve()
print("BFS путь:", [f"({c.x},{c.y})" for c in path])
print(f"Время: {ms:.3f} мс")
solver.set_strategy(DFSStrategy())
path, ms = solver.solve()
print("DFS путь:", [f"({c.x},{c.y})" for c in path])
print(f"Время: {ms:.3f} мс")
solver.set_strategy(AStarStrategy())
path, ms = solver.solve()
print("A* путь:", [f"({c.x},{c.y})" for c in path])
print(f"Время: {ms:.3f} мс")