from abc import ABC, abstractmethod from maze_model import Maze class MazeBuilder(ABC): @abstractmethod def build_from_file(self, filename: str) -> Maze: pass @abstractmethod def build_from_string(self, content: str) -> Maze: pass class TextFileMazeBuilder(MazeBuilder): def __init__(self): self._maze = None self._lines = [] self._start_found = False self._exit_found = False def build_from_file(self, filename: str) -> Maze: try: with open(filename, 'r', encoding='utf-8') as file: content = file.read() return self.build_from_string(content) except FileNotFoundError: raise FileNotFoundError(f"Файл не найден: {filename}") def build_from_string(self, content: str) -> Maze: self._reset() self._lines = [line.rstrip('\n\r') for line in content.split('\n')] while self._lines and not self._lines[-1].strip(): self._lines.pop() if not self._lines: raise ValueError("Пустой файл лабиринта") height = len(self._lines) width = max(len(line) for line in self._lines) for i, line in enumerate(self._lines): if len(line) != width: self._lines[i] = line.ljust(width) self._maze = Maze(width, height) for y, line in enumerate(self._lines): for x, char in enumerate(line): self._parse_cell(x, y, char) self._validate_maze() return self._maze def _reset(self): self._maze = None self._lines = [] self._start_found = False self._exit_found = False def _parse_cell(self, x: int, y: int, char: str): cell = self._maze.get_cell(x, y) if char == '#': cell.is_wall = True elif char == 'S': if self._start_found: raise ValueError(f"Найден второй старт в ({x}, {y})") self._maze.set_start(x, y) self._start_found = True elif char == 'E': if self._exit_found: raise ValueError(f"Найден второй выход в ({x}, {y})") self._maze.set_exit(x, y) self._exit_found = True elif char == ' ': pass else: raise ValueError(f"Неизвестный символ '{char}' в ({x}, {y})") def _validate_maze(self): if not self._start_found: raise ValueError("В лабиринте не найден старт (символ 'S')") if not self._exit_found: raise ValueError("В лабиринте не найден выход (символ 'E')")