72 lines
2.5 KiB
Python
72 lines
2.5 KiB
Python
"""
|
|
Этап 2: Паттерн Builder — загрузка лабиринта из файла.
|
|
|
|
Формат файла:
|
|
# — стена
|
|
' ' (пробел) — проход
|
|
S — старт
|
|
E — выход
|
|
"""
|
|
|
|
from abc import ABC, abstractmethod
|
|
from maze_model import Cell, Maze
|
|
|
|
|
|
class MazeBuilder(ABC):
|
|
"""Интерфейс строителя лабиринта (паттерн Builder)."""
|
|
|
|
@abstractmethod
|
|
def build_from_file(self, filename: str) -> Maze:
|
|
"""Читает файл и возвращает готовый объект Maze."""
|
|
...
|
|
|
|
|
|
class TextFileMazeBuilder(MazeBuilder):
|
|
"""
|
|
Конкретный строитель: читает текстовый файл и строит Maze.
|
|
|
|
Зачем Builder?
|
|
Процесс построения многошаговый: чтение, парсинг, валидация,
|
|
поиск старта/выхода, создание объектов Cell. Builder скрывает
|
|
эту сложность от клиента. Чтобы добавить JSON-формат достаточно
|
|
написать JsonMazeBuilder, не трогая остальной код.
|
|
"""
|
|
|
|
def build_from_file(self, filename: str) -> Maze:
|
|
with open(filename, "r", encoding="utf-8") as f:
|
|
lines = f.read().splitlines()
|
|
|
|
if not lines:
|
|
raise ValueError("Файл лабиринта пуст.")
|
|
|
|
height = len(lines)
|
|
width = max(len(line) for line in lines)
|
|
|
|
# Дополняем строки до одинаковой длины (стенами)
|
|
lines = [line.ljust(width, "#") for line in lines]
|
|
|
|
cells: list[list[Cell]] = []
|
|
start: Cell | None = None
|
|
exit_cell: Cell | None = None
|
|
|
|
for y, line in enumerate(lines):
|
|
row = []
|
|
for x, ch in enumerate(line):
|
|
is_wall = ch == "#"
|
|
is_start = ch == "S"
|
|
is_exit = ch == "E"
|
|
cell = Cell(x, y, is_wall=is_wall, is_start=is_start, is_exit=is_exit)
|
|
if is_start:
|
|
start = cell
|
|
if is_exit:
|
|
exit_cell = cell
|
|
row.append(cell)
|
|
cells.append(row)
|
|
|
|
if start is None:
|
|
raise ValueError("Лабиринт не содержит стартовой клетки (S).")
|
|
if exit_cell is None:
|
|
raise ValueError("Лабиринт не содержит выхода (E).")
|
|
|
|
return Maze(width, height, cells, start, exit_cell)
|