diff --git a/SavelevMI/docs/data/2-nd-exersize/maze_core.py b/SavelevMI/docs/data/2-nd-exersize/maze_core.py new file mode 100644 index 0000000..a605bfb --- /dev/null +++ b/SavelevMI/docs/data/2-nd-exersize/maze_core.py @@ -0,0 +1,146 @@ +# Модель лабиринта: клетки, карта и загрузка из файла (Builder pattern) + +class Cell: + + def __init__(self, x, y): + self._x = x + self._y = y + self._is_wall = False + self._is_start = False + self._is_exit = False + + @property + def x(self): + return self._x + + @property + def y(self): + return self._y + + @property + def is_wall(self): + return self._is_wall + + @is_wall.setter + def is_wall(self, value): + self._is_wall = value + + @property + def is_start(self): + return self._is_start + + @is_start.setter + def is_start(self, value): + self._is_start = value + + @property + def is_exit(self): + return self._is_exit + + @is_exit.setter + def is_exit(self, value): + self._is_exit = value + + def is_passable(self): + return not self._is_wall + + +class Maze: + + def __init__(self, width, height): + self._width = width + self._height = height + self._cells = [[Cell(x, y) for x in range(width)] for y in range(height)] + self._start = None + self._exit = None + + @property + def width(self): + return self._width + + @property + def height(self): + return self._height + + @property + def start(self): + return self._start + + @property + def exit(self): + return self._exit + + def get_cell(self, x, y): + if 0 <= x < self._width and 0 <= y < self._height: + return self._cells[y][x] + return None + + def set_cell(self, x, y, cell_type): + cell = self.get_cell(x, y) + if cell is None: + return + + if cell_type == 'wall': + cell.is_wall = True + elif cell_type == 'start': + if self._start: + self._start.is_start = False + cell.is_start = True + cell.is_wall = False + self._start = cell + elif cell_type == 'exit': + if self._exit: + self._exit.is_exit = False + cell.is_exit = True + cell.is_wall = False + self._exit = cell + elif cell_type == 'path': + cell.is_wall = False + + def get_neighbors(self, cell): + neighbors = [] + directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] # up, down, left, right + for dx, dy in directions: + nx, ny = cell.x + dx, cell.y + dy + neighbor = self.get_cell(nx, ny) + if neighbor and neighbor.is_passable(): + neighbors.append(neighbor) + return neighbors + + +class MazeBuilder: + + def build_from_file(self, filename): + raise NotImplementedError("Must be implemented in subclass") + + +class TextFileMazeBuilder(MazeBuilder): + + def build_from_file(self, filename): + with open(filename, 'r') as f: + lines = [line.rstrip('\n') for line in f.readlines()] + + height = len(lines) + width = max(len(line) for line in lines) if height > 0 else 0 + + start_count = 0 + exit_count = 0 + maze = Maze(width, height) + + for y, line in enumerate(lines): + for x, ch in enumerate(line): + if ch == "#": + maze.set_cell(x, y, "wall") + elif ch == "S": + maze.set_cell(x, y, "start") + start_count += 1 + elif ch == "E": + maze.set_cell(x, y, "exit") + exit_count += 1 + elif ch == " ": + maze.set_cell(x, y, "path") + + if start_count != 1 or exit_count != 1: + raise ValueError(f"Лабиринт должен иметь ровно один вход S и один выход E. Найдено: S={start_count}, E={exit_count}") + + return maze \ No newline at end of file