[2] Добавление docstring к Cell, Maze
Добавлен интерфейс для алгоритмов
This commit is contained in:
parent
de1170df68
commit
535c706af8
49
skorohodovsa/task_2/source/algorithms/strategy.py
Normal file
49
skorohodovsa/task_2/source/algorithms/strategy.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from collections import deque
|
||||
from typing import Optional
|
||||
|
||||
from source.models.base import Maze, Cell
|
||||
|
||||
|
||||
class PathFindingStrategy(ABC):
|
||||
"""Интерфейс стратегии поиска пути в лабиринте."""
|
||||
|
||||
@abstractmethod
|
||||
def find_path(self, maze: Maze, start: Cell, exit: Cell) -> list[Cell]:
|
||||
"""Найти путь от start до exit.
|
||||
|
||||
Args:
|
||||
maze: Объект лабиринта.
|
||||
start: Стартовая клетка.
|
||||
exit: Целевая клетка.
|
||||
|
||||
Returns:
|
||||
Список клеток пути (от start до exit включительно).
|
||||
Пустой список, если путь не найден.
|
||||
"""
|
||||
|
||||
def _reconstruct_path(came_from: dict[Cell, Optional[Cell]], end: Cell) -> list[Cell]:
|
||||
"""Восстанавливает путь от старта до end, идя по came_from в обратном порядке.
|
||||
|
||||
Args:
|
||||
came_from: Словарь {клетка: родитель}, где родитель старта = None.
|
||||
end: Конечная клетка.
|
||||
|
||||
Returns:
|
||||
Список клеток пути от старта до end включительно.
|
||||
Пустой список, если end отсутствует в came_from.
|
||||
"""
|
||||
if end not in came_from:
|
||||
return []
|
||||
|
||||
path: list[Cell] = []
|
||||
current: Optional[Cell] = end
|
||||
while current is not None:
|
||||
path.append(current)
|
||||
current = came_from[current]
|
||||
path.reverse()
|
||||
return path
|
||||
|
||||
class BFS(PathFindingStrategy):
|
||||
def find_path(self, maze: Maze, start: Cell, exit: Cell) -> list[Cell]:
|
||||
pass
|
||||
|
|
@ -4,7 +4,12 @@ from source.settings import cell_mapping
|
|||
|
||||
|
||||
class Cell:
|
||||
"""Класс отвечает за хранение характеристик поля лабиринта"""
|
||||
"""Представляет одну клетку поля лабиринта.
|
||||
|
||||
Каждая клетка хранит свои координаты и один из четырёх возможных
|
||||
типов: стена, старт, выход или пустая клетка. Типы взаимоисключают
|
||||
друг друга: установка одного автоматически сбрасывает остальные.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
|
@ -14,17 +19,14 @@ class Cell:
|
|||
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
|
||||
"""Инициализирует клетку с заданными координатами и типом.
|
||||
|
||||
Args:
|
||||
x: Координата клетки по оси X.
|
||||
y: Координата клетки по оси Y.
|
||||
is_wall: Если True — клетка является стеной.
|
||||
is_start: Если True — клетка является стартом.
|
||||
is_exit: Если True — клетка является выходом.
|
||||
"""
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
|
@ -33,170 +35,204 @@ class Cell:
|
|||
self._is_exit = is_exit
|
||||
|
||||
def is_possible(self) -> bool:
|
||||
"""Проверка возможности перемещения в это поле
|
||||
"""Проверяет, можно ли переместиться в эту клетку.
|
||||
|
||||
:return: Если перемещение возможно, то `True`, иначе `False`
|
||||
:rtype: bool
|
||||
Returns:
|
||||
True, если клетка не является стеной, иначе False.
|
||||
"""
|
||||
return not self.is_wall
|
||||
|
||||
return not self._is_wall
|
||||
|
||||
@property
|
||||
def is_wall(self):
|
||||
def is_wall(self) -> bool:
|
||||
"""True, если клетка является стеной."""
|
||||
return self._is_wall
|
||||
|
||||
|
||||
@property
|
||||
def is_start(self):
|
||||
def is_start(self) -> bool:
|
||||
"""True, если клетка является стартовой позицией."""
|
||||
return self._is_start
|
||||
|
||||
|
||||
@property
|
||||
def is_exit(self):
|
||||
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
|
||||
self._is_wall = 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:
|
||||
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
|
||||
|
||||
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):
|
||||
return f"Cell: (x={self.x}, y={self.y}), '{self._get_type_cell()}'"
|
||||
def __repr__(self) -> str:
|
||||
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]
|
||||
"""Представляет двумерный лабиринт из клеток Cell.
|
||||
|
||||
# Создание двумерного списка лабиринта
|
||||
self._map = [
|
||||
[Cell(x, y) for x in range(self._width)] for y in range(self._height)
|
||||
Лабиринт хранится как список списков клеток. Доступ к отдельным
|
||||
клеткам и их изменение возможны через индексацию вида 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 (int): Координата точки в оси X
|
||||
y (int): Координата точки в оси Y
|
||||
x: Координата по оси X.
|
||||
y: Координата по оси Y.
|
||||
|
||||
Returns:
|
||||
bool: True если поля в поле, иначе False
|
||||
True, если точка (x, y) находится внутри лабиринта.
|
||||
"""
|
||||
return (0 <= x < self._width) and (0 <= y < self._height)
|
||||
return 0 <= x < self._width and 0 <= y < self._height
|
||||
|
||||
def get_cell(self, x: int, y: int) -> Optional[Cell]:
|
||||
"""Получение значения поля по координате в лабиринте
|
||||
"""Возвращает клетку по координатам или None, если координаты вне границ.
|
||||
|
||||
Args:
|
||||
x (int): Координата точки в оси X
|
||||
y (int): Координата точки в оси Y
|
||||
x: Координата по оси X.
|
||||
y: Координата по оси Y.
|
||||
|
||||
Returns:
|
||||
Optional[Cell]: Объект поля, при его наличии
|
||||
Объект 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]]:
|
||||
"""Получение соседних полей относительно заданного поля
|
||||
|
||||
Под соседями поля в лабиринте имеется виду клетки сверху, справа,
|
||||
снизу и слева относительно её. Если точка находится за границами,
|
||||
то будет возвращено `None`
|
||||
"""Возвращает список проходимых соседей клетки (вверх, вправо, вниз, влево).
|
||||
|
||||
Args:
|
||||
x (int): Координата точки в оси X
|
||||
y (int): Координата точки в оси Y
|
||||
x: Координата клетки по оси X.
|
||||
y: Координата клетки по оси Y.
|
||||
|
||||
Returns:
|
||||
Optional[list[Cell]]: Список соседних полей
|
||||
Список проходимых соседних клеток, или None если (x, y) вне границ.
|
||||
"""
|
||||
if not self._check_point_in_map(x, y):
|
||||
return None
|
||||
list()
|
||||
vector_x = [0, 1, 0, -1]
|
||||
vector_y = [1, 0, -1, 0]
|
||||
|
||||
deltas = ((0, 1), (1, 0), (0, -1), (-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)
|
||||
|
||||
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
|
||||
|
||||
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}) выходит за пределы лабиринта")
|
||||
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}) выходит за пределы лабиринта")
|
||||
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
|
||||
cell_type = next(
|
||||
(t for t, s in cell_mapping.items() if s == value),
|
||||
None,
|
||||
)
|
||||
|
||||
if cell_type is None:
|
||||
raise ValueError(f"Значение '{value}' не соответствует ни одному типу клетки")
|
||||
raise ValueError(f"Символ '{value}' не соответствует ни одному типу клетки")
|
||||
|
||||
if cell_type == "empty":
|
||||
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
|
||||
return '\n'.join(
|
||||
''.join(str(self._map[y][x]) for x in range(self._width))
|
||||
for y in range(self._height)
|
||||
)
|
||||
Loading…
Reference in New Issue
Block a user