2026-rff_mp/skorohodovsa/task_2/source/models/base.py
SerKin0 f7577f803c [2] Добавлено:
- Тесты на классы Cell, Maze
- Алгоритмы поиска пути: BFS, DFS, Astar
2026-05-24 17:17:02 +03:00

242 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
):
"""Инициализирует клетку с заданными координатами и типом.
Args:
x: Координата клетки по оси X.
y: Координата клетки по оси Y.
is_wall: Если True — клетка является стеной.
is_start: Если True — клетка является стартом.
is_exit: Если True — клетка является выходом.
"""
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:
"""Проверяет, можно ли переместиться в эту клетку.
Returns:
True, если клетка не является стеной, иначе False.
"""
return not self._is_wall
@property
def is_wall(self) -> bool:
"""True, если клетка является стеной."""
return self._is_wall
@property
def is_start(self) -> bool:
"""True, если клетка является стартовой позицией."""
return self._is_start
@property
def is_exit(self) -> bool:
"""True, если клетка является выходом из лабиринта."""
return self._is_exit
def _clear_flags(self) -> None:
"""Сбрасывает все флаги типа клетки в False."""
self._is_wall = False
self._is_start = False
self._is_exit = False
@is_wall.setter
def is_wall(self, value: bool) -> None:
"""Устанавливает флаг стены, сбрасывая остальные типы при value=True.
Args:
value: Новое значение флага стены.
"""
if value:
self._clear_flags()
self._is_wall = value
@is_start.setter
def is_start(self, value: bool) -> None:
"""Устанавливает флаг старта, сбрасывая остальные типы при value=True.
Args:
value: Новое значение флага старта.
"""
if value:
self._clear_flags()
self._is_start = value
@is_exit.setter
def is_exit(self, value: bool) -> None:
"""Устанавливает флаг выхода, сбрасывая остальные типы при value=True.
Args:
value: Новое значение флага выхода.
"""
if value:
self._clear_flags()
self._is_exit = value
def _get_type_cell(self) -> str:
"""Возвращает символ клетки согласно cell_mapping.
Returns:
Строковый символ, соответствующий текущему типу клетки.
"""
if self._is_wall:
return cell_mapping["wall"]
if self._is_start:
return cell_mapping["start"]
if self._is_exit:
return cell_mapping["exit"]
return cell_mapping["empty"]
def __str__(self) -> str:
return self._get_type_cell()
def __repr__(self) -> str:
return f"Cell(x={self.x}, y={self.y}, '{self._get_type_cell()}')"
class Maze:
"""Представляет двумерный лабиринт из клеток Cell.
Лабиринт хранится как список списков клеток. Доступ к отдельным
клеткам и их изменение возможны через индексацию вида maze[row, col].
"""
def __init__(self, size: tuple[int, int] = (10, 10)):
"""Создаёт пустой лабиринт заданного размера.
Args:
size: Кортеж (width, height) — ширина и высота лабиринта в клетках.
"""
self._width, self._height = size
self._map: list[list[Cell]] = [
[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: Координата по оси X.
y: Координата по оси Y.
Returns:
True, если точка (x, y) находится внутри лабиринта.
"""
return 0 <= x < self._width and 0 <= y < self._height
def get_cell(self, x: int, y: int) -> Optional[Cell]:
"""Возвращает клетку по координатам или None, если координаты вне границ.
Args:
x: Координата по оси X.
y: Координата по оси Y.
Returns:
Объект Cell, если координаты корректны, иначе None.
"""
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]]:
"""Возвращает список проходимых соседей клетки (вверх, вправо, вниз, влево).
Args:
x: Координата клетки по оси X.
y: Координата клетки по оси Y.
Returns:
Список проходимых соседних клеток, или None если (x, y) вне границ.
"""
if not self._check_point_in_map(x, y):
return None
deltas = ((0, 1), (1, 0), (0, -1), (-1, 0))
neighbors = []
for dx, dy in deltas:
cell = self.get_cell(x + dx, y + dy)
if cell is not None and cell.is_possible():
neighbors.append(cell)
return neighbors
@property
def shape(self) -> tuple[int, int]:
return self._height, self._width
def __getitem__(self, index: tuple[int, int]) -> Cell:
"""Возвращает клетку по индексу [row, col].
Args:
index: Кортеж (row, col) — строка и столбец.
Returns:
Объект Cell в позиции (row, col).
Raises:
IndexError: Если индекс выходит за пределы лабиринта.
"""
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] через символ из cell_mapping.
Args:
index: Кортеж (row, col) — строка и столбец.
value: Символ типа клетки согласно cell_mapping.
Raises:
IndexError: Если индекс выходит за пределы лабиринта.
ValueError: Если символ не найден в cell_mapping.
"""
row, col = index
if not self._check_point_in_map(col, row):
raise IndexError(f"Индекс ({row}, {col}) выходит за пределы лабиринта")
cell = self._map[row][col]
cell_type = next(
(t for t, s in cell_mapping.items() if s == value),
None,
)
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:
return "\n".join(
"".join(str(self._map[y][x]) for x in range(self._width))
for y in range(self._height)
)