class Cell: def __init__(self, row, col, value): self.row = row self.col = col self.value = value def is_wall(self): return self.value == "#" def is_start(self): return self.value == "S" def is_finish(self): return self.value == "F" class Maze: def __init__(self, cells, start, finish): self.cells = cells self.start = start self.finish = finish self.height = len(cells) self.width = len(cells[0]) if cells else 0 def inside(self, row, col): return 0 <= row < self.height and 0 <= col < self.width def get_cell(self, row, col): if not self.inside(row, col): return None return self.cells[row][col] def is_free(self, row, col): cell = self.get_cell(row, col) return cell is not None and not cell.is_wall() def neighbors(self, row, col): variants = [ (row - 1, col), (row + 1, col), (row, col - 1), (row, col + 1) ] result = [] for next_row, next_col in variants: if self.is_free(next_row, next_col): result.append((next_row, next_col)) return result def draw(self, path=None): path_set = set(path) if path else set() lines = [] for row in range(self.height): line = "" for col in range(self.width): cell = self.cells[row][col] if (row, col) in path_set and not cell.is_start() and not cell.is_finish(): line += "*" else: line += cell.value lines.append(line) return "\n".join(lines) class MazeBuilder: def __init__(self): self.lines = [] def from_file(self, filename): with open(filename, "r", encoding="utf-8") as file: self.lines = [line.rstrip("\n") for line in file if line.strip()] return self def build(self): cells = [] start = None finish = None width = len(self.lines[0]) for row, line in enumerate(self.lines): if len(line) != width: raise ValueError("maze lines have different length") cell_row = [] for col, value in enumerate(line): cell = Cell(row, col, value) if cell.is_start(): start = (row, col) if cell.is_finish(): finish = (row, col) cell_row.append(cell) cells.append(cell_row) if start is None or finish is None: raise ValueError("maze must have start and finish") return Maze(cells, start, finish)