75 lines
2.7 KiB
Python
75 lines
2.7 KiB
Python
|
|
# maze_solver/builders.py
|
|||
|
|
from abc import ABC, abstractmethod
|
|||
|
|
from models import Cell, Maze
|
|||
|
|
|
|||
|
|
|
|||
|
|
class MazeBuilder(ABC):
|
|||
|
|
"""Интерфейс строителя лабиринта."""
|
|||
|
|
@abstractmethod
|
|||
|
|
def buildFromFile(self, filename: str) -> Maze:
|
|||
|
|
"""Строит объект Maze из файла."""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
class TextFileMazeBuilder(MazeBuilder):
|
|||
|
|
"""Строитель для текстового формата: ■ стена, ' ' проход, S старт, E выход."""
|
|||
|
|
|
|||
|
|
# Поддерживаемые символы стен
|
|||
|
|
WALL_SYMBOLS = {'#', '■', '█', '▓', '▒', '░'}
|
|||
|
|
|
|||
|
|
def __init__(self, require_exit: bool = True):
|
|||
|
|
"""
|
|||
|
|
Args:
|
|||
|
|
require_exit: Если False, позволяет создавать лабиринты без выхода
|
|||
|
|
"""
|
|||
|
|
self.require_exit = require_exit
|
|||
|
|
|
|||
|
|
def buildFromFile(self, filename: str) -> Maze:
|
|||
|
|
with open(filename, 'r', encoding='utf-8') as f:
|
|||
|
|
lines = f.readlines()
|
|||
|
|
|
|||
|
|
# Убираем символы новой строки и пустые строки в конце файла
|
|||
|
|
cleaned_lines = [line.rstrip('\n') for line in lines if line.strip() != '']
|
|||
|
|
|
|||
|
|
if not cleaned_lines:
|
|||
|
|
raise ValueError("Файл лабиринта пуст")
|
|||
|
|
|
|||
|
|
height = len(cleaned_lines)
|
|||
|
|
width = len(cleaned_lines[0])
|
|||
|
|
|
|||
|
|
grid = []
|
|||
|
|
start_cell = None
|
|||
|
|
exit_cell = None
|
|||
|
|
|
|||
|
|
for y, line in enumerate(cleaned_lines):
|
|||
|
|
row = []
|
|||
|
|
if len(line) != width:
|
|||
|
|
raise ValueError(
|
|||
|
|
f"Строка {y} имеет длину {len(line)}, ожидалось {width}. "
|
|||
|
|
f"Лабиринт должен быть прямоугольным."
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
for x, char in enumerate(line):
|
|||
|
|
is_wall = char in self.WALL_SYMBOLS
|
|||
|
|
cell = Cell(x, y, is_wall)
|
|||
|
|
|
|||
|
|
if char == 'S':
|
|||
|
|
cell.isStart = True
|
|||
|
|
start_cell = cell
|
|||
|
|
elif char == 'E':
|
|||
|
|
cell.isExit = True
|
|||
|
|
exit_cell = cell
|
|||
|
|
|
|||
|
|
row.append(cell)
|
|||
|
|
grid.append(row)
|
|||
|
|
|
|||
|
|
if not start_cell:
|
|||
|
|
raise ValueError("В лабиринте не найдена стартовая позиция (S)")
|
|||
|
|
|
|||
|
|
if self.require_exit and not exit_cell:
|
|||
|
|
raise ValueError("В лабиринте не найдена выходная позиция (E)")
|
|||
|
|
|
|||
|
|
maze = Maze(width, height, grid)
|
|||
|
|
maze.start = start_cell
|
|||
|
|
maze.exit = exit_cell
|
|||
|
|
return maze
|