2026-rff_mp/groshevava/docs/data/builders.py

75 lines
2.7 KiB
Python
Raw Normal View History

# 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