from typing import Optional from source.settings import cell_mapping class Cell: """Класс отвечает за хранение характеристик поля лабиринта""" def __init__( self, x: int, y: int, is_wall: bool = False, is_start: bool = False, is_exit: bool = False, ): """ :param x: Координата поля по оси X :type x: int :param y: Координата поля по оси Y :type y: int :param is_wall: Является ли поле **стеной**, defaults to False :type is_wall: bool, optional :param is_start: Является ли поле **началом**, defaults to False :type is_start: bool, optional :param is_exit: Является ли поле **концом**, defaults to False :type is_exit: bool, optional """ self.x = x self.y = y self._is_wall = is_wall self._is_start = is_start self._is_exit = is_exit def is_possible(self) -> bool: """Проверка возможности перемещения в это поле :return: Если перемещение возможно, то `True`, иначе `False` :rtype: bool """ return not self.is_wall @property def is_wall(self): return self._is_wall @property def is_start(self): return self._is_start @property def is_exit(self): return self._is_exit def _clear_flags(self) -> None: """Обнуляет все флаги поля""" self._is_start = False self._is_exit = False self._is_wall = False @is_wall.setter def is_wall(self, value: bool) -> None: if value: self._clear_flags() self._is_wall = value @is_start.setter def is_start(self, value: bool) -> None: if value: self._clear_flags() self._is_start = value @is_exit.setter def is_exit(self, value: bool) -> None: if value: self._clear_flags() self._is_exit = value def _get_type_cell(self) -> str: if self._is_wall: type_cell = cell_mapping.get('wall') elif self._is_start: type_cell = cell_mapping.get('start') elif self._is_exit: type_cell = cell_mapping.get('exit') else: type_cell = cell_mapping.get('empty') return type_cell def __str__(self) -> str: return self._get_type_cell() def __repr__(self): return f"Cell: (x={self.x}, y={self.y}), '{self._get_type_cell()}'" class Maze: def __init__( self, size: tuple[int, int] = (10, 10) ): # Установка размеров лабиринта self._width = size[0] self._height = size[1] # Создание двумерного списка лабиринта self._map = [ [Cell(x, y) for x in range(self._width)] for y in range(self._height) ] def _check_point_in_map(self, x: int, y: int) -> bool: """Проверка нахождения точки в границах поля Args: x (int): Координата точки в оси X y (int): Координата точки в оси Y Returns: bool: True если поля в поле, иначе False """ return (0 <= x < self._width) and (0 <= y < self._height) def get_cell(self, x: int, y: int) -> Optional[Cell]: """Получение значения поля по координате в лабиринте Args: x (int): Координата точки в оси X y (int): Координата точки в оси Y Returns: Optional[Cell]: Объект поля, при его наличии """ if not self._check_point_in_map(x, y): return None return self._map[y][x] def get_neighbors(self, x: int, y: int) -> Optional[list[Cell]]: """Получение соседних полей относительно заданного поля Под соседями поля в лабиринте имеется виду клетки сверху, справа, снизу и слева относительно её. Если точка находится за границами, то будет возвращено `None` Args: x (int): Координата точки в оси X y (int): Координата точки в оси Y Returns: Optional[list[Cell]]: Список соседних полей """ if not self._check_point_in_map(x, y): return None list() vector_x = [0, 1, 0, -1] vector_y = [1, 0, -1, 0] neighbors = [] for vec_x, vec_y in zip(vector_x, vector_y): temp_x, temp_y = x + vec_x, y + vec_y value = self.get_cell(temp_x, temp_y) if value is not None and value.is_possible(): neighbors.append(value) return neighbors def __getitem__(self, index: tuple[int, int]) -> Cell: row, col = index if not self._check_point_in_map(col, row): raise IndexError(f"Поле с индексом ({row}, {col}) выходит за пределы лабиринта") return self._map[row][col] def __setitem__(self, index: tuple[int, int], value: str) -> None: row, col = index if not self._check_point_in_map(col, row): raise IndexError(f"Поле с индексом ({row}, {col}) выходит за пределы лабиринта") cell = self._map[row][col] cell_type = None for type_name, symbol in cell_mapping.items(): if symbol == value: cell_type = type_name break if cell_type is None: raise ValueError(f"Значение '{value}' не соответствует ни одному типу клетки") if cell_type == "empty": cell._clear_flags() else: setattr(cell, f"is_{cell_type}", True) def __str__(self) -> str: result = "" for y in range(self._height): for x in range(self._width): result += str(self[y, x]) result += '\n' return result