2026-rff_mp/BorisovMI/lab_2/docs/data/maze.py

271 lines
7.6 KiB
Python
Raw Normal View History

from abc import ABC, abstractclassmethod
from collections import deque
import heapq
import time
import os
import time
import csv
import random
class Cell:
def __init__(self, x, y):
self.x = x
self.y = y
self.isWall = False
self.isStart = False
self.isExit = False
def __eq__(self, other):
if other is None:
return False
return self.x == other.x and self.y == other.y
def __lt__(self, other):
if other is None:
return False
return (self.x, self.y) < (other.x, other.y)
def __hash__(self):
return hash((self.x, self.y))
def __repr__(self):
return f"Cell({self.x}, {self.y})"
def isPassable(self):
return not self.isWall
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 = None
self.exit = None
def getCell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.grid[x][y]
return None
def getNeighbors(self, cell):
neighbors = []
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
for dx, dy in directions:
neighbor = self.getCell(cell.x + dx, cell.y + dy)
if neighbor and neighbor.isPassable():
neighbors.append(neighbor)
return neighbors
def setStart(self, x, y):
cell = self.getCell(x, y)
if cell:
cell.isStart = True
self.start = cell
def setExit(self, x, y):
cell = self.getCell(x, y)
if cell:
cell.isExit = True
2026-05-19 19:26:42 +00:00
self.exit = cell
class MazeBuilder(ABC):
def buildFromFile(self, filename):
pass
class TextileMazeBuilder(MazeBuilder):
def buildFromFile(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 line in lines:
if len(line) != width:
raise ValueError("все строки одинаковой длины")
maze = Maze(width, height)
for y in range(height):
for x in range(width):
char = lines[y][x]
cell = maze.getCell(x, y)
if char == '#':
cell.isWall = True
elif char == ' ':
cell.isWall = False
elif char == 's':
cell.isWall = False
cell.isStart = True
maze.start = cell
elif char == 'e':
cell.isWall = False
cell.isExit = True
maze.exit = cell
else:
raise ValueError(f"неизв сим")
if maze.start is None:
raise ValueError("в лабиринте не найден старт")
if maze.exit is None:
raise ValueError("в лабиринте не найден выход")
return maze
class PathFindingStrategy:
def findPath(self, maze, start, exit):
pass
class BFSStrategy(PathFindingStrategy):
def findPath(self, maze, start, exit):
if exit is None:
return []
queue = deque([start])
visited = {start}
parent = {start: None}
while queue:
current = queue.popleft()
if current == exit:
return self._reconstruct_path(parent, start, exit)
for neighbor in maze.getNeighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
queue.append(neighbor)
return []
def _reconstruct_path(self, parent, start, exit):
path = []
current = exit
while current is not None:
path.append(current)
current = parent[current]
path.reverse()
return path
class DFSStrategy(PathFindingStrategy):
def findPath(self, maze, start, exit):
if exit is None:
return []
stack = [start]
visited = {start}
parent = {start: None}
while stack:
current = stack.pop()
if current == exit:
return self._reconstruct_path(parent, start, exit)
for neighbor in maze.getNeighbors(current):
if neighbor not in visited:
visited.add(neighbor)
parent[neighbor] = current
stack.append(neighbor)
return []
def _reconstruct_path(self, parent, start, exit):
path = []
current = exit
while current is not None:
path.append(current)
current = parent[current]
path.reverse()
return path
class AStrategy(PathFindingStrategy):
def _heuristic(self, cell, exit):
if exit is None:
return 0
return abs(cell.x - exit.x) + abs(cell.y - exit.y)
def findPath(self, maze, start, exit):
if exit is None:
return []
open_set = []
heapq.heappush(open_set, (0, start))
came_from = {start: None}
g_score = {start: 0}
while open_set:
current = heapq.heappop(open_set)[1]
if current == exit:
return self._reconstruct_path(came_from, start, exit)
for neighbor in maze.getNeighbors(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 = tentative_g + self._heuristic(neighbor, exit)
heapq.heappush(open_set, (f_score, neighbor))
return []
def _reconstruct_path(self, came_from, start, exit):
path = []
current = exit
while current is not None:
path.append(current)
current = came_from[current]
path.reverse()
return path
2026-05-20 21:41:30 +00:00
class SearchStats:
def __init__(self, time_ms=0, visited_cells=0, path_length=0):
self.time_ms = time_ms
self.visited_cells = visited_cells
self.path_length = path_length
def __str__(self):
return f"Время: {self.time_ms:.3f} мс | Посещено: {self.visited_cells} | Длина пути: {self.path_length}"
class MazeSolver:
def __init__(self, maze):
self.maze = maze
self.strategy = None
def setStrategy(self, strategy):
self.strategy = strategy
def solve(self):
if self.strategy is None:
raise ValueError("Стратегия не установлена")
start_time = time.perf_counter()
path = self.strategy.findPath(self.maze, self.maze.start, self.maze.exit)
end_time = time.perf_counter()
elapsed_ms = (end_time - start_time) * 1000
stats = SearchStats(
time_ms=elapsed_ms,
visited_cells=len(path),
path_length=len(path)
)
return path, stats