2026-rff_mp/konnovaea/maze_solver.py

242 lines
6.8 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.

class Cell:
def __init__(self, x, y):
self.x = x
self.y = y
self.is_wall = False
self.is_start = False
self.is_exit = False
def is_passable(self):
return not self.is_wall
def __repr__(self):
return f"Cell({self.x}, {self.y})"
class Maze:
def __init__(self, width, height):
self.width = width
self.height = height
self.cells = []
self.start = None
self.exit = None
for y in range(height):
row = []
for x in range(width):
row.append(Cell(x, y))
self.cells.append(row)
def get_cell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.cells[y][x]
return None
def get_neighbors(self, cell):
neighbors = []
directions = [(0, -1), (0, 1), (-1, 0), (1,0)]
for dx, dy in directions:
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
def __repr__(self):
return f"Maze({self.width}x{self.height})"
from abc import ABC, abstractmethod
class MazeBuilder(ABC):
@abstractmethod
def build_from_file(self, filename):
pass
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\r') for line in lines]
height = len(lines)
width = len(lines[0]) if height > 0 else 0
for i, line in enumerate(lines):
if len(line) != width:
raise ValueError(f"Строка {i+1} имеет длину {len(line)}, ожидается {width}")
maze = Maze(width, height)
start = None
exit_cell = None
for y, line in enumerate(lines):
for x, ch in enumerate(line):
cell = maze.get_cell(x, y)
if ch == '#':
cell.is_wall = True
elif ch == ' ':
cell.is_wall = False
elif ch == 'S':
cell.is_wall = False
cell.is_start = True
start = cell
elif ch == 'E':
cell.is_wall = False
cell.ia_exit = True
exit_cell = cell
else:
cell.is_wall = True
if start is None:
raise ValueError("В лабиринте не найден старт (S)")
if exit_cell is None:
raise ValueError("В лабиринте не найден выход (E)")
maze.start = start
maze.exit = exit_cell
return maze
from collections import deque
import heapq
from abc import ABC, abstractmethod
class PathfindingStrategy(ABC):
@abstractmethod
def find_path(self, maze, start, exit):
pass
class BFSStrategy(PathfindingStrategy):
def find_path(self, maze, start, exit):
if start is None or exit is None:
return []
queue = deque()
queue.append((start, [start]))
visited = set()
visited.add(start)
while queue:
current, path = queue.popleft()
if current == exit:
return path
neighbors = maze.get_neighbors(current)
for neighbor in neighbors:
if neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, path + [neighbor]))
return []
class DFSStrategy(PathfindingStrategy):
def find_path(self, maze, start, exit):
if start is None or exit is None:
return []
stack = [(start, [start])]
visited = set()
visited.add(start)
while stack:
current, path = stack.pop()
if current == exit:
return path
for neighbor in maze.get_neighbors(current):
if neighbor not in visited:
visited.add(neighbor)
stack.append((neighbor, path + [neighbor]))
return []
class AStartStrategy(PathfindingStrategy):
def _heuristic(self, cell, exit):
return abs(cell.x - exit.x) + abs(cell.y -exit.y)
def find_path(self, maze, start, exit):
if start is None or exit is None:
return []
counter = 0
heap = []
heapq.heappush(heap, (self._heuristic(start, exit), counter, start, [start]))
g_score = {start: 0}
visited = set()
while heap:
f_score, _, current, path = heapq.heappop(heap)
if current in visited:
continue
visited.add(current)
if current == exit:
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]:
g_score[neighbor] = tentative_g
f_score = tentative_g + self._heuristic(neighbor, exit)
counter += 1
heapq.heappush(heap, (f_score, counter, neighbor, path + [neighbor]))
return []
#тест
if __name__ == "__main__":
builder = TextFileMazeBuilder()
maze = builder.build_from_file("maze1.txt")
print("Лабиринт загружен")
print(f"Старт: {maze.start}")
print(f"Выход: {maze.exit}")
# Проверяем, что старт и выход проходимые
print(f"Старт проходим: {maze.start.is_passable()}")
print(f"Выход проходим: {maze.exit.is_passable()}")
# Проверяем соседей старта
neighbors = maze.get_neighbors(maze.start)
print(f"Соседи старта: {neighbors}")
# Тестируем BFS
bfs = BFSStrategy()
path = bfs.find_path(maze, maze.start, maze.exit)
print(f"BFS путь: {[f'({c.x},{c.y})' for c in path]}")
print(f"BFS длина пути: {len(path)}")
# Тестируем DFS
dfs = DFSStrategy()
path = dfs.find_path(maze, maze.start, maze.exit)
print(f"DFS путь: {[f'({c.x},{c.y})' for c in path]}")
print(f"DFS длина пути: {len(path)}")
# Тестируем A*
astar = AStartStrategy()
path = astar.find_path(maze, maze.start, maze.exit)
print(f"A* путь: {[f'({c.x},{c.y})' for c in path]}")
print(f"A* длина пути: {len(path)}")