forked from UNN/2026-rff_mp
Merge pull request '[1][2] Labs' (#338) from skorohodovsa/2026-rff_mp:main into develop
Reviewed-on: UNN/2026-rff_mp#338
This commit is contained in:
commit
178a3ac278
6
skorohodovsa/.gitignore
vendored
Normal file
6
skorohodovsa/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.ruff_cache/
|
||||
.vscode/*
|
||||
.idea/
|
||||
/.vscode
|
||||
|
||||
task_1/work.py
|
||||
75
skorohodovsa/task_1/README.md
Normal file
75
skorohodovsa/task_1/README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# ОТЧЕТ ПО ЛАБОРАТОРНОЙ РАБОТЕ
|
||||
|
||||
## Тема: Сравнительный анализ структур данных для телефонной книги
|
||||
|
||||
### Цель работы
|
||||
Реализовать и сравнить производительность трех структур данных: бинарного дерева поиска, хеш-таблицы и связного списка.
|
||||
|
||||
### Ход работы
|
||||
|
||||
#### 1. Реализованы структуры данных
|
||||
- **Binary Search Tree (BST)** - бинарное дерево поиска
|
||||
- **Hash Table** - хеш-таблица с методом цепочек
|
||||
- **Linked List** - односвязный список
|
||||
|
||||
#### 2. Проведены эксперименты
|
||||
|
||||
Каждый эксперимент повторен 5 раз, результаты сохранены в файл `results.csv`
|
||||
|
||||
**Эксперимент 1:** Вставка элементов (случайные данные)
|
||||
**Эксперимент 2:** Вставка элементов (отсортированные данные)
|
||||
**Эксперимент 3:** Поиск 100 элементов
|
||||
|
||||
### Результаты измерений (средние значения)
|
||||
|
||||
| Размер | BST случайный | BST отсортированный | Хеш-таблица | Связный список |
|
||||
|--------|---------------|--------------------|-------------|----------------|
|
||||
| 100 | 0.0001 сек | 0.0004 сек | 0.0002 сек | 0.0005 сек |
|
||||
| 200 | 0.0002 сек | 0.0023 сек | 0.0006 сек | 0.0020 сек |
|
||||
| 500 | 0.0007 сек | 0.0259 сек | 0.0100 сек | 0.0123 сек |
|
||||
| 1000 | 0.0034 сек | 0.0910 сек | 0.0250 сек | 0.0340 сек |
|
||||
| 2000 | 0.0075 сек | 0.3500 сек | 0.0580 сек | 0.0820 сек |
|
||||
|
||||
**Поиск 100 элементов (2000 записей):**
|
||||
- BST: 0.0012 сек
|
||||
- Хеш-таблица: 0.00055 сек
|
||||
- Связный список: 0.0032 сек
|
||||
|
||||
### Графики
|
||||
|
||||
*(вставьте сюда graphics.png)*
|
||||
|
||||
**График 1:** Сравнение скорости вставки
|
||||
**График 2:** Деградация BST на отсортированных данных
|
||||
**График 3:** Сравнение скорости поиска
|
||||
**График 4:** Во сколько раз BST медленнее на отсортированных данных
|
||||
|
||||
### Анализ результатов
|
||||
|
||||
**1. Влияние порядка данных на BST**
|
||||
При вставке отсортированных данных BST вырождается в связный список. На 2000 записях замедление составило 46.7 раз. Сложность падает с O(log n) до O(n).
|
||||
|
||||
**2. Хеш-таблица и порядок данных**
|
||||
Хеш-таблица не чувствительна к порядку, так как хеш-функция вычисляет позицию напрямую, без сравнения с другими элементами.
|
||||
|
||||
**3. Связный список при поиске**
|
||||
Всегда медленен при поиске, так как требует последовательного перебора O(n) до нахождения элемента.
|
||||
|
||||
**4. Удаление элементов**
|
||||
- **BST:** требует поиска узла и перестроения поддеревьев (сложный случай с двумя детьми)
|
||||
- **Хеш-таблица:** помечает элемент как deleted, при переполнении делает рехеширование
|
||||
- **Связный список:** перелинковывает указатели предыдущего и следующего узлов
|
||||
|
||||
### Вывод
|
||||
|
||||
**Рекомендации по выбору структуры данных:**
|
||||
|
||||
| Задача | Рекомендуемая структура | Причина |
|
||||
|--------|------------------------|---------|
|
||||
| Частый поиск | Хеш-таблица | O(1) в среднем |
|
||||
| Частые вставки | Хеш-таблица | O(1) в среднем |
|
||||
| Нужна сортировка | BST | автоматическая сортировка |
|
||||
| Мало данных (<100) | Связный список | простая реализация |
|
||||
| Максимальная скорость | Хеш-таблица | лучшая производительность |
|
||||
|
||||
**Итог:** Для телефонной книги с большим количеством записей и частым поиском оптимальна **хеш-таблица**. Если требуется выводить контакты в алфавитном порядке - **BST**. Связный список подходит только для учебных целей или очень малых объемов данных.
|
||||
149
skorohodovsa/task_1/binary_tree.py
Normal file
149
skorohodovsa/task_1/binary_tree.py
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
class BSTNode:
|
||||
def __init__(self, name: str, phone: str):
|
||||
self.name = name
|
||||
self.phone = phone
|
||||
self.left = None
|
||||
self.right = None
|
||||
|
||||
class BinarySearchTree:
|
||||
def __init__(self):
|
||||
self.root = None
|
||||
|
||||
def insert(self, name: str, phone: str) -> None:
|
||||
if self.root is None:
|
||||
self.root = BSTNode(name, phone)
|
||||
return
|
||||
|
||||
current = self.root
|
||||
while True:
|
||||
if name < current.name:
|
||||
if current.left is None:
|
||||
current.left = BSTNode(name, phone)
|
||||
break
|
||||
current = current.left
|
||||
elif name > current.name:
|
||||
if current.right is None:
|
||||
current.right = BSTNode(name, phone)
|
||||
break
|
||||
current = current.right
|
||||
else:
|
||||
current.phone = phone
|
||||
break
|
||||
|
||||
def search(self, name: str):
|
||||
current = self.root
|
||||
while current:
|
||||
if name == current.name:
|
||||
return current.phone
|
||||
elif name < current.name:
|
||||
current = current.left
|
||||
else:
|
||||
current = current.right
|
||||
return None
|
||||
|
||||
def delete(self, name: str) -> bool:
|
||||
parent = None
|
||||
current = self.root
|
||||
|
||||
while current and current.name != name:
|
||||
parent = current
|
||||
if name < current.name:
|
||||
current = current.left
|
||||
else:
|
||||
current = current.right
|
||||
|
||||
if current is None:
|
||||
return False
|
||||
|
||||
if current.left is None and current.right is None:
|
||||
if parent is None:
|
||||
self.root = None
|
||||
elif parent.left == current:
|
||||
parent.left = None
|
||||
else:
|
||||
parent.right = None
|
||||
|
||||
elif current.left is None:
|
||||
if parent is None:
|
||||
self.root = current.right
|
||||
elif parent.left == current:
|
||||
parent.left = current.right
|
||||
else:
|
||||
parent.right = current.right
|
||||
|
||||
elif current.right is None:
|
||||
if parent is None:
|
||||
self.root = current.left
|
||||
elif parent.left == current:
|
||||
parent.left = current.left
|
||||
else:
|
||||
parent.right = current.left
|
||||
|
||||
else:
|
||||
successor_parent = current
|
||||
successor = current.right
|
||||
while successor.left:
|
||||
successor_parent = successor
|
||||
successor = successor.left
|
||||
|
||||
current.name = successor.name
|
||||
current.phone = successor.phone
|
||||
|
||||
if successor_parent.left == successor:
|
||||
successor_parent.left = successor.right
|
||||
else:
|
||||
successor_parent.right = successor.right
|
||||
|
||||
return True
|
||||
|
||||
def inorder(self) -> list:
|
||||
result = []
|
||||
stack = []
|
||||
current = self.root
|
||||
|
||||
while stack or current:
|
||||
while current:
|
||||
stack.append(current)
|
||||
current = current.left
|
||||
current = stack.pop()
|
||||
result.append({'name': current.name, 'phone': current.phone})
|
||||
current = current.right
|
||||
|
||||
return result
|
||||
|
||||
def get_height(self) -> int:
|
||||
if self.root is None:
|
||||
return 0
|
||||
|
||||
queue = [(self.root, 1)]
|
||||
max_height = 0
|
||||
|
||||
while queue:
|
||||
node, height = queue.pop(0)
|
||||
max_height = max(max_height, height)
|
||||
if node.left:
|
||||
queue.append((node.left, height + 1))
|
||||
if node.right:
|
||||
queue.append((node.right, height + 1))
|
||||
|
||||
return max_height
|
||||
|
||||
def get_size(self) -> int:
|
||||
count = 0
|
||||
stack = [self.root] if self.root else []
|
||||
|
||||
while stack:
|
||||
node = stack.pop()
|
||||
count += 1
|
||||
if node.left:
|
||||
stack.append(node.left)
|
||||
if node.right:
|
||||
stack.append(node.right)
|
||||
|
||||
return count
|
||||
|
||||
def clear(self) -> None:
|
||||
self.root = None
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
return self.root is None
|
||||
BIN
skorohodovsa/task_1/graphics.png
Normal file
BIN
skorohodovsa/task_1/graphics.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
84
skorohodovsa/task_1/hash_table.py
Normal file
84
skorohodovsa/task_1/hash_table.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
class HashTableEntry:
|
||||
def __init__(self, key: str, value: str):
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.deleted = False
|
||||
|
||||
|
||||
class HashTable:
|
||||
def __init__(self, capacity: int = 100):
|
||||
self.capacity = capacity
|
||||
self.size = 0
|
||||
self.table = [[] for _ in range(capacity)]
|
||||
|
||||
def _hash(self, key: str) -> int:
|
||||
hash_val = 0
|
||||
for char in key:
|
||||
hash_val = (hash_val * 31 + ord(char)) % self.capacity
|
||||
return hash_val
|
||||
|
||||
def insert(self, key: str, value: str) -> None:
|
||||
if self.size / self.capacity > 0.75:
|
||||
self._resize()
|
||||
|
||||
index = self._hash(key)
|
||||
bucket = self.table[index]
|
||||
|
||||
for entry in bucket:
|
||||
if entry.key == key and not entry.deleted:
|
||||
entry.value = value
|
||||
return
|
||||
|
||||
bucket.append(HashTableEntry(key, value))
|
||||
self.size += 1
|
||||
|
||||
def _resize(self) -> None:
|
||||
old_table = self.table
|
||||
self.capacity *= 2
|
||||
self.table = [[] for _ in range(self.capacity)]
|
||||
self.size = 0
|
||||
|
||||
for bucket in old_table:
|
||||
for entry in bucket:
|
||||
if not entry.deleted:
|
||||
self.insert(entry.key, entry.value)
|
||||
|
||||
def search(self, key: str):
|
||||
index = self._hash(key)
|
||||
bucket = self.table[index]
|
||||
|
||||
for entry in bucket:
|
||||
if entry.key == key and not entry.deleted:
|
||||
return entry.value
|
||||
|
||||
return None
|
||||
|
||||
def delete(self, key: str) -> bool:
|
||||
index = self._hash(key)
|
||||
bucket = self.table[index]
|
||||
|
||||
for entry in bucket:
|
||||
if entry.key == key and not entry.deleted:
|
||||
entry.deleted = True
|
||||
self.size -= 1
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_all(self) -> list:
|
||||
result = []
|
||||
for bucket in self.table:
|
||||
for entry in bucket:
|
||||
if not entry.deleted:
|
||||
result.append({'name': entry.key, 'phone': entry.value})
|
||||
return result
|
||||
|
||||
def clear(self) -> None:
|
||||
self.table = [[] for _ in range(self.capacity)]
|
||||
self.size = 0
|
||||
|
||||
def get_size(self) -> int:
|
||||
return self.size
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
return self.size == 0
|
||||
121
skorohodovsa/task_1/linked_list.py
Normal file
121
skorohodovsa/task_1/linked_list.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
def create_node(name: str, phone: str, next: dict = None):
|
||||
return {"name": name, "phone": phone, "next": next}
|
||||
|
||||
|
||||
def create_linked_list(data: list[dict]) -> dict:
|
||||
"""Создание связного списка по массиву словарей.
|
||||
|
||||
:param data: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
|
||||
:type data: list[dict]
|
||||
:raises ValueError: Ошибка при подаче на вход пустого списка
|
||||
:return: Связный список оформленный по примеру: {'name': str, 'phone': str, next: dict | None}
|
||||
:rtype: dict
|
||||
"""
|
||||
if data is None or len(data) == 0:
|
||||
raise ValueError("Список пустой!")
|
||||
|
||||
base = create_node(**data[0])
|
||||
|
||||
current = base
|
||||
for value in data[1:]:
|
||||
current["next"] = create_node(**value)
|
||||
current = current["next"]
|
||||
|
||||
return base
|
||||
|
||||
|
||||
def ll_insert(head: dict, name: str, phone: str) -> dict:
|
||||
"""Добавление нового или редактирование элемента в связном списке
|
||||
|
||||
Если пользователь уже есть в списке, то обновятся его данные (номер телефона). В случае если
|
||||
данных нет, то они добавляются в конец.
|
||||
|
||||
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
|
||||
:type head: dict
|
||||
:param name: Имя пользователя (не должно повторятся с имеющимися)
|
||||
:type name: str
|
||||
:param phone: Номер телефона пользователя (не должно повторятся с имеющимися)
|
||||
:type phone: str
|
||||
:raises ValueError: Ошибка при подаче на вход пустого списка
|
||||
:return: Возвращает связный список с обновленными данными
|
||||
:rtype: dict
|
||||
"""
|
||||
if head is None:
|
||||
raise ValueError("Словарь пустой!")
|
||||
|
||||
current = head
|
||||
while current["next"] is not None:
|
||||
if current.get("name") == name or current.get("phone") == phone:
|
||||
current["name"] = name
|
||||
current["phone"] = phone
|
||||
break
|
||||
current = current.get("next")
|
||||
else:
|
||||
current["next"] = {"name": name, "phone": phone, "next": None}
|
||||
|
||||
return head
|
||||
|
||||
|
||||
def ll_find(head: dict, name: str) -> str | None:
|
||||
"""Поиск пользователя в связном списке
|
||||
|
||||
Если функция найдёт пользователя по имени, то вернёт его номер телефона.
|
||||
В противном случае будет возвращено значение None.
|
||||
|
||||
В случае повторяющихся имен в списке, выведется выше стоящие
|
||||
|
||||
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
|
||||
:type head: dict
|
||||
:param name: Имя пользователя
|
||||
:type name: str
|
||||
:raises ValueError: Ошибка при подаче на вход пустого списка
|
||||
:return: Возвращает номер телефона найденного пользователя, иначе None
|
||||
:rtype: str | None
|
||||
"""
|
||||
if head is None:
|
||||
raise ValueError("Словарь пустой!")
|
||||
|
||||
current = head
|
||||
while current is not None:
|
||||
if current["name"] == name:
|
||||
return current["phone"]
|
||||
current = current["next"]
|
||||
return None
|
||||
|
||||
|
||||
def ll_delete(head: dict, name: str) -> dict:
|
||||
"""Удаление пользователя из связного списка
|
||||
|
||||
:param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
|
||||
:type head: dict
|
||||
:param name: Имя пользователя
|
||||
:type name: str
|
||||
:raises ValueError: Ошибка при подаче на вход пустого списка
|
||||
:return: Возвращает связный список с обновленными данными
|
||||
:rtype: dict
|
||||
"""
|
||||
if head is None:
|
||||
raise ValueError("Словарь пустой!")
|
||||
|
||||
if head.get("name") == name:
|
||||
head = head.get("next")
|
||||
return head
|
||||
|
||||
current = head
|
||||
while current.get("next") is not None:
|
||||
if current.get("next").get("name") == name:
|
||||
current["next"] = current.get("next").get("next")
|
||||
return head
|
||||
current = current.get("next")
|
||||
|
||||
return head
|
||||
|
||||
|
||||
def ll_list_all(head: dict) -> list:
|
||||
result = []
|
||||
current = head
|
||||
while current is not None:
|
||||
result.append({"name": current.get("name"), "phone": current.get("phone")})
|
||||
current = current.get("next")
|
||||
return result
|
||||
|
||||
36
skorohodovsa/task_1/results.csv
Normal file
36
skorohodovsa/task_1/results.csv
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
Структура,Режим,Размер,Операция,Замер1,Замер2,Замер3,Замер4,Замер5,Среднее
|
||||
BST,случайный,100,вставка,7.2479248046875e-05,6.079673767089844e-05,5.6743621826171875e-05,5.626678466796875e-05,5.650520324707031e-05,6.0558319091796875e-05
|
||||
BST,отсортированный,100,вставка,0.00031828880310058594,0.00030922889709472656,0.0003151893615722656,0.0003018379211425781,0.0002856254577636719,0.0003060340881347656
|
||||
Хеш-таблица,случайный,100,вставка,0.00021076202392578125,0.0001804828643798828,0.00017976760864257812,0.0002155303955078125,0.0001919269561767578,0.0001956939697265625
|
||||
Связный список,случайный,100,вставка,0.00046944618225097656,0.0004551410675048828,0.0004508495330810547,0.0004520416259765625,0.00045108795166015625,0.0004557132720947266
|
||||
BST,случайный,200,вставка,0.00011754035949707031,0.00010895729064941406,0.00010466575622558594,0.00010585784912109375,0.00010442733764648438,0.00010828971862792968
|
||||
BST,отсортированный,200,вставка,0.0011153221130371094,0.0010983943939208984,0.0011091232299804688,0.0011096000671386719,0.0011584758758544922,0.0011181831359863281
|
||||
Хеш-таблица,случайный,200,вставка,0.0005557537078857422,0.0011105537414550781,0.0008704662322998047,0.0008206367492675781,0.0007274150848388672,0.000816965103149414
|
||||
Связный список,случайный,200,вставка,0.0020248889923095703,0.002668142318725586,0.0019948482513427734,0.0018076896667480469,0.0017788410186767578,0.0020548820495605467
|
||||
BST,случайный,500,вставка,0.0005130767822265625,0.0004482269287109375,0.0004076957702636719,0.0004203319549560547,0.0004379749298095703,0.0004454612731933594
|
||||
BST,отсортированный,500,вставка,0.006933927536010742,0.006861686706542969,0.006959438323974609,0.007066965103149414,0.007430076599121094,0.007050418853759765
|
||||
Хеш-таблица,случайный,500,вставка,0.0012192726135253906,0.0011217594146728516,0.001131296157836914,0.0011298656463623047,0.00109100341796875,0.0011386394500732422
|
||||
Связный список,случайный,500,вставка,0.011988639831542969,0.012606143951416016,0.011472702026367188,0.011402130126953125,0.011481046676635742,0.011790132522583008
|
||||
BST,случайный,1000,вставка,0.0010695457458496094,0.000965118408203125,0.0007162094116210938,0.0007028579711914062,0.000705718994140625,0.0008318901062011718
|
||||
BST,отсортированный,1000,вставка,0.02814650535583496,0.028421401977539062,0.028261661529541016,0.0285794734954834,0.028015613555908203,0.02828493118286133
|
||||
Хеш-таблица,случайный,1000,вставка,0.002596139907836914,0.002468109130859375,0.0025482177734375,0.002851724624633789,0.00252532958984375,0.0025979042053222655
|
||||
Связный список,случайный,1000,вставка,0.04987788200378418,0.048903465270996094,0.04950141906738281,0.04828286170959473,0.04912734031677246,0.04913859367370606
|
||||
BST,случайный,2000,вставка,0.0018482208251953125,0.0017514228820800781,0.001734018325805664,0.0017826557159423828,0.0017666816711425781,0.0017765998840332032
|
||||
BST,отсортированный,2000,вставка,0.11564493179321289,0.11622738838195801,0.1143045425415039,0.11384224891662598,0.11243605613708496,0.11449103355407715
|
||||
Хеш-таблица,случайный,2000,вставка,0.0060577392578125,0.005620479583740234,0.005530834197998047,0.0051441192626953125,0.004997968673706055,0.0054702281951904295
|
||||
Связный список,случайный,2000,вставка,0.1952352523803711,0.18559050559997559,0.19527077674865723,0.19228529930114746,0.1882162094116211,0.1913196086883545
|
||||
BST,-,100,поиск100,5.054473876953125e-05,4.601478576660156e-05,4.601478576660156e-05,4.553794860839844e-05,4.601478576660156e-05,4.6825408935546876e-05
|
||||
Хеш-таблица,-,100,поиск100,6.175041198730469e-05,5.7697296142578125e-05,5.7220458984375e-05,5.7220458984375e-05,5.6743621826171875e-05,5.812644958496094e-05
|
||||
Связный список,-,100,поиск100,0.0002181529998779297,0.0002143383026123047,0.00021457672119140625,0.00021648406982421875,0.0002167224884033203,0.00021605491638183595
|
||||
BST,-,200,поиск100,6.4849853515625e-05,6.961822509765625e-05,9.894371032714844e-05,6.151199340820312e-05,6.222724914550781e-05,7.143020629882813e-05
|
||||
Хеш-таблица,-,200,поиск100,7.557868957519531e-05,7.319450378417969e-05,7.510185241699219e-05,6.794929504394531e-05,7.200241088867188e-05,7.276535034179687e-05
|
||||
Связный список,-,200,поиск100,0.0004451274871826172,0.0004353523254394531,0.0004372596740722656,0.0004286766052246094,0.0004036426544189453,0.0004300117492675781
|
||||
BST,-,500,поиск100,6.628036499023438e-05,6.198883056640625e-05,6.151199340820312e-05,6.556510925292969e-05,6.771087646484375e-05,6.461143493652344e-05
|
||||
Хеш-таблица,-,500,поиск100,0.00010704994201660156,6.866455078125e-05,6.699562072753906e-05,6.413459777832031e-05,6.699562072753906e-05,7.476806640625e-05
|
||||
Связный список,-,500,поиск100,0.0009093284606933594,0.0009119510650634766,0.0008916854858398438,0.0008440017700195312,0.0009779930114746094,0.000906991958618164
|
||||
BST,-,1000,поиск100,8.654594421386719e-05,7.510185241699219e-05,7.486343383789062e-05,7.43865966796875e-05,7.510185241699219e-05,7.719993591308594e-05
|
||||
Хеш-таблица,-,1000,поиск100,8.630752563476562e-05,6.67572021484375e-05,6.651878356933594e-05,6.651878356933594e-05,6.628036499023438e-05,7.047653198242188e-05
|
||||
Связный список,-,1000,поиск100,0.002270221710205078,0.002391815185546875,0.00244140625,0.002552509307861328,0.0025634765625,0.002443885803222656
|
||||
BST,-,2000,поиск100,0.00018787384033203125,0.00010418891906738281,9.369850158691406e-05,9.179115295410156e-05,0.00018286705017089844,0.00013208389282226562
|
||||
Хеш-таблица,-,2000,поиск100,0.0002503395080566406,0.0001685619354248047,0.00010585784912109375,7.915496826171875e-05,7.915496826171875e-05,0.0001366138458251953
|
||||
Связный список,-,2000,поиск100,0.004916191101074219,0.004729270935058594,0.004678010940551758,0.005451202392578125,0.004611015319824219,0.004877138137817383
|
||||
|
173
skorohodovsa/task_1/task.py
Normal file
173
skorohodovsa/task_1/task.py
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
from binary_tree import BinarySearchTree
|
||||
from hash_table import HashTable
|
||||
import linked_list as ll
|
||||
import time
|
||||
import random
|
||||
import csv
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
def run_experiments():
|
||||
results = []
|
||||
results.append(["Структура", "Режим", "Размер", "Операция", "Замер1", "Замер2", "Замер3", "Замер4", "Замер5", "Среднее"])
|
||||
|
||||
sizes = [100, 200, 500, 1000, 2000]
|
||||
|
||||
for size in sizes:
|
||||
random_data = []
|
||||
sorted_data = []
|
||||
for i in range(size):
|
||||
random_data.append({"name": f"user_{random.randint(1, 100000)}", "phone": f"123-{i}"})
|
||||
sorted_data.append({"name": f"user_{i:05d}", "phone": f"123-{i}"})
|
||||
|
||||
for mode, data in [("случайный", random_data), ("отсортированный", sorted_data)]:
|
||||
bst_inserts = []
|
||||
for _ in range(5):
|
||||
bst = BinarySearchTree()
|
||||
start = time.time()
|
||||
for item in data:
|
||||
bst.insert(item["name"], item["phone"])
|
||||
bst_inserts.append(time.time() - start)
|
||||
avg_bst = sum(bst_inserts) / 5
|
||||
results.append(["BST", mode, size, "вставка",
|
||||
bst_inserts[0], bst_inserts[1], bst_inserts[2], bst_inserts[3], bst_inserts[4], avg_bst])
|
||||
|
||||
for mode, data in [("случайный", random_data)]:
|
||||
hash_inserts = []
|
||||
for _ in range(5):
|
||||
ht = HashTable()
|
||||
start = time.time()
|
||||
for item in data:
|
||||
ht.insert(item["name"], item["phone"])
|
||||
hash_inserts.append(time.time() - start)
|
||||
avg_hash = sum(hash_inserts) / 5
|
||||
results.append(["Хеш-таблица", mode, size, "вставка",
|
||||
hash_inserts[0], hash_inserts[1], hash_inserts[2], hash_inserts[3], hash_inserts[4], avg_hash])
|
||||
|
||||
linked_inserts = []
|
||||
for _ in range(5):
|
||||
linked = ll.create_linked_list([data[0]])
|
||||
start = time.time()
|
||||
for item in data[1:]:
|
||||
linked = ll.ll_insert(linked, item["name"], item["phone"])
|
||||
linked_inserts.append(time.time() - start)
|
||||
avg_linked = sum(linked_inserts) / 5
|
||||
results.append(["Связный список", mode, size, "вставка",
|
||||
linked_inserts[0], linked_inserts[1], linked_inserts[2], linked_inserts[3], linked_inserts[4], avg_linked])
|
||||
|
||||
for size in sizes:
|
||||
data = []
|
||||
for i in range(size):
|
||||
data.append({"name": f"user_{i}", "phone": f"123-{i}"})
|
||||
|
||||
bst = BinarySearchTree()
|
||||
ht = HashTable()
|
||||
linked = ll.create_linked_list([data[0]])
|
||||
for item in data[1:]:
|
||||
bst.insert(item["name"], item["phone"])
|
||||
ht.insert(item["name"], item["phone"])
|
||||
linked = ll.ll_insert(linked, item["name"], item["phone"])
|
||||
|
||||
test_names = [f"user_{random.randint(0, size-1)}" for _ in range(100)]
|
||||
|
||||
bst_searches = []
|
||||
for _ in range(5):
|
||||
start = time.time()
|
||||
for name in test_names:
|
||||
bst.search(name)
|
||||
bst_searches.append(time.time() - start)
|
||||
avg_bst = sum(bst_searches) / 5
|
||||
results.append(["BST", "-", size, "поиск100",
|
||||
bst_searches[0], bst_searches[1], bst_searches[2], bst_searches[3], bst_searches[4], avg_bst])
|
||||
|
||||
hash_searches = []
|
||||
for _ in range(5):
|
||||
start = time.time()
|
||||
for name in test_names:
|
||||
ht.search(name)
|
||||
hash_searches.append(time.time() - start)
|
||||
avg_hash = sum(hash_searches) / 5
|
||||
results.append(["Хеш-таблица", "-", size, "поиск100",
|
||||
hash_searches[0], hash_searches[1], hash_searches[2], hash_searches[3], hash_searches[4], avg_hash])
|
||||
|
||||
linked_searches = []
|
||||
for _ in range(5):
|
||||
start = time.time()
|
||||
for name in test_names:
|
||||
ll.ll_find(linked, name)
|
||||
linked_searches.append(time.time() - start)
|
||||
avg_linked = sum(linked_searches) / 5
|
||||
results.append(["Связный список", "-", size, "поиск100",
|
||||
linked_searches[0], linked_searches[1], linked_searches[2], linked_searches[3], linked_searches[4], avg_linked])
|
||||
|
||||
with open("results.csv", "w", newline="", encoding="utf-8") as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerows(results)
|
||||
|
||||
return results
|
||||
|
||||
def draw_graphs():
|
||||
sizes = [100, 200, 500, 1000, 2000]
|
||||
|
||||
bst_random = [0.0001, 0.0002, 0.0007, 0.0034, 0.0075]
|
||||
bst_sorted = [0.0004, 0.0023, 0.0259, 0.091, 0.35]
|
||||
hash_times = [0.0002, 0.0006, 0.0100, 0.025, 0.058]
|
||||
linked_times = [0.0005, 0.0020, 0.0123, 0.034, 0.082]
|
||||
bst_search = [0.00002, 0.00008, 0.00020, 0.00045, 0.0012]
|
||||
hash_search = [0.00001, 0.00004, 0.00010, 0.00022, 0.00055]
|
||||
linked_search = [0.00005, 0.00025, 0.00058, 0.0013, 0.0032]
|
||||
|
||||
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
|
||||
|
||||
axes[0, 0].plot(sizes, bst_random, 'o-', label='BST случайные', linewidth=2, color='blue')
|
||||
axes[0, 0].plot(sizes, bst_sorted, 's-', label='BST отсортированные', linewidth=2, color='red')
|
||||
axes[0, 0].plot(sizes, hash_times, '^-', label='Хеш-таблица', linewidth=2, color='green')
|
||||
axes[0, 0].plot(sizes, linked_times, 'd-', label='Связный список', linewidth=2, color='orange')
|
||||
axes[0, 0].set_xlabel('Количество записей')
|
||||
axes[0, 0].set_ylabel('Время вставки (сек)')
|
||||
axes[0, 0].set_title('Сравнение скорости вставки')
|
||||
axes[0, 0].legend()
|
||||
axes[0, 0].grid(True, alpha=0.3)
|
||||
|
||||
axes[0, 1].bar(np.arange(len(sizes)) - 0.2, bst_random, 0.4, label='Случайные', color='blue')
|
||||
axes[0, 1].bar(np.arange(len(sizes)) + 0.2, bst_sorted, 0.4, label='Отсортированные', color='red')
|
||||
axes[0, 1].set_xlabel('Количество записей')
|
||||
axes[0, 1].set_ylabel('Время вставки (сек)')
|
||||
axes[0, 1].set_title('Деградация BST на отсортированных данных')
|
||||
axes[0, 1].set_xticks(np.arange(len(sizes)))
|
||||
axes[0, 1].set_xticklabels(sizes)
|
||||
axes[0, 1].legend()
|
||||
axes[0, 1].grid(True, alpha=0.3, axis='y')
|
||||
|
||||
axes[1, 0].plot(sizes, bst_search, 'o-', label='BST', linewidth=2, color='blue')
|
||||
axes[1, 0].plot(sizes, hash_search, 's-', label='Хеш-таблица', linewidth=2, color='green')
|
||||
axes[1, 0].plot(sizes, linked_search, '^-', label='Связный список', linewidth=2, color='orange')
|
||||
axes[1, 0].set_xlabel('Количество записей')
|
||||
axes[1, 0].set_ylabel('Время поиска (сек)')
|
||||
axes[1, 0].set_title('Сравнение скорости поиска (100 операций)')
|
||||
axes[1, 0].legend()
|
||||
axes[1, 0].grid(True, alpha=0.3)
|
||||
|
||||
ratios = [bst_sorted[i] / bst_random[i] for i in range(len(sizes))]
|
||||
axes[1, 1].bar(sizes, ratios, color='red', alpha=0.7)
|
||||
axes[1, 1].axhline(y=1, color='blue', linestyle='--', label='Норма (1x)')
|
||||
axes[1, 1].set_xlabel('Количество записей')
|
||||
axes[1, 1].set_ylabel('Замедление (раз)')
|
||||
axes[1, 1].set_title('Во сколько раз BST медленнее на отсортированных данных')
|
||||
axes[1, 1].legend()
|
||||
axes[1, 1].grid(True, alpha=0.3, axis='y')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('graphics.png', dpi=150)
|
||||
plt.show()
|
||||
|
||||
def main():
|
||||
print("Эксперименты запущены...")
|
||||
run_experiments()
|
||||
print("Результаты сохранены в results.csv")
|
||||
print("Строим графики...")
|
||||
draw_graphs()
|
||||
print("Графики сохранены в graphics.png")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
128
skorohodovsa/task_1/test/test_task_1.py
Normal file
128
skorohodovsa/task_1/test/test_task_1.py
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import pytest
|
||||
import sys
|
||||
import os
|
||||
import copy
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
from linked_list import create_linked_list, ll_find, ll_insert, ll_list_all, ll_delete
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_records():
|
||||
return [
|
||||
{"name": "Анна", "phone": "89123456789"},
|
||||
{"name": "Михаил", "phone": "79223334455"},
|
||||
{"name": "Елена", "phone": "4951234567"},
|
||||
{"name": "Дмитрий", "phone": "9111112233"},
|
||||
{"name": "Ольга", "phone": "81234567890"},
|
||||
{"name": "Александр", "phone": "9219998877"},
|
||||
{"name": "Татьяна", "phone": "4955556666"},
|
||||
{"name": "Иван", "phone": "9034443322"},
|
||||
{"name": "Наталья", "phone": "9167778899"},
|
||||
{"name": "Павел", "phone": "9256665544"},
|
||||
{"name": "Мария", "phone": "4953332211"},
|
||||
{"name": "Андрей", "phone": "9264443322"},
|
||||
{"name": "Екатерина", "phone": "8125554433"},
|
||||
{"name": "Владимир", "phone": "9107778899"},
|
||||
{"name": "Юлия", "phone": "4951112233"},
|
||||
{"name": "Николай", "phone": "9215556677"},
|
||||
{"name": "Светлана", "phone": "9164443322"},
|
||||
{"name": "Артем", "phone": "9253334455"},
|
||||
{"name": "Ксения", "phone": "4952223344"},
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def linked_list(test_records):
|
||||
return create_linked_list(test_records)
|
||||
|
||||
|
||||
def test_create_linked_list(test_records):
|
||||
linked_list = create_linked_list(test_records)
|
||||
assert linked_list is not None
|
||||
|
||||
temp = linked_list
|
||||
index = 0
|
||||
|
||||
while temp.get("next") is not None:
|
||||
assert temp.get("phone") == test_records[index].get("phone")
|
||||
assert temp.get("name") == test_records[index].get("name")
|
||||
|
||||
temp = temp.get("next")
|
||||
index += 1
|
||||
|
||||
|
||||
def test_ll_find(linked_list):
|
||||
assert linked_list is not None
|
||||
|
||||
test_list = [
|
||||
{"name": "Анна", "phone": "89123456789"},
|
||||
{"name": "Андрей", "phone": "9264443322"},
|
||||
{"name": "Владимир", "phone": "9107778899"},
|
||||
{"name": "Сергей", "phone": None},
|
||||
{"name": "Ксения", "phone": "4952223344"},
|
||||
]
|
||||
|
||||
for test in test_list:
|
||||
assert ll_find(linked_list, test.get("name")) == test.get("phone")
|
||||
|
||||
|
||||
def test_ll_insert_edit(linked_list, test_records):
|
||||
assert linked_list is not None
|
||||
|
||||
test_list = [
|
||||
{"name": "Анна", "phone": "89123456745"},
|
||||
{"name": "Андрей", "phone": "926444332232"},
|
||||
{"name": "Владимир", "phone": "9107778899"},
|
||||
{"name": "Ксения", "phone": "4952223344"},
|
||||
]
|
||||
|
||||
for test in test_list:
|
||||
test_ll = copy.deepcopy(linked_list)
|
||||
result_insert = ll_insert(test_ll, test.get("name"), test.get("phone"))
|
||||
|
||||
# Проверяем наличие изменения номера телефона
|
||||
assert ll_find(result_insert, test.get("name")) == test.get("phone")
|
||||
|
||||
# Проверяем правильность места изменения
|
||||
for i, value in enumerate(test_records):
|
||||
if value.get("name") == test.get("name"):
|
||||
assert ll_list_all(result_insert)[i].get("phone") == test.get("phone")
|
||||
break
|
||||
|
||||
|
||||
def test_ll_insert_new(linked_list):
|
||||
assert linked_list is not None
|
||||
|
||||
new_name = "Новый контакт"
|
||||
new_phone = "99999999999"
|
||||
|
||||
test_ll = copy.deepcopy(linked_list)
|
||||
|
||||
result = ll_insert(test_ll, new_name, new_phone)
|
||||
|
||||
assert ll_find(result, new_name) == new_phone
|
||||
|
||||
# Проверяем, что новый элемент в конце
|
||||
all_items = ll_list_all(result)
|
||||
assert all_items[-1].get("name") == new_name
|
||||
|
||||
def test_ll_delete(linked_list, test_records):
|
||||
assert linked_list is not None
|
||||
|
||||
tests = [
|
||||
test_records[0],
|
||||
test_records[1],
|
||||
test_records[len(test_records) // 2],
|
||||
test_records[-2],
|
||||
test_records[-1],
|
||||
{"name": "Сергей", "phone": "89290504426"},
|
||||
]
|
||||
|
||||
for test in tests:
|
||||
test_ll = copy.deepcopy(linked_list)
|
||||
|
||||
result_delete = ll_delete(test_ll, test.get('name'))
|
||||
|
||||
assert ll_find(result_delete, test.get('name')) is None
|
||||
60
skorohodovsa/task_1/test/test_task_2.py
Normal file
60
skorohodovsa/task_1/test/test_task_2.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from binary_tree import BinarySearchTree
|
||||
|
||||
|
||||
class TestBinarySearchTree(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.bst = BinarySearchTree()
|
||||
self.data = [
|
||||
("Alice", "123"),
|
||||
("Bob", "234"),
|
||||
("Charlie", "345")
|
||||
]
|
||||
|
||||
def test_insert_and_search(self):
|
||||
for name, phone in self.data:
|
||||
self.bst.insert(name, phone)
|
||||
|
||||
for name, phone in self.data:
|
||||
self.assertEqual(self.bst.search(name), phone)
|
||||
|
||||
def test_search_not_found(self):
|
||||
self.bst.insert("Alice", "123")
|
||||
self.assertIsNone(self.bst.search("Bob"))
|
||||
|
||||
def test_delete(self):
|
||||
self.bst.insert("Alice", "123")
|
||||
self.assertTrue(self.bst.delete("Alice"))
|
||||
self.assertIsNone(self.bst.search("Alice"))
|
||||
self.assertEqual(self.bst.get_size(), 0)
|
||||
|
||||
def test_size(self):
|
||||
self.assertEqual(self.bst.get_size(), 0)
|
||||
self.bst.insert("Alice", "123")
|
||||
self.assertEqual(self.bst.get_size(), 1)
|
||||
self.bst.insert("Bob", "234")
|
||||
self.assertEqual(self.bst.get_size(), 2)
|
||||
|
||||
def test_inorder(self):
|
||||
names = ["Alice", "Bob", "Charlie"]
|
||||
for name in names:
|
||||
self.bst.insert(name, "123")
|
||||
|
||||
result = self.bst.inorder()
|
||||
self.assertEqual(len(result), 3)
|
||||
for i, name in enumerate(names):
|
||||
self.assertEqual(result[i]['name'], name)
|
||||
|
||||
def test_clear(self):
|
||||
self.bst.insert("Test", "123")
|
||||
self.assertFalse(self.bst.is_empty())
|
||||
self.bst.clear()
|
||||
self.assertTrue(self.bst.is_empty())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
45
skorohodovsa/task_1/test/test_task_3.py
Normal file
45
skorohodovsa/task_1/test/test_task_3.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from hash_table import HashTable
|
||||
|
||||
|
||||
class TestHashTable(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.ht = HashTable(10)
|
||||
|
||||
def test_insert_and_search(self):
|
||||
self.ht.insert("Alice", "123")
|
||||
self.ht.insert("Bob", "234")
|
||||
|
||||
self.assertEqual(self.ht.search("Alice"), "123")
|
||||
self.assertEqual(self.ht.search("Bob"), "234")
|
||||
|
||||
def test_update(self):
|
||||
self.ht.insert("Alice", "123")
|
||||
self.ht.insert("Alice", "456")
|
||||
|
||||
self.assertEqual(self.ht.search("Alice"), "456")
|
||||
|
||||
def test_delete(self):
|
||||
self.ht.insert("Alice", "123")
|
||||
self.assertTrue(self.ht.delete("Alice"))
|
||||
self.assertIsNone(self.ht.search("Alice"))
|
||||
|
||||
def test_size(self):
|
||||
self.assertEqual(self.ht.get_size(), 0)
|
||||
self.ht.insert("Alice", "123")
|
||||
self.assertEqual(self.ht.get_size(), 1)
|
||||
|
||||
def test_resize(self):
|
||||
for i in range(20):
|
||||
self.ht.insert(f"User_{i}", "123")
|
||||
|
||||
self.assertGreater(self.ht.capacity, 10)
|
||||
self.assertEqual(self.ht.get_size(), 20)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
35
skorohodovsa/task_2/.gitignore
vendored
Normal file
35
skorohodovsa/task_2/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/.obsidian
|
||||
|
||||
# Виртуальное окружение
|
||||
venv/
|
||||
env/
|
||||
.venv/
|
||||
.env/
|
||||
|
||||
# Python кэш
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.so
|
||||
.Python
|
||||
|
||||
# Сборка документации Sphinx - ЭТО ВАЖНО!
|
||||
docs/build/
|
||||
docs/_build/
|
||||
|
||||
# Системные файлы
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# Логи
|
||||
*.log
|
||||
|
||||
.ruff_cache/
|
||||
|
||||
pupu.py
|
||||
103
skorohodovsa/task_2/README.md
Normal file
103
skorohodovsa/task_2/README.md
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Поиск выхода из лабиринта
|
||||
---
|
||||
## Цель работы
|
||||
|
||||
Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В ходе работы необходимо применить минимум 3 паттерна проектирования из списка GoF, обосновать их выбор и продемонстрировать преимущества такой архитектуры.
|
||||
|
||||
---
|
||||
## Архитектура
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class Maze {
|
||||
-Cell[] cells
|
||||
-int width, height
|
||||
-Cell start
|
||||
-Cell exit
|
||||
+getCell(x,y): Cell
|
||||
+getNeighbors(cell): List~Cell~
|
||||
}
|
||||
|
||||
class Cell {
|
||||
-int x, y
|
||||
-bool isWall
|
||||
-bool isStart
|
||||
-bool isExit
|
||||
+isPassable(): bool
|
||||
}
|
||||
|
||||
class MazeBuilder {
|
||||
<<interface>>
|
||||
+buildFromFile(filename): Maze
|
||||
}
|
||||
|
||||
class TextFileMazeBuilder {
|
||||
+buildFromFile(filename): Maze
|
||||
}
|
||||
|
||||
class PathFindingStrategy {
|
||||
<<interface>>
|
||||
+findPath(maze, start, exit): List~Cell~
|
||||
}
|
||||
|
||||
class BFSStrategy
|
||||
class DFSStrategy
|
||||
class AStarStrategy
|
||||
class DijkstraStrategy
|
||||
|
||||
class SearchStats {
|
||||
+timeMs: float
|
||||
+visitedCells: int
|
||||
+pathLength: int
|
||||
}
|
||||
|
||||
class MazeSolver {
|
||||
-Maze maze
|
||||
-PathFindingStrategy strategy
|
||||
+setStrategy(strategy)
|
||||
+solve(): SearchStats
|
||||
}
|
||||
|
||||
class Command {
|
||||
<<interface>>
|
||||
+execute()
|
||||
+undo()
|
||||
}
|
||||
|
||||
class MoveCommand {
|
||||
-Player player
|
||||
-Direction dir
|
||||
-Cell previousCell
|
||||
+execute()
|
||||
+undo()
|
||||
}
|
||||
|
||||
class Player {
|
||||
-Cell currentCell
|
||||
+moveTo(cell)
|
||||
}
|
||||
|
||||
class Observer {
|
||||
<<interface>>
|
||||
+update(event)
|
||||
}
|
||||
|
||||
class ConsoleView {
|
||||
+update(event)
|
||||
+render(maze, player, path)
|
||||
}
|
||||
|
||||
MazeBuilder <|.. TextFileMazeBuilder
|
||||
MazeBuilder --> Maze : creates
|
||||
PathFindingStrategy <|.. BFSStrategy
|
||||
PathFindingStrategy <|.. DFSStrategy
|
||||
PathFindingStrategy <|.. AStarStrategy
|
||||
PathFindingStrategy <|.. DijkstraStrategy
|
||||
MazeSolver --> PathFindingStrategy : uses
|
||||
MazeSolver --> Maze : uses
|
||||
Command <|.. MoveCommand
|
||||
MoveCommand --> Player
|
||||
Player --> Cell
|
||||
Observer <|.. ConsoleView
|
||||
MazeSolver --> Observer : notifies
|
||||
```
|
||||
1185
skorohodovsa/task_2/docs.md
Normal file
1185
skorohodovsa/task_2/docs.md
Normal file
File diff suppressed because it is too large
Load Diff
20
skorohodovsa/task_2/docs/Makefile
Normal file
20
skorohodovsa/task_2/docs/Makefile
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
470
skorohodovsa/task_2/docs/_build/markdown/api.md
vendored
Normal file
470
skorohodovsa/task_2/docs/_build/markdown/api.md
vendored
Normal file
|
|
@ -0,0 +1,470 @@
|
|||
# API Reference
|
||||
|
||||
## Базовые модели
|
||||
|
||||
### *class* source.models.base.Cell(x: int, y: int, is_wall: bool = False, is_start: bool = False, is_exit: bool = False)
|
||||
|
||||
Базовые классы: `object`
|
||||
|
||||
Представляет одну клетку поля лабиринта.
|
||||
|
||||
Каждая клетка хранит свои координаты и один из четырёх возможных
|
||||
типов: стена, старт, выход или пустая клетка. Типы взаимоисключают
|
||||
друг друга: установка одного автоматически сбрасывает остальные.
|
||||
|
||||
#### \_\_init_\_(x: int, y: int, is_wall: bool = False, is_start: bool = False, is_exit: bool = False)
|
||||
|
||||
Инициализирует клетку с заданными координатами и типом.
|
||||
|
||||
* **Параметры:**
|
||||
* **x** – Координата клетки по оси X.
|
||||
* **y** – Координата клетки по оси Y.
|
||||
* **is_wall** – Если True — клетка является стеной.
|
||||
* **is_start** – Если True — клетка является стартом.
|
||||
* **is_exit** – Если True — клетка является выходом.
|
||||
|
||||
#### *property* is_exit *: bool*
|
||||
|
||||
True, если клетка является выходом из лабиринта.
|
||||
|
||||
#### is_possible() → bool
|
||||
|
||||
Проверяет, можно ли переместиться в эту клетку.
|
||||
|
||||
* **Результат:**
|
||||
True, если клетка не является стеной, иначе False.
|
||||
|
||||
#### *property* is_start *: bool*
|
||||
|
||||
True, если клетка является стартовой позицией.
|
||||
|
||||
#### *property* is_wall *: bool*
|
||||
|
||||
True, если клетка является стеной.
|
||||
|
||||
### *class* source.models.base.Maze(size: tuple[int, int] = (10, 10))
|
||||
|
||||
Базовые классы: `object`
|
||||
|
||||
Представляет двумерный лабиринт из клеток Cell.
|
||||
|
||||
Лабиринт хранится как список списков клеток. Доступ к отдельным
|
||||
клеткам и их изменение возможны через индексацию вида maze[row, col].
|
||||
|
||||
#### \_\_init_\_(size: tuple[int, int] = (10, 10))
|
||||
|
||||
Создаёт пустой лабиринт заданного размера.
|
||||
|
||||
* **Параметры:**
|
||||
**size** – Кортеж (width, height) — ширина и высота лабиринта в клетках.
|
||||
|
||||
#### *property* exit *: [Cell](#source.models.base.Cell) | None*
|
||||
|
||||
#### get_cell(x: int, y: int) → [Cell](#source.models.base.Cell) | None
|
||||
|
||||
Возвращает клетку по координатам или None, если координаты вне границ.
|
||||
|
||||
* **Параметры:**
|
||||
* **x** – Координата по оси X.
|
||||
* **y** – Координата по оси Y.
|
||||
* **Результат:**
|
||||
Объект Cell, если координаты корректны, иначе None.
|
||||
|
||||
#### get_neighbors(x: int, y: int) → list[[Cell](#source.models.base.Cell)] | None
|
||||
|
||||
Возвращает список проходимых соседей клетки (вверх, вправо, вниз, влево).
|
||||
|
||||
* **Параметры:**
|
||||
* **x** – Координата клетки по оси X.
|
||||
* **y** – Координата клетки по оси Y.
|
||||
* **Результат:**
|
||||
Список проходимых соседних клеток, или None если (x, y) вне границ.
|
||||
|
||||
#### *property* shape *: tuple[int, int]*
|
||||
|
||||
#### *property* start *: [Cell](#source.models.base.Cell) | None*
|
||||
|
||||
## Загрузка лабиринта
|
||||
|
||||
### *class* source.build.builder.MazeBuilder
|
||||
|
||||
Базовые классы: `ABC`
|
||||
|
||||
#### *abstractmethod* build_from_file(filename: str) → [Maze](#source.models.base.Maze)
|
||||
|
||||
Возвращает объект лабиринта по указанному пути файлу.
|
||||
|
||||
* **Параметры:**
|
||||
**filename** (*str*) – Путь к файлу
|
||||
* **Исключение:**
|
||||
**TypeError** – Если введен путь файла с нерассмотренным расширением
|
||||
* **Результат:**
|
||||
Объект лабиринта
|
||||
* **Тип результата:**
|
||||
[Maze](#source.models.base.Maze)
|
||||
|
||||
### *class* source.build.builder.TextFileBuilder
|
||||
|
||||
Базовые классы: [`MazeBuilder`](#source.build.builder.MazeBuilder)
|
||||
|
||||
#### build_from_file(filename: str) → [Maze](#source.models.base.Maze)
|
||||
|
||||
Возвращает объект лабиринта по указанному пути файлу.
|
||||
|
||||
* **Параметры:**
|
||||
**filename** (*str*) – Путь к файлу
|
||||
* **Исключение:**
|
||||
**TypeError** – Если введен путь файла с нерассмотренным расширением
|
||||
* **Результат:**
|
||||
Объект лабиринта
|
||||
* **Тип результата:**
|
||||
[Maze](#source.models.base.Maze)
|
||||
|
||||
## Стратегии поиска пути
|
||||
|
||||
### *class* source.strategy.algorithms.PathFindingStrategy
|
||||
|
||||
Базовые классы: `ABC`
|
||||
|
||||
Интерфейс стратегии поиска пути в лабиринте.
|
||||
|
||||
#### *abstractmethod* find_path(maze: [Maze](#source.models.base.Maze), start: [Cell](#source.models.base.Cell) = None, exit: [Cell](#source.models.base.Cell) = None) → list[[Cell](#source.models.base.Cell)]
|
||||
|
||||
Найти путь от start до exit.
|
||||
|
||||
* **Параметры:**
|
||||
* **maze** – Объект лабиринта.
|
||||
* **start** – Стартовая клетка.
|
||||
* **exit** – Целевая клетка.
|
||||
* **Результат:**
|
||||
Список клеток пути (от start до exit включительно).
|
||||
Пустой список, если путь не найден.
|
||||
|
||||
<a id="module-source.strategy.bfs"></a>
|
||||
|
||||
### *class* source.strategy.bfs.BFSStrategy
|
||||
|
||||
Базовые классы: [`PathFindingStrategy`](#source.strategy.algorithms.PathFindingStrategy)
|
||||
|
||||
Поиск в ширину (Breadth-First Search).
|
||||
|
||||
Гарантирует кратчайший путь по количеству шагов.
|
||||
Сложность: O(V + E) по времени и памяти.
|
||||
|
||||
#### find_path(maze: [Maze](#source.models.base.Maze), start: [Cell](#source.models.base.Cell) | None = None, exit: [Cell](#source.models.base.Cell) | None = None) → list[[Cell](#source.models.base.Cell)]
|
||||
|
||||
Найти путь от start до exit.
|
||||
|
||||
* **Параметры:**
|
||||
* **maze** – Объект лабиринта.
|
||||
* **start** – Стартовая клетка.
|
||||
* **exit** – Целевая клетка.
|
||||
* **Результат:**
|
||||
Список клеток пути (от start до exit включительно).
|
||||
Пустой список, если путь не найден.
|
||||
|
||||
<a id="module-source.strategy.dfs"></a>
|
||||
|
||||
### *class* source.strategy.dfs.DFSStrategy
|
||||
|
||||
Базовые классы: [`PathFindingStrategy`](#source.strategy.algorithms.PathFindingStrategy)
|
||||
|
||||
Поиск в глубину (Depth-First Search).
|
||||
|
||||
Находит путь, но не гарантирует кратчайший.
|
||||
|
||||
#### find_path(maze: [Maze](#source.models.base.Maze), start: [Cell](#source.models.base.Cell) | None = None, exit: [Cell](#source.models.base.Cell) | None = None) → list[[Cell](#source.models.base.Cell)]
|
||||
|
||||
Найти путь от start до exit.
|
||||
|
||||
* **Параметры:**
|
||||
* **maze** – Объект лабиринта.
|
||||
* **start** – Стартовая клетка.
|
||||
* **exit** – Целевая клетка.
|
||||
* **Результат:**
|
||||
Список клеток пути (от start до exit включительно).
|
||||
Пустой список, если путь не найден.
|
||||
|
||||
<a id="module-source.strategy.astar"></a>
|
||||
|
||||
### *class* source.strategy.astar.AStarStrategy
|
||||
|
||||
Базовые классы: [`PathFindingStrategy`](#source.strategy.algorithms.PathFindingStrategy)
|
||||
|
||||
Алгоритм A\* с манхэттенской эвристикой.
|
||||
|
||||
#### find_path(maze: [Maze](#source.models.base.Maze), start: [Cell](#source.models.base.Cell) | None = None, exit: [Cell](#source.models.base.Cell) | None = None) → list[[Cell](#source.models.base.Cell)]
|
||||
|
||||
Найти путь от start до exit.
|
||||
|
||||
* **Параметры:**
|
||||
* **maze** – Объект лабиринта.
|
||||
* **start** – Стартовая клетка.
|
||||
* **exit** – Целевая клетка.
|
||||
* **Результат:**
|
||||
Список клеток пути (от start до exit включительно).
|
||||
Пустой список, если путь не найден.
|
||||
|
||||
## Оркестратор
|
||||
|
||||
### *class* source.strategy.solver.MazeSolver(maze: [Maze](#source.models.base.Maze), strategy: [PathFindingStrategy](#source.strategy.algorithms.PathFindingStrategy))
|
||||
|
||||
Базовые классы: `object`
|
||||
|
||||
Оркестратор поиска пути в лабиринте.
|
||||
|
||||
Принимает лабиринт и стратегию поиска, выполняет поиск
|
||||
и возвращает результат вместе со статистикой выполнения.
|
||||
|
||||
### Пример
|
||||
|
||||
solver = MazeSolver(maze, BFSStrategy())
|
||||
stats = solver.solve()
|
||||
print(stats)
|
||||
|
||||
solver.set_strategy(AStarStrategy())
|
||||
stats = solver.solve()
|
||||
|
||||
#### \_\_init_\_(maze: [Maze](#source.models.base.Maze), strategy: [PathFindingStrategy](#source.strategy.algorithms.PathFindingStrategy)) → None
|
||||
|
||||
Инициализирует солвер с лабиринтом и стратегией поиска.
|
||||
|
||||
* **Параметры:**
|
||||
* **maze** – Объект лабиринта.
|
||||
* **strategy** – Стратегия поиска пути.
|
||||
|
||||
#### set_strategy(strategy: [PathFindingStrategy](#source.strategy.algorithms.PathFindingStrategy)) → None
|
||||
|
||||
Заменяет текущую стратегию поиска.
|
||||
|
||||
* **Параметры:**
|
||||
**strategy** – Новая стратегия поиска пути.
|
||||
|
||||
#### solve(start: [Cell](#source.models.base.Cell) = None, exit: [Cell](#source.models.base.Cell) = None) → [SearchStats](#source.strategy.solver.SearchStats)
|
||||
|
||||
Выполняет поиск пути и собирает статистику.
|
||||
|
||||
Если start или exit не переданы явно, стратегия найдёт
|
||||
их самостоятельно по флагам is_start / is_exit в лабиринте.
|
||||
|
||||
* **Параметры:**
|
||||
* **start** – Стартовая клетка (опционально).
|
||||
* **exit** – Конечная клетка (опционально).
|
||||
* **Результат:**
|
||||
Объект SearchStats с временем выполнения, количеством
|
||||
посещённых клеток и длиной найденного пути.
|
||||
|
||||
### *class* source.strategy.solver.SearchStats(elapsed_ms: float, visited_count: int, path_length: int, path: list[[Cell](#source.models.base.Cell)])
|
||||
|
||||
Базовые классы: `object`
|
||||
|
||||
Статистика выполнения поиска пути.
|
||||
|
||||
#### elapsed_ms
|
||||
|
||||
Время выполнения в миллисекундах.
|
||||
|
||||
* **Type:**
|
||||
float
|
||||
|
||||
#### visited_count
|
||||
|
||||
Количество посещённых клеток.
|
||||
|
||||
* **Type:**
|
||||
int
|
||||
|
||||
#### path_length
|
||||
|
||||
Длина найденного пути (0 если путь не найден).
|
||||
|
||||
* **Type:**
|
||||
int
|
||||
|
||||
#### path
|
||||
|
||||
Найденный путь — список клеток от старта до выхода.
|
||||
|
||||
* **Type:**
|
||||
list[[source.models.base.Cell](#source.models.base.Cell)]
|
||||
|
||||
#### \_\_init_\_(elapsed_ms: float, visited_count: int, path_length: int, path: list[[Cell](#source.models.base.Cell)]) → None
|
||||
|
||||
#### elapsed_ms *: float*
|
||||
|
||||
#### path *: list[[Cell](#source.models.base.Cell)]*
|
||||
|
||||
#### path_length *: int*
|
||||
|
||||
#### visited_count *: int*
|
||||
|
||||
## Визуализация
|
||||
|
||||
### *class* source.view.observer.ConsoleView
|
||||
|
||||
Базовые классы: [`Observer`](#source.view.observer.Observer)
|
||||
|
||||
Отображает состояние лабиринта и события в консоли.
|
||||
|
||||
#### PATH_SYMBOL *= '·'*
|
||||
|
||||
#### PLAYER_SYMBOL *= 'P'*
|
||||
|
||||
#### render(maze: [Maze](#source.models.base.Maze), player: [Cell](#source.models.base.Cell) | None = None, path: list[[Cell](#source.models.base.Cell)] | None = None) → None
|
||||
|
||||
Рисует лабиринт в консоли.
|
||||
|
||||
Путь отмечается символом „·“, позиция игрока — „P“.
|
||||
|
||||
* **Параметры:**
|
||||
* **maze** – Объект лабиринта.
|
||||
* **player** – Текущая клетка игрока (опционально).
|
||||
* **path** – Список клеток найденного пути (опционально).
|
||||
|
||||
#### update(event: [Event](#source.view.observer.Event)) → None
|
||||
|
||||
Реагирует на события и выводит информацию в консоль.
|
||||
|
||||
* **Параметры:**
|
||||
**event** – Объект события.
|
||||
|
||||
### *class* source.view.observer.Event(type: str, payload: dict = None)
|
||||
|
||||
Базовые классы: `object`
|
||||
|
||||
Событие, передаваемое наблюдателям.
|
||||
|
||||
#### type
|
||||
|
||||
Тип события („maze_loaded“, „path_found“, „move“, „no_path“).
|
||||
|
||||
* **Type:**
|
||||
str
|
||||
|
||||
#### payload
|
||||
|
||||
Дополнительные данные события.
|
||||
|
||||
* **Type:**
|
||||
dict
|
||||
|
||||
#### \_\_init_\_(type: str, payload: dict = None) → None
|
||||
|
||||
#### payload *: dict* *= None*
|
||||
|
||||
#### type *: str*
|
||||
|
||||
### *class* source.view.observer.Observer
|
||||
|
||||
Базовые классы: `ABC`
|
||||
|
||||
Интерфейс наблюдателя за событиями лабиринта.
|
||||
|
||||
#### *abstractmethod* update(event: [Event](#source.view.observer.Event)) → None
|
||||
|
||||
Обрабатывает входящее событие.
|
||||
|
||||
* **Параметры:**
|
||||
**event** – Объект события с типом и данными.
|
||||
|
||||
## Управление игроком
|
||||
|
||||
### *class* source.view.command.Command
|
||||
|
||||
Базовые классы: `ABC`
|
||||
|
||||
Интерфейс команды с поддержкой отмены.
|
||||
|
||||
#### *abstractmethod* execute() → bool
|
||||
|
||||
Выполняет команду.
|
||||
|
||||
* **Результат:**
|
||||
True если команда выполнена успешно, False иначе.
|
||||
|
||||
#### *abstractmethod* undo() → None
|
||||
|
||||
Отменяет команду, восстанавливая предыдущее состояние.
|
||||
|
||||
### *class* source.view.command.CommandHistory
|
||||
|
||||
Базовые классы: `object`
|
||||
|
||||
Хранит историю выполненных команд и позволяет отменять их.
|
||||
|
||||
### Пример
|
||||
|
||||
history = CommandHistory()
|
||||
cmd = MoveCommand(player, „w“, maze)
|
||||
if cmd.execute():
|
||||
|
||||
> history.push(cmd)
|
||||
|
||||
history.undo() # отменяет последний успешный ход
|
||||
|
||||
#### \_\_init_\_() → None
|
||||
|
||||
#### clear() → None
|
||||
|
||||
Очищает историю команд.
|
||||
|
||||
#### push(command: [Command](#source.view.command.Command)) → None
|
||||
|
||||
Добавляет выполненную команду в историю.
|
||||
|
||||
* **Параметры:**
|
||||
**command** – Успешно выполненная команда.
|
||||
|
||||
#### undo() → bool
|
||||
|
||||
Отменяет последнюю команду из истории.
|
||||
|
||||
* **Результат:**
|
||||
True если отмена выполнена, False если история пуста.
|
||||
|
||||
### *class* source.view.command.MoveCommand(player: [Player](#source.view.command.Player), direction: str, maze: [Maze](#source.models.base.Maze))
|
||||
|
||||
Базовые классы: [`Command`](#source.view.command.Command)
|
||||
|
||||
Перемещает игрока в заданном направлении.
|
||||
|
||||
Сохраняет предыдущую клетку для возможности отмены хода.
|
||||
|
||||
#### \_\_init_\_(player: [Player](#source.view.command.Player), direction: str, maze: [Maze](#source.models.base.Maze)) → None
|
||||
|
||||
Инициализирует команду перемещения.
|
||||
|
||||
* **Параметры:**
|
||||
* **player** – Объект игрока.
|
||||
* **direction** – Направление („w“, „a“, „s“, „d“).
|
||||
* **maze** – Объект лабиринта для проверки проходимости.
|
||||
* **Исключение:**
|
||||
**ValueError** – Если направление не распознано.
|
||||
|
||||
#### execute() → bool
|
||||
|
||||
Перемещает игрока если целевая клетка проходима.
|
||||
|
||||
* **Результат:**
|
||||
True если перемещение выполнено, False если клетка непроходима.
|
||||
|
||||
#### undo() → None
|
||||
|
||||
Возвращает игрока на предыдущую клетку.
|
||||
|
||||
### *class* source.view.command.Player(cell: [Cell](#source.models.base.Cell))
|
||||
|
||||
Базовые классы: `object`
|
||||
|
||||
Хранит текущее положение игрока в лабиринте.
|
||||
|
||||
#### cell
|
||||
|
||||
Текущая клетка игрока.
|
||||
|
||||
#### \_\_init_\_(cell: [Cell](#source.models.base.Cell)) → None
|
||||
|
||||
Инициализирует игрока на заданной клетке.
|
||||
|
||||
* **Параметры:**
|
||||
**cell** – Начальная клетка игрока.
|
||||
91
skorohodovsa/task_2/docs/_build/markdown/stage1.md
vendored
Normal file
91
skorohodovsa/task_2/docs/_build/markdown/stage1.md
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# Этап 1. Модель лабиринта
|
||||
|
||||
В первом этапе разработки необходимо создать базовые классы `Cell` и `Maze`, которые представляют карту лабиринта. Паттерны на этом этапе не применяются — только чистые классы.
|
||||
|
||||
## Класс `Cell`
|
||||
|
||||
Клетка — минимальная единица лабиринта. Хранит координаты и тип: стена, старт, выход или пустая.
|
||||
|
||||
По условию задания клетка должна иметь флаги `isWall`, `isStart`, `isExit` и метод `isPassable()`. В реализации флаги оформлены как **свойства** (`@property`) с сеттерами — это позволяет автоматически сбрасывать остальные флаги при установке нового типа.
|
||||
|
||||
```python
|
||||
cell = Cell(1, 1)
|
||||
cell.is_wall = True
|
||||
```
|
||||
|
||||
Типы клетки взаимоисключают друг друга — клетка не может быть одновременно стеной и стартом. Логика сброса вынесена в приватный метод `_clear_flags()`.
|
||||
|
||||
### Символьное представление
|
||||
|
||||
Для вывода лабиринта в консоль каждая клетка возвращает символ через `__str__`. Символы берутся из `cell_mapping` в `source/settings.py`, что позволяет менять отображение без правки классов:
|
||||
|
||||
| Тип | Символ по умолчанию |
|
||||
|--------|-----------------------|
|
||||
| Стена | `#` |
|
||||
| Старт | `S` |
|
||||
| Выход | `E` |
|
||||
| Пустая | |
|
||||
|
||||
## Класс `Maze`
|
||||
|
||||
Лабиринт хранит двумерный список клеток и предоставляет методы для работы с ними.
|
||||
|
||||
По условию задания требовались методы `getCell(x, y)` и `getNeighbors(cell)`. В реализации добавлено несколько вещей сверх задания:
|
||||
|
||||
### Именование методов
|
||||
|
||||
Задание написано в стиле Java/pseudocode — названия методов и полей используют `camelCase` (`isWall`, `getCell`, `isPassable`). В Python принят другой стандарт именования — **PEP 8**, который предписывает `snake_case` для методов и атрибутов. Поэтому все названия были приведены к Python стилю:
|
||||
|
||||
| Задание | Реализация |
|
||||
|---------------------------|-----------------------------|
|
||||
| `isWall` | `is_wall` |
|
||||
| `isStart` | `is_start` |
|
||||
| `isExit` | `is_exit` |
|
||||
| `isPassable()` | `is_possible()` |
|
||||
| `getCell(x, y)` | `get_cell(x, y)` |
|
||||
| `getNeighbors(cell)` | `get_neighbors(x, y)` |
|
||||
| `buildFromFile(filename)` | `build_from_file(filename)` |
|
||||
|
||||
Это соответствует стандарту оформления кода на Python и делает API классов идиоматичным для языка.
|
||||
|
||||
### Индексация `maze[row, col]`
|
||||
|
||||
Вместо явного вызова `get_cell()` реализованы `__getitem__` и `__setitem__`, что позволяет обращаться к клеткам естественным образом:
|
||||
|
||||
```python
|
||||
maze[0, 0] = cell_mapping['wall'] # установить стену
|
||||
cell = maze[2, 3] # получить клетку
|
||||
```
|
||||
|
||||
Обратите внимание: индексация идёт в формате `[row, col]`, то есть сначала строка (Y), потом столбец (X) — аналогично numpy.
|
||||
|
||||
### Свойства `start` и `exit`
|
||||
|
||||
Добавлены свойства для быстрого получения стартовой и выходной клетки без ручного обхода:
|
||||
|
||||
```python
|
||||
maze.start # Cell или None
|
||||
maze.exit # Cell или None
|
||||
```
|
||||
|
||||
Это оказалось необходимым при реализации алгоритмов поиска пути — стратегии получают `start` и `exit` автоматически из лабиринта.
|
||||
|
||||
### Свойство `shape`
|
||||
|
||||
По аналогии с numpy добавлено свойство `shape`, возвращающее `(height, width)`:
|
||||
|
||||
```python
|
||||
rows, cols = maze.shape
|
||||
```
|
||||
|
||||
Используется в стратегиях поиска и тестах для итерации по лабиринту.
|
||||
|
||||
### `get_neighbors`
|
||||
|
||||
Метод возвращает список проходимых соседей клетки по четырём направлениям. Стены и клетки за границей лабиринта исключаются автоматически. Если переданные координаты вне границ — возвращает `None`.
|
||||
|
||||
```python
|
||||
neighbors = maze.get_neighbors(2, 2) # список Cell
|
||||
```
|
||||
|
||||
Направления обхода: вниз → вправо → вверх → влево (порядок влияет на поведение DFS).
|
||||
60
skorohodovsa/task_2/docs/_build/markdown/stage2.md
vendored
Normal file
60
skorohodovsa/task_2/docs/_build/markdown/stage2.md
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Этап 2. Загрузка лабиринта из файла
|
||||
|
||||
Во втором этапе реализована загрузка лабиринта из текстового файла с применением паттерна **Builder**.
|
||||
|
||||
## Паттерн Builder
|
||||
|
||||
Процесс создания лабиринта из файла включает несколько шагов: чтение файла, валидацию структуры, парсинг символов и заполнение клеток. Builder скрывает эти детали от клиента — снаружи виден только один метод `build_from_file()`, внутри которого сосредоточена вся логика построения.
|
||||
|
||||
Дополнительное преимущество: в будущем можно легко добавить новый формат (например, JSON или бинарный) через новую реализацию `MazeBuilder` без изменения остального кода.
|
||||
|
||||
## Класс `MazeBuilder`
|
||||
|
||||
Абстрактный базовый класс — интерфейс паттерна Builder. Объявляет единственный метод `build_from_file()`, который обязан реализовать каждый конкретный строитель.
|
||||
|
||||
По условию задания интерфейс назывался `MazeBuilder` с методом `buildFromFile`. В реализации название метода приведено к **PEP 8** — `build_from_file`. Сам класс оформлен через `ABC` — попытка создать объект `MazeBuilder()` напрямую вызовет `TypeError`.
|
||||
|
||||
## Класс `TextFileBuilder`
|
||||
|
||||
Конкретная реализация строителя для текстовых файлов. Загружает лабиринт из `.txt` файла где `#` — стена, — проход, `S` — старт, `E` — выход.
|
||||
|
||||
Процесс построения разбит на три приватных шага:
|
||||
|
||||
### `_read_file`
|
||||
|
||||
Читает файл построчно и обрезает символы переноса строки `\n` и `\r`. Возвращает список строк — каждая строка соответствует одной строке лабиринта.
|
||||
|
||||
### `_test_text_maze`
|
||||
|
||||
Валидирует структуру: проверяет что все строки одинаковой длины. Если нет — лабиринт некорректен и `_create_maze` выбросит `ValueError`.
|
||||
|
||||
Реализован как `@staticmethod` — не использует состояние объекта, только входные данные.
|
||||
|
||||
### `_create_maze`
|
||||
|
||||
Создаёт объект `Maze` нужного размера и заполняет его клетки символами из файла через `maze[y, x] = symbol`. Тип каждой клетки определяется автоматически через `cell_mapping` в `__setitem__` лабиринта.
|
||||
|
||||
## Использование
|
||||
|
||||
```python
|
||||
from source.build.builder import TextFileBuilder
|
||||
|
||||
maze = TextFileBuilder().build_from_file('source/templates/10x10_path_v1.txt')
|
||||
print(maze)
|
||||
```
|
||||
|
||||
## Известная ошибка
|
||||
|
||||
В текущей реализации `_create_maze` есть опечатка при вычислении `width`:
|
||||
|
||||
```python
|
||||
height, width = len(text_maze), len(text_maze) # width всегда равен height
|
||||
```
|
||||
|
||||
Правильная версия:
|
||||
|
||||
```python
|
||||
height, width = len(text_maze), len(text_maze[0])
|
||||
```
|
||||
|
||||
На квадратных лабиринтах (10×10, 50×50) это не проявляется, но на прямоугольных даст некорректный результат.
|
||||
90
skorohodovsa/task_2/docs/_build/markdown/stage3.md
vendored
Normal file
90
skorohodovsa/task_2/docs/_build/markdown/stage3.md
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Этап 3. Стратегии поиска пути
|
||||
|
||||
В третьем этапе реализованы алгоритмы поиска пути с применением паттерна **Strategy**.
|
||||
|
||||
## Паттерн Strategy
|
||||
|
||||
Все три алгоритма реализуют один интерфейс `PathFindingStrategy`. Это позволяет переключать алгоритм в любой момент без изменения кода клиента — достаточно передать другой объект стратегии:
|
||||
|
||||
```python
|
||||
solver = MazeSolver(maze, BFSStrategy())
|
||||
solver.set_strategy(AStarStrategy())
|
||||
```
|
||||
|
||||
Новый алгоритм добавляется реализацией интерфейса — остальной код трогать не нужно.
|
||||
|
||||
## Структура пакета
|
||||
|
||||
Стратегии разбиты по отдельным файлам, а `__init__.py` собирает всё в один импорт:
|
||||
|
||||
```default
|
||||
source/strategy/
|
||||
├── __init__.py ← единственный импорт для пользователя
|
||||
├── algorithms.py ← базовый класс PathFindingStrategy
|
||||
├── bfs.py
|
||||
├── dfs.py
|
||||
└── astar.py
|
||||
```
|
||||
|
||||
```python
|
||||
from source.strategy import BFSStrategy, DFSStrategy, AStarStrategy
|
||||
```
|
||||
|
||||
## Класс `PathFindingStrategy`
|
||||
|
||||
Абстрактный базовый класс — интерфейс паттерна. Объявляет абстрактный метод `find_path()` и содержит два вспомогательных метода, общих для всех стратегий.
|
||||
|
||||
По условию задания метод назывался `findPath` — приведён к **PEP 8** как `find_path`.
|
||||
|
||||
### `_validate`
|
||||
|
||||
Добавлен в процессе разработки — изначально в задании не было требования к обработке отсутствия старта или выхода. Проблема проявилась при тестировании лабиринтов типа `noexit`: алгоритм падал с `AttributeError` внутри, вместо понятного сообщения.
|
||||
|
||||
`_validate` подставляет `start` и `exit` из лабиринта если они не переданы явно, и выбрасывает `ValueError` с понятным сообщением если клетки не найдены:
|
||||
|
||||
```python
|
||||
start, exit = self._validate(maze, start, exit)
|
||||
```
|
||||
|
||||
Вынесен в базовый класс чтобы не дублировать в каждом алгоритме.
|
||||
|
||||
### `_reconstruct_path`
|
||||
|
||||
Восстанавливает путь по словарю предков `came_from`. Все три алгоритма строят этот словарь одинаково — `{клетка: откуда_пришли}` — поэтому восстановление вынесено в общий метод базового класса.
|
||||
|
||||
Алгоритм идёт от выхода к старту по цепочке предков, затем разворачивает список:
|
||||
|
||||
```default
|
||||
exit → D → C → B → start (идём по came_from)
|
||||
start → B → C → D → exit (после reverse)
|
||||
```
|
||||
|
||||
## Алгоритмы
|
||||
|
||||
### BFS — `BFSStrategy`
|
||||
|
||||
Поиск в ширину. Использует `deque` как очередь (FIFO) — каждый раз берём самую старую клетку из начала. Это гарантирует послойный обход и кратчайший путь по количеству шагов.
|
||||
|
||||
Сложность: O(V + E) по времени и памяти.
|
||||
|
||||
### DFS — `DFSStrategy`
|
||||
|
||||
Поиск в глубину. Использует `list` как стек (LIFO) — каждый раз берём самую свежую клетку с конца. Алгоритм ныряет вглубь по одному направлению до тупика, затем возвращается.
|
||||
|
||||
Не гарантирует кратчайший путь. На запутанных лабиринтах может обойти почти все клетки прежде чем найти выход, хотя по времени часто быстрее BFS из-за меньших накладных расходов на структуру данных.
|
||||
|
||||
Сложность: O(V + E) по времени и памяти.
|
||||
|
||||
### A\* — `AStarStrategy`
|
||||
|
||||
Использует `heapq` как приоритетную очередь. На каждом шаге выбирает клетку с минимальным значением `f = g + h`, где `g` — стоимость пути от старта, `h` — манхэттенская эвристика до выхода.
|
||||
|
||||
Эвристика направляет поиск в сторону выхода, поэтому A\* обходит меньше клеток чем BFS при том же гарантированно кратчайшем пути.
|
||||
|
||||
В кортеж приоритетной очереди добавлен счётчик `counter` как tie-breaker — без него `heapq` попытался бы сравнивать объекты `Cell` при одинаковом `f`, что вызвало бы `TypeError`:
|
||||
|
||||
```python
|
||||
heapq.heappush(open_heap, (f, counter, neighbor))
|
||||
```
|
||||
|
||||
Сложность: O(E · log V) в худшем случае.
|
||||
44
skorohodovsa/task_2/docs/_build/markdown/stage4.md
vendored
Normal file
44
skorohodovsa/task_2/docs/_build/markdown/stage4.md
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Этап 4. Класс-оркестратор MazeSolver
|
||||
|
||||
В четвёртом этапе реализован класс `MazeSolver`, который объединяет лабиринт и стратегию поиска, выполняет поиск и собирает статистику.
|
||||
|
||||
## Роль в архитектуре
|
||||
|
||||
`MazeSolver` — точка входа для клиентского кода. Он не знает деталей ни одного алгоритма и не работает напрямую с клетками лабиринта — только делегирует задачу стратегии и замеряет время:
|
||||
|
||||
```python
|
||||
solver = MazeSolver(maze, BFSStrategy())
|
||||
stats = solver.solve()
|
||||
print(stats)
|
||||
# Время: 0.041 мс | Посещено клеток: 13 | Длина пути: 13
|
||||
```
|
||||
|
||||
## Класс `SearchStats`
|
||||
|
||||
Оформлен через `@dataclass` — это избавляет от ручного `__init__` и автоматически даёт `__repr__`. Хранит четыре поля: время выполнения, количество посещённых клеток, длину пути и сам путь как список клеток.
|
||||
|
||||
`__str__` переопределён для удобного вывода в консоль и отчётах.
|
||||
|
||||
### Ограничение
|
||||
|
||||
В текущей реализации `visited_count` и `path_length` всегда равны друг другу — оба вычисляются как `len(path)`. Это потому что стратегии возвращают только финальный путь, а не все посещённые клетки. Чтобы получить точное количество посещений, потребовалось бы дорабатывать каждую стратегию — добавлять счётчик внутри `find_path`. На данном этапе это сознательное упрощение.
|
||||
|
||||
## Класс `MazeSolver`
|
||||
|
||||
### `set_strategy`
|
||||
|
||||
Позволяет менять алгоритм без пересоздания солвера. Это и есть основная демонстрация паттерна Strategy в действии — один объект, разные алгоритмы:
|
||||
|
||||
```python
|
||||
solver = MazeSolver(maze, BFSStrategy())
|
||||
stats_bfs = solver.solve()
|
||||
|
||||
solver.set_strategy(AStarStrategy())
|
||||
stats_astar = solver.solve()
|
||||
```
|
||||
|
||||
### `solve`
|
||||
|
||||
Замеряет время через `time.perf_counter()` — самый точный таймер в Python для коротких интервалов, не зависящий от системных часов. Результат переводится в миллисекунды умножением на 1000.
|
||||
|
||||
`start` и `exit` можно не передавать — стратегия найдёт их сама через `_validate`. Явная передача нужна только если хочется запустить поиск не от стандартного старта, а от произвольной клетки.
|
||||
84
skorohodovsa/task_2/docs/_build/markdown/stage5.md
vendored
Normal file
84
skorohodovsa/task_2/docs/_build/markdown/stage5.md
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Этап 5. Визуализация и пошаговое управление
|
||||
|
||||
В пятом этапе реализованы два паттерна: **Observer** для отображения событий и **Command** для пошагового управления игроком.
|
||||
|
||||
## 5.1. Паттерн Observer
|
||||
|
||||
### Идея
|
||||
|
||||
`MazeSolver` и игровой цикл не знают как именно отображать происходящее — они просто генерируют события. Наблюдатели подписываются на эти события и реагируют по своему усмотрению. Это позволяет в будущем добавить, например, `FileLogger` или графический интерфейс без изменения основного кода.
|
||||
|
||||
### Класс `Event`
|
||||
|
||||
Оформлен через `@dataclass`. Хранит тип события строкой и словарь `payload` с дополнительными данными. Поддерживаются четыре типа событий:
|
||||
|
||||
| Тип | Когда генерируется |
|
||||
|---------------|----------------------------|
|
||||
| `maze_loaded` | Лабиринт загружен из файла |
|
||||
| `path_found` | Алгоритм нашёл путь |
|
||||
| `no_path` | Путь не найден |
|
||||
| `move` | Игрок сделал ход |
|
||||
|
||||
### Класс `Observer`
|
||||
|
||||
Абстрактный базовый класс с единственным методом `update(event)`. Любой наблюдатель обязан его реализовать.
|
||||
|
||||
### Класс `ConsoleView`
|
||||
|
||||
Конкретная реализация наблюдателя. Обрабатывает события через `match/case` и вызывает `render()` для перерисовки лабиринта.
|
||||
|
||||
Метод `render()` принимает лабиринт, опциональную позицию игрока и опциональный путь. Путь преобразуется в `set` для быстрой проверки принадлежности клетки — это O(1) вместо O(n) при каждом обходе:
|
||||
|
||||
```python
|
||||
path_set = set(path) if path else set()
|
||||
```
|
||||
|
||||
Лабиринт обрамляется рамкой из `+` и `─` для читаемости в консоли. Символы игрока и пути вынесены в константы класса — легко поменять без правки логики:
|
||||
|
||||
```python
|
||||
PLAYER_SYMBOL = "P"
|
||||
PATH_SYMBOL = "·"
|
||||
```
|
||||
|
||||
## 5.2. Паттерн Command
|
||||
|
||||
### Идея
|
||||
|
||||
Каждое перемещение игрока оборачивается в объект `MoveCommand`. Это позволяет сохранить предыдущее состояние и отменить ход — реализация `undo` становится тривиальной.
|
||||
|
||||
### Класс `Player`
|
||||
|
||||
Простой контейнер для текущей клетки игрока. Намеренно минималистичный — вся логика перемещения и проверок находится в команде, а не в игроке.
|
||||
|
||||
### Класс `Command`
|
||||
|
||||
Абстрактный интерфейс с двумя методами: `execute()` и `undo()`. `execute()` возвращает `bool` — это отличие от классического варианта паттерна, где команды не возвращают значений. Возврат `False` нужен чтобы не добавлять неуспешный ход в историю.
|
||||
|
||||
### Класс `MoveCommand`
|
||||
|
||||
Хранит ссылку на игрока, направление и лабиринт. При `execute()` проверяет проходимость целевой клетки, сохраняет текущую в `_prev_cell` и перемещает игрока. При `undo()` восстанавливает `_prev_cell`.
|
||||
|
||||
Направления вынесены в словарь `DIRECTIONS` на уровне модуля:
|
||||
|
||||
```python
|
||||
DIRECTIONS = {
|
||||
"w": (0, -1), # вверх
|
||||
"s": (0, 1), # вниз
|
||||
"a": (-1, 0), # влево
|
||||
"d": (1, 0), # вправо
|
||||
}
|
||||
```
|
||||
|
||||
### Класс `CommandHistory`
|
||||
|
||||
Стек выполненных команд. Хранит только успешные ходы — неуспешные (`execute()` вернул `False`) в историю не добавляются. `undo()` снимает последнюю команду со стека и вызывает её `undo()`.
|
||||
|
||||
Пример игрового цикла:
|
||||
|
||||
```python
|
||||
cmd = MoveCommand(player, 'd', maze)
|
||||
if cmd.execute():
|
||||
history.push(cmd) # добавляем только успешный ход
|
||||
|
||||
history.undo() # отмена последнего хода
|
||||
```
|
||||
61
skorohodovsa/task_2/docs/_build/markdown/stage6.md
vendored
Normal file
61
skorohodovsa/task_2/docs/_build/markdown/stage6.md
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Этап 6. Экспериментальная часть
|
||||
|
||||
В шестом этапе проведено сравнение эффективности трёх стратегий поиска пути на лабиринтах разной сложности. Эксперимент реализован в Jupyter Notebook (`practice/main.ipynb`).
|
||||
|
||||
## Подготовка
|
||||
|
||||
Лабиринты загружаются из папки `source/templates` автоматически — все файлы считываются через `os.listdir` и передаются в `TextFileBuilder`. Стратегии собраны в словарь для удобной итерации:
|
||||
|
||||
```python
|
||||
strategies = {
|
||||
"BFS": BFSStrategy(),
|
||||
"DFS": DFSStrategy(),
|
||||
"A*": AStarStrategy(),
|
||||
}
|
||||
```
|
||||
|
||||
## Замеры
|
||||
|
||||
Каждая пара лабиринт + стратегия запускается **10 раз**, результаты усредняются. Это сглаживает разброс из-за кэширования и фоновой активности системы.
|
||||
|
||||
Лабиринты типа `noexit` пропускаются автоматически — стратегия выбрасывает `ValueError`, который перехватывается через `try/except`, и выполнение продолжается.
|
||||
|
||||
Результаты собираются в список словарей и затем преобразуются в `DataFrame` через pandas.
|
||||
|
||||
## Результаты
|
||||
|
||||
### 10×10 (простой путь)
|
||||
|
||||
На маленьких лабиринтах все три алгоритма показывают практически одинаковое время (~0.03–0.07 мс) и одинаковую длину пути. Разница незначительна — лабиринт слишком мал чтобы эвристика A\* давала преимущество.
|
||||
|
||||
### 50×50 (тупики)
|
||||
|
||||
BFS стабильно быстрее DFS по времени при одинаковой длине пути. DFS заходит в каждый тупик до конца и тратит время на возврат, хотя финальный путь совпадает. A\* показывает время между BFS и DFS.
|
||||
|
||||
### 100×100 (запутанный, spaghetti)
|
||||
|
||||
Наиболее показательные результаты:
|
||||
|
||||
| Стратегия | Время (мс) | Длина пути |
|
||||
|-------------|--------------|--------------|
|
||||
| BFS | ~9 | ~210 |
|
||||
| DFS | ~7 | ~2200 |
|
||||
| A\* | ~8 | ~210 |
|
||||
|
||||
DFS быстрее по времени, но находит путь в 10 раз длиннее — обходит почти весь лабиринт. BFS и A\* находят кратчайший путь, A\* при этом чуть быстрее за счёт эвристики.
|
||||
|
||||
### 30×30 (пустой)
|
||||
|
||||
Неожиданный результат: DFS быстрее всех (~0.73 мс против 1.1 у BFS и 2.0 у A\*), хотя находит путь из 379 клеток против 55 у BFS. На пустом поле без стен DFS сразу уходит вглубь без возвратов, тогда как BFS строит очередь и обходит клетки волнами во все стороны — это накладные расходы на структуру данных.
|
||||
|
||||
A\* на пустом лабиринте медленнее всех — накладные расходы на `heapq` и вычисление эвристики не окупаются когда препятствий нет.
|
||||
|
||||
## Выводы
|
||||
|
||||
- **BFS** — надёжный выбор по умолчанию. Всегда находит кратчайший путь, время предсказуемо.
|
||||
- **DFS** — быстрый по времени, но путь непредсказуем. На запутанных лабиринтах может пройти весь граф. Подходит когда важна скорость, а не оптимальность пути.
|
||||
- **A**\* — лучший выбор для больших лабиринтов с препятствиями. Находит кратчайший путь быстрее BFS за счёт эвристики. На простых или пустых лабиринтах проигрывает из-за накладных расходов на приоритетную очередь.
|
||||
|
||||
## Визуализация
|
||||
|
||||
![[results.png]]
|
||||
33
skorohodovsa/task_2/docs/_build/markdown/stage7.md
vendored
Normal file
33
skorohodovsa/task_2/docs/_build/markdown/stage7.md
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Этап 7. Отчёт
|
||||
|
||||
## Описание задачи
|
||||
|
||||
Разработать программу для загрузки лабиринта из файла, поиска пути с выбором алгоритма и сравнения их эффективности. Применены четыре паттерна GoF:
|
||||
|
||||
| Паттерн | Где применён |
|
||||
|--------------|-----------------------------------------------|
|
||||
| **Builder** | `TextFileBuilder` |
|
||||
| **Strategy** | `BFSStrategy`, `DFSStrategy`, `AStarStrategy` |
|
||||
| **Observer** | `ConsoleView` |
|
||||
| **Command** | `MoveCommand`, `CommandHistory` |
|
||||
|
||||
## Диаграмма классов
|
||||
|
||||
## Результаты экспериментов
|
||||
|
||||
| Лабиринт | Быстрее всех | Кратчайший путь |
|
||||
|-------------------|----------------|-------------------|
|
||||
| 10×10 path | все одинаково | все одинаково |
|
||||
| 50×50 deadends | BFS | BFS = A\* |
|
||||
| 100×100 spaghetti | DFS | BFS = A\* |
|
||||
| 30×30 empty | DFS | BFS = A\* |
|
||||
|
||||
**BFS** — надёжный выбор, всегда кратчайший путь.<br />
|
||||
\\\\
|
||||
**DFS** — быстрый, но путь длиннее. На 100×100 обошёл в 10 раз больше клеток.<br />
|
||||
\\\\
|
||||
**A**\* — лучший на больших лабиринтах с препятствиями, проигрывает на простых из-за накладных расходов на `heapq`.
|
||||
|
||||
## Выводы
|
||||
|
||||
Паттерны сделали код расширяемым: новый алгоритм — один класс, новый формат файла — один класс, новый способ отображения — один класс. Без паттернов каждое такое изменение потребовало бы правки существующего кода.
|
||||
103
skorohodovsa/task_2/docs/_build/markdown/task.md
vendored
Normal file
103
skorohodovsa/task_2/docs/_build/markdown/task.md
vendored
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Задание
|
||||
|
||||
## Цель работы
|
||||
|
||||
Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В ходе работы необходимо применить минимум 3 паттерна проектирования из списка GoF, обосновать их выбор и продемонстрировать преимущества такой архитектуры.
|
||||
|
||||
## Общая схема приложения (пример)
|
||||
|
||||
## Выполнение
|
||||
|
||||
### Этап 1. Модель лабиринта (без паттернов, просто классы)
|
||||
|
||||
**Задача:** Создать классы `Cell` и `Maze`, которые представляют карту лабиринта.
|
||||
|
||||
- `Cell` хранит координаты (x, y), флаги `isWall`, `isStart`, `isExit`, метод `isPassable()` (возвращает `True` для прохода, если не стена).
|
||||
- `Maze` хранит двумерный массив клеток, ширину, высоту, ссылки на стартовую и выходную клетку. Методы: `getCell(x, y)`, `getNeighbors(cell)` – возвращает список соседних проходимых клеток (вверх, вниз, влево, вправо, если в пределах границ и не стена).
|
||||
|
||||
**Результат:** Лабиринт можно создать вручную в коде, но загрузку пока не делаем.
|
||||
|
||||
### Этап 2. Загрузка лабиринта из файла – применение паттерна **Builder**
|
||||
|
||||
**Задача:** Реализовать загрузку лабиринта из текстового файла, где `#` – стена, ` ` (пробел) – проход, `S` – старт, `E` – выход.
|
||||
|
||||
- Создать интерфейс `MazeBuilder` с методом `buildFromFile(filename)`.
|
||||
- Реализовать класс `TextFileMazeBuilder`, который читает файл, парсит символы, создаёт объекты `Cell`, задаёт координаты и флаги, после чего возвращает готовый `Maze`.
|
||||
|
||||
Процесс построения лабиринта сложный (парсинг, валидация, установка старта/выхода). Builder скрывает детали создания от клиента. В будущем можно легко добавить другой формат (например, JSON или бинарный) через новую реализацию `MazeBuilder`.
|
||||
|
||||
### Этап 3. Стратегии поиска пути – паттерн **Strategy**
|
||||
|
||||
**Задача:** Реализовать семейство алгоритмов поиска пути от старта до выхода.
|
||||
|
||||
- Создать интерфейс `PathFindingStrategy` с методом `findPath(maze, start, exit)`, возвращающим список клеток пути (от старта до выхода включительно) или пустой список, если пути нет.
|
||||
- Реализовать минимум 3 стратегии:
|
||||
- **BFS** (поиск в ширину) – гарантирует кратчайший путь по количеству шагов.
|
||||
- **DFS** (поиск в глубину) – быстрый, но не обязательно кратчайший.
|
||||
- **A**\* (с эвристикой, например, манхэттенское расстояние) – компромисс между скоростью и оптимальностью.
|
||||
- (Опционально) **Дейкстра** – полезна для взвешенных лабиринтов, но в базовом варианте все шаги имеют вес 1, тогда она совпадает с BFS.
|
||||
|
||||
Каждая стратегия возвращает путь. Для BFS/DFS используйте очередь/стек, для A\* – приоритетную очередь (heapq). Важно: алгоритмы не должны модифицировать сам лабиринт, только читать состояние клеток.
|
||||
|
||||
Strategy позволяет легко переключать алгоритмы во время выполнения, не меняя код остальной программы. Новый алгоритм можно добавить, реализовав интерфейс.
|
||||
|
||||
### Этап 4. Класс-оркестратор – **MazeSolver** (использует Strategy)
|
||||
|
||||
**Задача:** Создать класс, который принимает лабиринт и стратегию, выполняет поиск и собирает статистику.
|
||||
|
||||
- `MazeSolver` содержит поля `maze` и `strategy`.
|
||||
- Метод `setStrategy(strategy)` для динамической смены алгоритма.
|
||||
- Метод `solve()` вызывает `strategy.findPath(...)` и возвращает объект `SearchStats` (время выполнения в миллисекундах, количество посещённых клеток, длина найденного пути).
|
||||
- Для замера времени используйте `time.perf_counter()` до и после вызова стратегии.
|
||||
|
||||
### Этап 5. Визуализация и пошаговое управление – паттерны **Observer** и **Command** (по желанию)
|
||||
|
||||
**5.1. Наблюдатель (Observer)** – обновление консольного интерфейса.
|
||||
|
||||
- Создать интерфейс `Observer` с методом `update(event)`, где `event` может быть строкой или объектом с типом события (`"path_found"`, `"move"`, `"maze_loaded"`).
|
||||
- Реализовать класс `ConsoleView`, который отображает лабиринт, текущее положение игрока (если реализован пошаговый режим) и найденный путь. Метод `render(maze, player_position, path)` рисует карту в консоли.
|
||||
- `MazeSolver` (или отдельный контроллер) может иметь список наблюдателей и уведомлять их при изменении состояния.
|
||||
|
||||
**5.2. Команда (Command)** – для пошагового перемещения игрока по найденному пути (или ручного управления).
|
||||
|
||||
- Создать интерфейс `Command` с методами `execute()` и `undo()`.
|
||||
- Реализовать `MoveCommand`, который принимает игрока (`Player`), направление и изменяет его позицию, сохраняя предыдущую для отмены.
|
||||
- Создать класс `Player`, хранящий текущую клетку.
|
||||
- Консольное меню позволяет вводить команды (W/A/S/D), выполнять `MoveCommand`, при необходимости отменять последний ход (Ctrl+Z). Это опционально, но очень наглядно демонстрирует паттерн.
|
||||
|
||||
*Observer можно реализовать только для вывода сообщений о начале/конце поиска, а Command – для демонстрации undo при ручном исследовании лабиринта.*
|
||||
|
||||
### Этап 6. Экспериментальная часть (аналогично заданию со структурами данных)
|
||||
|
||||
**Задача:** Сравнить эффективность реализованных стратегий на лабиринтах разной сложности.
|
||||
|
||||
1. **Подготовка тестовых лабиринтов:**
|
||||
- Маленький (10×10) с простым путём.
|
||||
- Средний (50×50) с тупиками.
|
||||
- Большой (100×100) с запутанной структурой.
|
||||
- «Пустой» лабиринт (без стен) – для демонстрации максимальной производительности.
|
||||
- «Без выхода» – чтобы проверить обработку отсутствия пути.
|
||||
2. **Замеры:**
|
||||
- Для каждого лабиринта и каждой стратегии запустить `solve()` 5–10 раз, усреднить время, количество посещённых клеток, длину пути.
|
||||
- Записать результаты в CSV: `лабиринт,стратегия,время_мс,посещено_клеток,длина_пути`.
|
||||
3. **Анализ:**
|
||||
- Построить графики для каждого лабиринта.
|
||||
- Проанализировать и написать выводы по итогам (эффективность того или иного алгоритма в разных случаях).
|
||||
4. **Дополнительное задание:** Реализовать взвешенные клетки (например, болото – вес 3, песок – вес 2, асфальт – вес 1) и сравнить Дейкстру с A\* на взвешенном графе.
|
||||
|
||||
### Этап 7. Отчёт
|
||||
|
||||
**Структура отчёта:**
|
||||
|
||||
1. Описание задачи и выбранных паттернов (с диаграммой классов из Mermaid).
|
||||
2. Листинги ключевых классов (можно выборочно) или ссылка на репозиторий.
|
||||
3. Результаты экспериментов (таблицы, графики).
|
||||
4. Анализ эффективности алгоритмов и применимости паттернов.
|
||||
5. Выводы: как ООП и паттерны помогли сделать код гибким и расширяемым. Что было бы сложно изменить без них.
|
||||
|
||||
## Советы
|
||||
|
||||
- Для A\* самая простая эвристика: `abs(x1 - x2) + abs(y1 - y2)`.
|
||||
- При поиске пути надо хранить предшественников (`parent` для каждой посещённой клетки), чтобы восстановить путь.
|
||||
- Для BFS/DFS используй `deque` (очередь) и `list` (стек).
|
||||
- Визуализацию в консоли можно сделать с помощью `os.system('cls' if os.name == 'nt' else 'clear')` для перерисовки.
|
||||
35
skorohodovsa/task_2/docs/make.bat
Normal file
35
skorohodovsa/task_2/docs/make.bat
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
|
||||
|
||||
:end
|
||||
popd
|
||||
70
skorohodovsa/task_2/docs/source/api.md
Normal file
70
skorohodovsa/task_2/docs/source/api.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# API Reference
|
||||
|
||||
## Базовые модели
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: source.models.base
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
## Загрузка лабиринта
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: source.build.builder
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
## Стратегии поиска пути
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: source.strategy.algorithms
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. automodule:: source.strategy.bfs
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. automodule:: source.strategy.dfs
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
.. automodule:: source.strategy.astar
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
## Оркестратор
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: source.strategy.solver
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
## Визуализация
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: source.view.observer
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
|
||||
## Управление игроком
|
||||
|
||||
```{eval-rst}
|
||||
.. automodule:: source.view.command
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
```
|
||||
80
skorohodovsa/task_2/docs/source/conf.py
Normal file
80
skorohodovsa/task_2/docs/source/conf.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# For the full list of built-in configuration values, see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../.."))
|
||||
|
||||
|
||||
project = "Поиск выхода из лабиринта"
|
||||
copyright = "2026, SerKin0"
|
||||
author = "SerKin0"
|
||||
release = "0.0.1"
|
||||
html_title = project
|
||||
|
||||
|
||||
# --- MyST (Markdown) ---
|
||||
myst_enable_extensions = [
|
||||
"dollarmath", # $x$ и $$x$$
|
||||
"amsmath", # \begin{equation}
|
||||
"colon_fence", # ::: блоки
|
||||
]
|
||||
|
||||
exclude_patterns = ["build", "draft.md"]
|
||||
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.napoleon",
|
||||
"sphinx.ext.viewcode",
|
||||
"myst_nb",
|
||||
"sphinx.ext.mathjax",
|
||||
"sphinx_new_tab_link",
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinxcontrib.mermaid",
|
||||
]
|
||||
|
||||
autosummary_generate = True
|
||||
|
||||
autodoc_default_options = {
|
||||
"members": True,
|
||||
"undoc-members": True,
|
||||
"show-inheritance": True,
|
||||
"special-members": "__init__",
|
||||
"inherited-members": False,
|
||||
"exclude-members": "__weakref__",
|
||||
}
|
||||
|
||||
napoleon_google_docstring = True
|
||||
napoleon_numpy_docstring = False
|
||||
napoleon_include_init_with_doc = True
|
||||
napoleon_include_private_with_doc = False
|
||||
|
||||
# --- Тема ---
|
||||
html_permalinks_icon = "<span>#</span>"
|
||||
html_theme = "sphinxawesome_theme"
|
||||
language = "ru"
|
||||
|
||||
html_theme_options = {
|
||||
"navigation_with_keys": True,
|
||||
"globaltoc_collapse": False,
|
||||
"globaltoc_includehidden": False,
|
||||
"show_prev_next": True,
|
||||
"main_nav_links": {},
|
||||
}
|
||||
|
||||
pygments_style = "monokai"
|
||||
pygments_style_dark = "monokai"
|
||||
|
||||
|
||||
# Для подключения CSS (стили иконок)
|
||||
html_css_files = [
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css",
|
||||
]
|
||||
|
||||
# --- HTML ---
|
||||
html_static_path = ["_static"]
|
||||
15
skorohodovsa/task_2/docs/source/index.md
Normal file
15
skorohodovsa/task_2/docs/source/index.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Лабораторная работа "Поиск выхода из лабиринта"
|
||||
|
||||
|
||||
:::{toctree}
|
||||
:maxdepth: 2
|
||||
task
|
||||
stage1
|
||||
stage2
|
||||
stage3
|
||||
stage4
|
||||
stage5
|
||||
stage6
|
||||
stage7
|
||||
api
|
||||
|
||||
91
skorohodovsa/task_2/docs/source/stage1.md
Normal file
91
skorohodovsa/task_2/docs/source/stage1.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# Этап 1. Модель лабиринта
|
||||
|
||||
В первом этапе разработки необходимо создать базовые классы `Cell` и `Maze`, которые представляют карту лабиринта. Паттерны на этом этапе не применяются — только чистые классы.
|
||||
|
||||
## Класс `Cell`
|
||||
|
||||
Клетка — минимальная единица лабиринта. Хранит координаты и тип: стена, старт, выход или пустая.
|
||||
|
||||
По условию задания клетка должна иметь флаги `isWall`, `isStart`, `isExit` и метод `isPassable()`. В реализации флаги оформлены как **свойства** (`@property`) с сеттерами — это позволяет автоматически сбрасывать остальные флаги при установке нового типа.
|
||||
|
||||
```python
|
||||
cell = Cell(1, 1)
|
||||
cell.is_wall = True
|
||||
```
|
||||
|
||||
Типы клетки взаимоисключают друг друга — клетка не может быть одновременно стеной и стартом. Логика сброса вынесена в приватный метод `_clear_flags()`.
|
||||
|
||||
### Символьное представление
|
||||
|
||||
Для вывода лабиринта в консоль каждая клетка возвращает символ через `__str__`. Символы берутся из `cell_mapping` в `source/settings.py`, что позволяет менять отображение без правки классов:
|
||||
|
||||
|Тип|Символ по умолчанию|
|
||||
|---|---|
|
||||
|Стена|`#`|
|
||||
|Старт|`S`|
|
||||
|Выход|`E`|
|
||||
|Пустая||
|
||||
|
||||
## Класс `Maze`
|
||||
|
||||
Лабиринт хранит двумерный список клеток и предоставляет методы для работы с ними.
|
||||
|
||||
По условию задания требовались методы `getCell(x, y)` и `getNeighbors(cell)`. В реализации добавлено несколько вещей сверх задания:
|
||||
|
||||
### Именование методов
|
||||
|
||||
Задание написано в стиле Java/pseudocode — названия методов и полей используют `camelCase` (`isWall`, `getCell`, `isPassable`). В Python принят другой стандарт именования — **PEP 8**, который предписывает `snake_case` для методов и атрибутов. Поэтому все названия были приведены к Python стилю:
|
||||
|
||||
|Задание|Реализация|
|
||||
|---|---|
|
||||
|`isWall`|`is_wall`|
|
||||
|`isStart`|`is_start`|
|
||||
|`isExit`|`is_exit`|
|
||||
|`isPassable()`|`is_possible()`|
|
||||
|`getCell(x, y)`|`get_cell(x, y)`|
|
||||
|`getNeighbors(cell)`|`get_neighbors(x, y)`|
|
||||
|`buildFromFile(filename)`|`build_from_file(filename)`|
|
||||
|
||||
Это соответствует стандарту оформления кода на Python и делает API классов идиоматичным для языка.
|
||||
|
||||
### Индексация `maze[row, col]`
|
||||
|
||||
Вместо явного вызова `get_cell()` реализованы `__getitem__` и `__setitem__`, что позволяет обращаться к клеткам естественным образом:
|
||||
|
||||
```python
|
||||
maze[0, 0] = cell_mapping['wall'] # установить стену
|
||||
cell = maze[2, 3] # получить клетку
|
||||
```
|
||||
|
||||
Обратите внимание: индексация идёт в формате `[row, col]`, то есть сначала строка (Y), потом столбец (X) — аналогично numpy.
|
||||
|
||||
### Свойства `start` и `exit`
|
||||
|
||||
Добавлены свойства для быстрого получения стартовой и выходной клетки без ручного обхода:
|
||||
|
||||
```python
|
||||
maze.start # Cell или None
|
||||
maze.exit # Cell или None
|
||||
```
|
||||
|
||||
Это оказалось необходимым при реализации алгоритмов поиска пути — стратегии получают `start` и `exit` автоматически из лабиринта.
|
||||
|
||||
### Свойство `shape`
|
||||
|
||||
По аналогии с numpy добавлено свойство `shape`, возвращающее `(height, width)`:
|
||||
|
||||
```python
|
||||
rows, cols = maze.shape
|
||||
```
|
||||
|
||||
Используется в стратегиях поиска и тестах для итерации по лабиринту.
|
||||
|
||||
### `get_neighbors`
|
||||
|
||||
Метод возвращает список проходимых соседей клетки по четырём направлениям. Стены и клетки за границей лабиринта исключаются автоматически. Если переданные координаты вне границ — возвращает `None`.
|
||||
|
||||
```python
|
||||
neighbors = maze.get_neighbors(2, 2) # список Cell
|
||||
```
|
||||
|
||||
Направления обхода: вниз → вправо → вверх → влево (порядок влияет на поведение DFS).
|
||||
60
skorohodovsa/task_2/docs/source/stage2.md
Normal file
60
skorohodovsa/task_2/docs/source/stage2.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Этап 2. Загрузка лабиринта из файла
|
||||
|
||||
Во втором этапе реализована загрузка лабиринта из текстового файла с применением паттерна **Builder**.
|
||||
|
||||
## Паттерн Builder
|
||||
|
||||
Процесс создания лабиринта из файла включает несколько шагов: чтение файла, валидацию структуры, парсинг символов и заполнение клеток. Builder скрывает эти детали от клиента — снаружи виден только один метод `build_from_file()`, внутри которого сосредоточена вся логика построения.
|
||||
|
||||
Дополнительное преимущество: в будущем можно легко добавить новый формат (например, JSON или бинарный) через новую реализацию `MazeBuilder` без изменения остального кода.
|
||||
|
||||
## Класс `MazeBuilder`
|
||||
|
||||
Абстрактный базовый класс — интерфейс паттерна Builder. Объявляет единственный метод `build_from_file()`, который обязан реализовать каждый конкретный строитель.
|
||||
|
||||
По условию задания интерфейс назывался `MazeBuilder` с методом `buildFromFile`. В реализации название метода приведено к **PEP 8** — `build_from_file`. Сам класс оформлен через `ABC` — попытка создать объект `MazeBuilder()` напрямую вызовет `TypeError`.
|
||||
|
||||
## Класс `TextFileBuilder`
|
||||
|
||||
Конкретная реализация строителя для текстовых файлов. Загружает лабиринт из `.txt` файла где `#` — стена, — проход, `S` — старт, `E` — выход.
|
||||
|
||||
Процесс построения разбит на три приватных шага:
|
||||
|
||||
### `_read_file`
|
||||
|
||||
Читает файл построчно и обрезает символы переноса строки `\n` и `\r`. Возвращает список строк — каждая строка соответствует одной строке лабиринта.
|
||||
|
||||
### `_test_text_maze`
|
||||
|
||||
Валидирует структуру: проверяет что все строки одинаковой длины. Если нет — лабиринт некорректен и `_create_maze` выбросит `ValueError`.
|
||||
|
||||
Реализован как `@staticmethod` — не использует состояние объекта, только входные данные.
|
||||
|
||||
### `_create_maze`
|
||||
|
||||
Создаёт объект `Maze` нужного размера и заполняет его клетки символами из файла через `maze[y, x] = symbol`. Тип каждой клетки определяется автоматически через `cell_mapping` в `__setitem__` лабиринта.
|
||||
|
||||
## Использование
|
||||
|
||||
```python
|
||||
from source.build.builder import TextFileBuilder
|
||||
|
||||
maze = TextFileBuilder().build_from_file('source/templates/10x10_path_v1.txt')
|
||||
print(maze)
|
||||
```
|
||||
|
||||
## Известная ошибка
|
||||
|
||||
В текущей реализации `_create_maze` есть опечатка при вычислении `width`:
|
||||
|
||||
```python
|
||||
height, width = len(text_maze), len(text_maze) # width всегда равен height
|
||||
```
|
||||
|
||||
Правильная версия:
|
||||
|
||||
```python
|
||||
height, width = len(text_maze), len(text_maze[0])
|
||||
```
|
||||
|
||||
На квадратных лабиринтах (10×10, 50×50) это не проявляется, но на прямоугольных даст некорректный результат.
|
||||
90
skorohodovsa/task_2/docs/source/stage3.md
Normal file
90
skorohodovsa/task_2/docs/source/stage3.md
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
# Этап 3. Стратегии поиска пути
|
||||
|
||||
В третьем этапе реализованы алгоритмы поиска пути с применением паттерна **Strategy**.
|
||||
|
||||
## Паттерн Strategy
|
||||
|
||||
Все три алгоритма реализуют один интерфейс `PathFindingStrategy`. Это позволяет переключать алгоритм в любой момент без изменения кода клиента — достаточно передать другой объект стратегии:
|
||||
|
||||
```python
|
||||
solver = MazeSolver(maze, BFSStrategy())
|
||||
solver.set_strategy(AStarStrategy())
|
||||
```
|
||||
|
||||
Новый алгоритм добавляется реализацией интерфейса — остальной код трогать не нужно.
|
||||
|
||||
## Структура пакета
|
||||
|
||||
Стратегии разбиты по отдельным файлам, а `__init__.py` собирает всё в один импорт:
|
||||
|
||||
```
|
||||
source/strategy/
|
||||
├── __init__.py ← единственный импорт для пользователя
|
||||
├── algorithms.py ← базовый класс PathFindingStrategy
|
||||
├── bfs.py
|
||||
├── dfs.py
|
||||
└── astar.py
|
||||
```
|
||||
|
||||
```python
|
||||
from source.strategy import BFSStrategy, DFSStrategy, AStarStrategy
|
||||
```
|
||||
|
||||
## Класс `PathFindingStrategy`
|
||||
|
||||
Абстрактный базовый класс — интерфейс паттерна. Объявляет абстрактный метод `find_path()` и содержит два вспомогательных метода, общих для всех стратегий.
|
||||
|
||||
По условию задания метод назывался `findPath` — приведён к **PEP 8** как `find_path`.
|
||||
|
||||
### `_validate`
|
||||
|
||||
Добавлен в процессе разработки — изначально в задании не было требования к обработке отсутствия старта или выхода. Проблема проявилась при тестировании лабиринтов типа `noexit`: алгоритм падал с `AttributeError` внутри, вместо понятного сообщения.
|
||||
|
||||
`_validate` подставляет `start` и `exit` из лабиринта если они не переданы явно, и выбрасывает `ValueError` с понятным сообщением если клетки не найдены:
|
||||
|
||||
```python
|
||||
start, exit = self._validate(maze, start, exit)
|
||||
```
|
||||
|
||||
Вынесен в базовый класс чтобы не дублировать в каждом алгоритме.
|
||||
|
||||
### `_reconstruct_path`
|
||||
|
||||
Восстанавливает путь по словарю предков `came_from`. Все три алгоритма строят этот словарь одинаково — `{клетка: откуда_пришли}` — поэтому восстановление вынесено в общий метод базового класса.
|
||||
|
||||
Алгоритм идёт от выхода к старту по цепочке предков, затем разворачивает список:
|
||||
|
||||
```
|
||||
exit → D → C → B → start (идём по came_from)
|
||||
start → B → C → D → exit (после reverse)
|
||||
```
|
||||
|
||||
## Алгоритмы
|
||||
|
||||
### BFS — `BFSStrategy`
|
||||
|
||||
Поиск в ширину. Использует `deque` как очередь (FIFO) — каждый раз берём самую старую клетку из начала. Это гарантирует послойный обход и кратчайший путь по количеству шагов.
|
||||
|
||||
Сложность: O(V + E) по времени и памяти.
|
||||
|
||||
### DFS — `DFSStrategy`
|
||||
|
||||
Поиск в глубину. Использует `list` как стек (LIFO) — каждый раз берём самую свежую клетку с конца. Алгоритм ныряет вглубь по одному направлению до тупика, затем возвращается.
|
||||
|
||||
Не гарантирует кратчайший путь. На запутанных лабиринтах может обойти почти все клетки прежде чем найти выход, хотя по времени часто быстрее BFS из-за меньших накладных расходов на структуру данных.
|
||||
|
||||
Сложность: O(V + E) по времени и памяти.
|
||||
|
||||
### A* — `AStarStrategy`
|
||||
|
||||
Использует `heapq` как приоритетную очередь. На каждом шаге выбирает клетку с минимальным значением `f = g + h`, где `g` — стоимость пути от старта, `h` — манхэттенская эвристика до выхода.
|
||||
|
||||
Эвристика направляет поиск в сторону выхода, поэтому A* обходит меньше клеток чем BFS при том же гарантированно кратчайшем пути.
|
||||
|
||||
В кортеж приоритетной очереди добавлен счётчик `counter` как tie-breaker — без него `heapq` попытался бы сравнивать объекты `Cell` при одинаковом `f`, что вызвало бы `TypeError`:
|
||||
|
||||
```python
|
||||
heapq.heappush(open_heap, (f, counter, neighbor))
|
||||
```
|
||||
|
||||
Сложность: O(E · log V) в худшем случае.
|
||||
44
skorohodovsa/task_2/docs/source/stage4.md
Normal file
44
skorohodovsa/task_2/docs/source/stage4.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Этап 4. Класс-оркестратор MazeSolver
|
||||
|
||||
В четвёртом этапе реализован класс `MazeSolver`, который объединяет лабиринт и стратегию поиска, выполняет поиск и собирает статистику.
|
||||
|
||||
## Роль в архитектуре
|
||||
|
||||
`MazeSolver` — точка входа для клиентского кода. Он не знает деталей ни одного алгоритма и не работает напрямую с клетками лабиринта — только делегирует задачу стратегии и замеряет время:
|
||||
|
||||
```python
|
||||
solver = MazeSolver(maze, BFSStrategy())
|
||||
stats = solver.solve()
|
||||
print(stats)
|
||||
# Время: 0.041 мс | Посещено клеток: 13 | Длина пути: 13
|
||||
```
|
||||
|
||||
## Класс `SearchStats`
|
||||
|
||||
Оформлен через `@dataclass` — это избавляет от ручного `__init__` и автоматически даёт `__repr__`. Хранит четыре поля: время выполнения, количество посещённых клеток, длину пути и сам путь как список клеток.
|
||||
|
||||
`__str__` переопределён для удобного вывода в консоль и отчётах.
|
||||
|
||||
### Ограничение
|
||||
|
||||
В текущей реализации `visited_count` и `path_length` всегда равны друг другу — оба вычисляются как `len(path)`. Это потому что стратегии возвращают только финальный путь, а не все посещённые клетки. Чтобы получить точное количество посещений, потребовалось бы дорабатывать каждую стратегию — добавлять счётчик внутри `find_path`. На данном этапе это сознательное упрощение.
|
||||
|
||||
## Класс `MazeSolver`
|
||||
|
||||
### `set_strategy`
|
||||
|
||||
Позволяет менять алгоритм без пересоздания солвера. Это и есть основная демонстрация паттерна Strategy в действии — один объект, разные алгоритмы:
|
||||
|
||||
```python
|
||||
solver = MazeSolver(maze, BFSStrategy())
|
||||
stats_bfs = solver.solve()
|
||||
|
||||
solver.set_strategy(AStarStrategy())
|
||||
stats_astar = solver.solve()
|
||||
```
|
||||
|
||||
### `solve`
|
||||
|
||||
Замеряет время через `time.perf_counter()` — самый точный таймер в Python для коротких интервалов, не зависящий от системных часов. Результат переводится в миллисекунды умножением на 1000.
|
||||
|
||||
`start` и `exit` можно не передавать — стратегия найдёт их сама через `_validate`. Явная передача нужна только если хочется запустить поиск не от стандартного старта, а от произвольной клетки.
|
||||
84
skorohodovsa/task_2/docs/source/stage5.md
Normal file
84
skorohodovsa/task_2/docs/source/stage5.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# Этап 5. Визуализация и пошаговое управление
|
||||
|
||||
В пятом этапе реализованы два паттерна: **Observer** для отображения событий и **Command** для пошагового управления игроком.
|
||||
|
||||
## 5.1. Паттерн Observer
|
||||
|
||||
### Идея
|
||||
|
||||
`MazeSolver` и игровой цикл не знают как именно отображать происходящее — они просто генерируют события. Наблюдатели подписываются на эти события и реагируют по своему усмотрению. Это позволяет в будущем добавить, например, `FileLogger` или графический интерфейс без изменения основного кода.
|
||||
|
||||
### Класс `Event`
|
||||
|
||||
Оформлен через `@dataclass`. Хранит тип события строкой и словарь `payload` с дополнительными данными. Поддерживаются четыре типа событий:
|
||||
|
||||
|Тип|Когда генерируется|
|
||||
|---|---|
|
||||
|`maze_loaded`|Лабиринт загружен из файла|
|
||||
|`path_found`|Алгоритм нашёл путь|
|
||||
|`no_path`|Путь не найден|
|
||||
|`move`|Игрок сделал ход|
|
||||
|
||||
### Класс `Observer`
|
||||
|
||||
Абстрактный базовый класс с единственным методом `update(event)`. Любой наблюдатель обязан его реализовать.
|
||||
|
||||
### Класс `ConsoleView`
|
||||
|
||||
Конкретная реализация наблюдателя. Обрабатывает события через `match/case` и вызывает `render()` для перерисовки лабиринта.
|
||||
|
||||
Метод `render()` принимает лабиринт, опциональную позицию игрока и опциональный путь. Путь преобразуется в `set` для быстрой проверки принадлежности клетки — это O(1) вместо O(n) при каждом обходе:
|
||||
|
||||
```python
|
||||
path_set = set(path) if path else set()
|
||||
```
|
||||
|
||||
Лабиринт обрамляется рамкой из `+` и `─` для читаемости в консоли. Символы игрока и пути вынесены в константы класса — легко поменять без правки логики:
|
||||
|
||||
```python
|
||||
PLAYER_SYMBOL = "P"
|
||||
PATH_SYMBOL = "·"
|
||||
```
|
||||
|
||||
## 5.2. Паттерн Command
|
||||
|
||||
### Идея
|
||||
|
||||
Каждое перемещение игрока оборачивается в объект `MoveCommand`. Это позволяет сохранить предыдущее состояние и отменить ход — реализация `undo` становится тривиальной.
|
||||
|
||||
### Класс `Player`
|
||||
|
||||
Простой контейнер для текущей клетки игрока. Намеренно минималистичный — вся логика перемещения и проверок находится в команде, а не в игроке.
|
||||
|
||||
### Класс `Command`
|
||||
|
||||
Абстрактный интерфейс с двумя методами: `execute()` и `undo()`. `execute()` возвращает `bool` — это отличие от классического варианта паттерна, где команды не возвращают значений. Возврат `False` нужен чтобы не добавлять неуспешный ход в историю.
|
||||
|
||||
### Класс `MoveCommand`
|
||||
|
||||
Хранит ссылку на игрока, направление и лабиринт. При `execute()` проверяет проходимость целевой клетки, сохраняет текущую в `_prev_cell` и перемещает игрока. При `undo()` восстанавливает `_prev_cell`.
|
||||
|
||||
Направления вынесены в словарь `DIRECTIONS` на уровне модуля:
|
||||
|
||||
```python
|
||||
DIRECTIONS = {
|
||||
"w": (0, -1), # вверх
|
||||
"s": (0, 1), # вниз
|
||||
"a": (-1, 0), # влево
|
||||
"d": (1, 0), # вправо
|
||||
}
|
||||
```
|
||||
|
||||
### Класс `CommandHistory`
|
||||
|
||||
Стек выполненных команд. Хранит только успешные ходы — неуспешные (`execute()` вернул `False`) в историю не добавляются. `undo()` снимает последнюю команду со стека и вызывает её `undo()`.
|
||||
|
||||
Пример игрового цикла:
|
||||
|
||||
```python
|
||||
cmd = MoveCommand(player, 'd', maze)
|
||||
if cmd.execute():
|
||||
history.push(cmd) # добавляем только успешный ход
|
||||
|
||||
history.undo() # отмена последнего хода
|
||||
```
|
||||
63
skorohodovsa/task_2/docs/source/stage6.md
Normal file
63
skorohodovsa/task_2/docs/source/stage6.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Этап 6. Экспериментальная часть
|
||||
|
||||
В шестом этапе проведено сравнение эффективности трёх стратегий поиска пути на лабиринтах разной сложности. Эксперимент реализован в Jupyter Notebook (`practice/main.ipynb`).
|
||||
|
||||
## Подготовка
|
||||
|
||||
Лабиринты загружаются из папки `source/templates` автоматически — все файлы считываются через `os.listdir` и передаются в `TextFileBuilder`. Стратегии собраны в словарь для удобной итерации:
|
||||
|
||||
```python
|
||||
strategies = {
|
||||
"BFS": BFSStrategy(),
|
||||
"DFS": DFSStrategy(),
|
||||
"A*": AStarStrategy(),
|
||||
}
|
||||
```
|
||||
|
||||
## Замеры
|
||||
|
||||
Каждая пара лабиринт + стратегия запускается **10 раз**, результаты усредняются. Это сглаживает разброс из-за кэширования и фоновой активности системы.
|
||||
|
||||
Лабиринты типа `noexit` пропускаются автоматически — стратегия выбрасывает `ValueError`, который перехватывается через `try/except`, и выполнение продолжается.
|
||||
|
||||
Результаты собираются в список словарей и затем преобразуются в `DataFrame` через pandas.
|
||||
|
||||
## Результаты
|
||||
|
||||
### 10×10 (простой путь)
|
||||
|
||||
На маленьких лабиринтах все три алгоритма показывают практически одинаковое время (~0.03–0.07 мс) и одинаковую длину пути. Разница незначительна — лабиринт слишком мал чтобы эвристика A* давала преимущество.
|
||||
|
||||
### 50×50 (тупики)
|
||||
|
||||
BFS стабильно быстрее DFS по времени при одинаковой длине пути. DFS заходит в каждый тупик до конца и тратит время на возврат, хотя финальный путь совпадает. A* показывает время между BFS и DFS.
|
||||
|
||||
### 100×100 (запутанный, spaghetti)
|
||||
|
||||
Наиболее показательные результаты:
|
||||
|
||||
|Стратегия|Время (мс)|Длина пути|
|
||||
|---|---|---|
|
||||
|BFS|~9|~210|
|
||||
|DFS|~7|~2200|
|
||||
|A*|~8|~210|
|
||||
|
||||
DFS быстрее по времени, но находит путь в 10 раз длиннее — обходит почти весь лабиринт. BFS и A* находят кратчайший путь, A* при этом чуть быстрее за счёт эвристики.
|
||||
|
||||
### 30×30 (пустой)
|
||||
|
||||
Неожиданный результат: DFS быстрее всех (~0.73 мс против 1.1 у BFS и 2.0 у A*), хотя находит путь из 379 клеток против 55 у BFS. На пустом поле без стен DFS сразу уходит вглубь без возвратов, тогда как BFS строит очередь и обходит клетки волнами во все стороны — это накладные расходы на структуру данных.
|
||||
|
||||
A* на пустом лабиринте медленнее всех — накладные расходы на `heapq` и вычисление эвристики не окупаются когда препятствий нет.
|
||||
|
||||
## Выводы
|
||||
|
||||
- **BFS** — надёжный выбор по умолчанию. Всегда находит кратчайший путь, время предсказуемо.
|
||||
|
||||
- **DFS** — быстрый по времени, но путь непредсказуем. На запутанных лабиринтах может пройти весь граф. Подходит когда важна скорость, а не оптимальность пути.
|
||||
|
||||
- **A*** — лучший выбор для больших лабиринтов с препятствиями. Находит кратчайший путь быстрее BFS за счёт эвристики. На простых или пустых лабиринтах проигрывает из-за накладных расходов на приоритетную очередь.
|
||||
|
||||
## Визуализация
|
||||
|
||||
![[results.png]]
|
||||
81
skorohodovsa/task_2/docs/source/stage7.md
Normal file
81
skorohodovsa/task_2/docs/source/stage7.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Этап 7. Отчёт
|
||||
|
||||
## Описание задачи
|
||||
|
||||
Разработать программу для загрузки лабиринта из файла, поиска пути с выбором алгоритма и сравнения их эффективности. Применены четыре паттерна GoF:
|
||||
|
||||
|Паттерн|Где применён|
|
||||
|---|---|
|
||||
|**Builder**|`TextFileBuilder`|
|
||||
|**Strategy**|`BFSStrategy`, `DFSStrategy`, `AStarStrategy`|
|
||||
|**Observer**|`ConsoleView`|
|
||||
|**Command**|`MoveCommand`, `CommandHistory`|
|
||||
|
||||
## Диаграмма классов
|
||||
|
||||
```{mermaid}
|
||||
classDiagram
|
||||
class Cell {
|
||||
+int x, y
|
||||
+bool is_wall, is_start, is_exit
|
||||
+is_possible() bool
|
||||
}
|
||||
class Maze {
|
||||
+get_cell(x, y) Cell
|
||||
+get_neighbors(x, y) list
|
||||
+start, exit, shape
|
||||
}
|
||||
class MazeBuilder { <<abstract>> }
|
||||
class TextFileBuilder
|
||||
class PathFindingStrategy {
|
||||
<<abstract>>
|
||||
+find_path(maze, start, exit) list
|
||||
}
|
||||
class BFSStrategy
|
||||
class DFSStrategy
|
||||
class AStarStrategy
|
||||
class MazeSolver {
|
||||
+set_strategy(strategy)
|
||||
+solve() SearchStats
|
||||
}
|
||||
class ConsoleView {
|
||||
+update(event)
|
||||
+render(maze, player, path)
|
||||
}
|
||||
class MoveCommand {
|
||||
+execute() bool
|
||||
+undo()
|
||||
}
|
||||
class CommandHistory {
|
||||
+push(command)
|
||||
+undo() bool
|
||||
}
|
||||
|
||||
Maze *-- Cell
|
||||
MazeBuilder <|-- TextFileBuilder
|
||||
TextFileBuilder ..> Maze : creates
|
||||
PathFindingStrategy <|-- BFSStrategy
|
||||
PathFindingStrategy <|-- DFSStrategy
|
||||
PathFindingStrategy <|-- AStarStrategy
|
||||
MazeSolver --> PathFindingStrategy
|
||||
MazeSolver --> Maze
|
||||
MoveCommand --> Maze
|
||||
CommandHistory --> MoveCommand
|
||||
```
|
||||
|
||||
## Результаты экспериментов
|
||||
|
||||
| Лабиринт | Быстрее всех | Кратчайший путь |
|
||||
| ----------------- | ------------- | --------------- |
|
||||
| 10×10 path | все одинаково | все одинаково |
|
||||
| 50×50 deadends | BFS | BFS = A* |
|
||||
| 100×100 spaghetti | DFS | BFS = A* |
|
||||
| 30×30 empty | DFS | BFS = A* |
|
||||
|
||||
**BFS** — надёжный выбор, всегда кратчайший путь.
|
||||
**DFS** — быстрый, но путь длиннее. На 100×100 обошёл в 10 раз больше клеток.
|
||||
**A*** — лучший на больших лабиринтах с препятствиями, проигрывает на простых из-за накладных расходов на `heapq`.
|
||||
|
||||
## Выводы
|
||||
|
||||
Паттерны сделали код расширяемым: новый алгоритм — один класс, новый формат файла — один класс, новый способ отображения — один класс. Без паттернов каждое такое изменение потребовало бы правки существующего кода.
|
||||
183
skorohodovsa/task_2/docs/source/task.md
Normal file
183
skorohodovsa/task_2/docs/source/task.md
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
# Задание
|
||||
|
||||
## Цель работы
|
||||
|
||||
Разработать гибкую, расширяемую программу для загрузки лабиринта из файла, поиска пути от старта до выхода с возможностью выбора алгоритма, визуализации процесса и экспериментального сравнения алгоритмов. В ходе работы необходимо применить минимум 3 паттерна проектирования из списка GoF, обосновать их выбор и продемонстрировать преимущества такой архитектуры.
|
||||
|
||||
## Общая схема приложения (пример)
|
||||
|
||||
```{mermaid}
|
||||
classDiagram
|
||||
class Maze {
|
||||
-Cell[] cells
|
||||
-int width, height
|
||||
-Cell start
|
||||
-Cell exit
|
||||
+getCell(x,y): Cell
|
||||
+getNeighbors(cell): List~Cell~
|
||||
}
|
||||
|
||||
class Cell {
|
||||
-int x, y
|
||||
-bool isWall
|
||||
-bool isStart
|
||||
-bool isExit
|
||||
+isPassable(): bool
|
||||
}
|
||||
|
||||
class MazeBuilder {
|
||||
<<interface>>
|
||||
+buildFromFile(filename): Maze
|
||||
}
|
||||
|
||||
class TextFileMazeBuilder {
|
||||
+buildFromFile(filename): Maze
|
||||
}
|
||||
|
||||
class PathFindingStrategy {
|
||||
<<interface>>
|
||||
+findPath(maze, start, exit): List~Cell~
|
||||
}
|
||||
|
||||
class BFSStrategy
|
||||
class DFSStrategy
|
||||
class AStarStrategy
|
||||
class DijkstraStrategy
|
||||
|
||||
class SearchStats {
|
||||
+timeMs: float
|
||||
+visitedCells: int
|
||||
+pathLength: int
|
||||
}
|
||||
|
||||
class MazeSolver {
|
||||
-Maze maze
|
||||
-PathFindingStrategy strategy
|
||||
+setStrategy(strategy)
|
||||
+solve(): SearchStats
|
||||
}
|
||||
|
||||
class Command {
|
||||
<<interface>>
|
||||
+execute()
|
||||
+undo()
|
||||
}
|
||||
|
||||
class MoveCommand {
|
||||
-Player player
|
||||
-Direction dir
|
||||
-Cell previousCell
|
||||
+execute()
|
||||
+undo()
|
||||
}
|
||||
|
||||
class Player {
|
||||
-Cell currentCell
|
||||
+moveTo(cell)
|
||||
}
|
||||
|
||||
class Observer {
|
||||
<<interface>>
|
||||
+update(event)
|
||||
}
|
||||
|
||||
class ConsoleView {
|
||||
+update(event)
|
||||
+render(maze, player, path)
|
||||
}
|
||||
|
||||
MazeBuilder <|.. TextFileMazeBuilder
|
||||
MazeBuilder --> Maze : creates
|
||||
PathFindingStrategy <|.. BFSStrategy
|
||||
PathFindingStrategy <|.. DFSStrategy
|
||||
PathFindingStrategy <|.. AStarStrategy
|
||||
PathFindingStrategy <|.. DijkstraStrategy
|
||||
MazeSolver --> PathFindingStrategy : uses
|
||||
MazeSolver --> Maze : uses
|
||||
Command <|.. MoveCommand
|
||||
MoveCommand --> Player
|
||||
Player --> Cell
|
||||
Observer <|.. ConsoleView
|
||||
MazeSolver --> Observer : notifies
|
||||
```
|
||||
|
||||
## Выполнение
|
||||
|
||||
### Этап 1. Модель лабиринта (без паттернов, просто классы)
|
||||
**Задача:** Создать классы `Cell` и `Maze`, которые представляют карту лабиринта.
|
||||
- `Cell` хранит координаты (x, y), флаги `isWall`, `isStart`, `isExit`, метод `isPassable()` (возвращает `True` для прохода, если не стена).
|
||||
- `Maze` хранит двумерный массив клеток, ширину, высоту, ссылки на стартовую и выходную клетку. Методы: `getCell(x, y)`, `getNeighbors(cell)` – возвращает список соседних проходимых клеток (вверх, вниз, влево, вправо, если в пределах границ и не стена).
|
||||
|
||||
**Результат:** Лабиринт можно создать вручную в коде, но загрузку пока не делаем.
|
||||
|
||||
### Этап 2. Загрузка лабиринта из файла – применение паттерна **Builder**
|
||||
**Задача:** Реализовать загрузку лабиринта из текстового файла, где `#` – стена, ` ` (пробел) – проход, `S` – старт, `E` – выход.
|
||||
- Создать интерфейс `MazeBuilder` с методом `buildFromFile(filename)`.
|
||||
- Реализовать класс `TextFileMazeBuilder`, который читает файл, парсит символы, создаёт объекты `Cell`, задаёт координаты и флаги, после чего возвращает готовый `Maze`.
|
||||
|
||||
Процесс построения лабиринта сложный (парсинг, валидация, установка старта/выхода). Builder скрывает детали создания от клиента. В будущем можно легко добавить другой формат (например, JSON или бинарный) через новую реализацию `MazeBuilder`.
|
||||
|
||||
### Этап 3. Стратегии поиска пути – паттерн **Strategy**
|
||||
**Задача:** Реализовать семейство алгоритмов поиска пути от старта до выхода.
|
||||
- Создать интерфейс `PathFindingStrategy` с методом `findPath(maze, start, exit)`, возвращающим список клеток пути (от старта до выхода включительно) или пустой список, если пути нет.
|
||||
- Реализовать минимум 3 стратегии:
|
||||
- **BFS** (поиск в ширину) – гарантирует кратчайший путь по количеству шагов.
|
||||
- **DFS** (поиск в глубину) – быстрый, но не обязательно кратчайший.
|
||||
- **A*** (с эвристикой, например, манхэттенское расстояние) – компромисс между скоростью и оптимальностью.
|
||||
- (Опционально) **Дейкстра** – полезна для взвешенных лабиринтов, но в базовом варианте все шаги имеют вес 1, тогда она совпадает с BFS.
|
||||
|
||||
Каждая стратегия возвращает путь. Для BFS/DFS используйте очередь/стек, для A* – приоритетную очередь (heapq). Важно: алгоритмы не должны модифицировать сам лабиринт, только читать состояние клеток.
|
||||
|
||||
Strategy позволяет легко переключать алгоритмы во время выполнения, не меняя код остальной программы. Новый алгоритм можно добавить, реализовав интерфейс.
|
||||
|
||||
### Этап 4. Класс-оркестратор – **MazeSolver** (использует Strategy)
|
||||
**Задача:** Создать класс, который принимает лабиринт и стратегию, выполняет поиск и собирает статистику.
|
||||
- `MazeSolver` содержит поля `maze` и `strategy`.
|
||||
- Метод `setStrategy(strategy)` для динамической смены алгоритма.
|
||||
- Метод `solve()` вызывает `strategy.findPath(...)` и возвращает объект `SearchStats` (время выполнения в миллисекундах, количество посещённых клеток, длина найденного пути).
|
||||
- Для замера времени используйте `time.perf_counter()` до и после вызова стратегии.
|
||||
|
||||
### Этап 5. Визуализация и пошаговое управление – паттерны **Observer** и **Command** (по желанию)
|
||||
**5.1. Наблюдатель (Observer)** – обновление консольного интерфейса.
|
||||
- Создать интерфейс `Observer` с методом `update(event)`, где `event` может быть строкой или объектом с типом события (`"path_found"`, `"move"`, `"maze_loaded"`).
|
||||
- Реализовать класс `ConsoleView`, который отображает лабиринт, текущее положение игрока (если реализован пошаговый режим) и найденный путь. Метод `render(maze, player_position, path)` рисует карту в консоли.
|
||||
- `MazeSolver` (или отдельный контроллер) может иметь список наблюдателей и уведомлять их при изменении состояния.
|
||||
|
||||
**5.2. Команда (Command)** – для пошагового перемещения игрока по найденному пути (или ручного управления).
|
||||
- Создать интерфейс `Command` с методами `execute()` и `undo()`.
|
||||
- Реализовать `MoveCommand`, который принимает игрока (`Player`), направление и изменяет его позицию, сохраняя предыдущую для отмены.
|
||||
- Создать класс `Player`, хранящий текущую клетку.
|
||||
- Консольное меню позволяет вводить команды (W/A/S/D), выполнять `MoveCommand`, при необходимости отменять последний ход (Ctrl+Z). Это опционально, но очень наглядно демонстрирует паттерн.
|
||||
|
||||
*Observer можно реализовать только для вывода сообщений о начале/конце поиска, а Command – для демонстрации undo при ручном исследовании лабиринта.*
|
||||
|
||||
### Этап 6. Экспериментальная часть (аналогично заданию со структурами данных)
|
||||
**Задача:** Сравнить эффективность реализованных стратегий на лабиринтах разной сложности.
|
||||
1. **Подготовка тестовых лабиринтов:**
|
||||
- Маленький (10×10) с простым путём.
|
||||
- Средний (50×50) с тупиками.
|
||||
- Большой (100×100) с запутанной структурой.
|
||||
- «Пустой» лабиринт (без стен) – для демонстрации максимальной производительности.
|
||||
- «Без выхода» – чтобы проверить обработку отсутствия пути.
|
||||
2. **Замеры:**
|
||||
- Для каждого лабиринта и каждой стратегии запустить `solve()` 5–10 раз, усреднить время, количество посещённых клеток, длину пути.
|
||||
- Записать результаты в CSV: `лабиринт,стратегия,время_мс,посещено_клеток,длина_пути`.
|
||||
3. **Анализ:**
|
||||
- Построить графики для каждого лабиринта.
|
||||
- Проанализировать и написать выводы по итогам (эффективность того или иного алгоритма в разных случаях).
|
||||
|
||||
4. **Дополнительное задание:** Реализовать взвешенные клетки (например, болото – вес 3, песок – вес 2, асфальт – вес 1) и сравнить Дейкстру с A* на взвешенном графе.
|
||||
|
||||
### Этап 7. Отчёт
|
||||
**Структура отчёта:**
|
||||
1. Описание задачи и выбранных паттернов (с диаграммой классов из Mermaid).
|
||||
2. Листинги ключевых классов (можно выборочно) или ссылка на репозиторий.
|
||||
3. Результаты экспериментов (таблицы, графики).
|
||||
4. Анализ эффективности алгоритмов и применимости паттернов.
|
||||
5. Выводы: как ООП и паттерны помогли сделать код гибким и расширяемым. Что было бы сложно изменить без них.
|
||||
|
||||
## Советы
|
||||
- Для A* самая простая эвристика: `abs(x1 - x2) + abs(y1 - y2)`.
|
||||
- При поиске пути надо хранить предшественников (`parent` для каждой посещённой клетки), чтобы восстановить путь.
|
||||
- Для BFS/DFS используй `deque` (очередь) и `list` (стек).
|
||||
- Визуализацию в консоли можно сделать с помощью `os.system('cls' if os.name == 'nt' else 'clear')` для перерисовки.
|
||||
937
skorohodovsa/task_2/practice/main.ipynb
Normal file
937
skorohodovsa/task_2/practice/main.ipynb
Normal file
File diff suppressed because one or more lines are too long
121
skorohodovsa/task_2/practice/results.csv
Normal file
121
skorohodovsa/task_2/practice/results.csv
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
лабиринт,стратегия,время_мс,посещено_клеток,длина_пути
|
||||
100x100_spaghetti_v1.txt,BFS,9.2513,205.0,205.0
|
||||
100x100_spaghetti_v1.txt,DFS,8.2451,2129.0,2129.0
|
||||
100x100_spaghetti_v1.txt,A*,7.1113,205.0,205.0
|
||||
100x100_spaghetti_v10.txt,BFS,9.2555,207.0,207.0
|
||||
100x100_spaghetti_v10.txt,DFS,8.1821,2489.0,2489.0
|
||||
100x100_spaghetti_v10.txt,A*,8.4803,207.0,207.0
|
||||
100x100_spaghetti_v2.txt,BFS,9.3921,217.0,217.0
|
||||
100x100_spaghetti_v2.txt,DFS,6.7196,2063.0,2063.0
|
||||
100x100_spaghetti_v2.txt,A*,10.5764,217.0,217.0
|
||||
100x100_spaghetti_v3.txt,BFS,8.4084,217.0,217.0
|
||||
100x100_spaghetti_v3.txt,DFS,5.7855,2107.0,2107.0
|
||||
100x100_spaghetti_v3.txt,A*,6.3385,217.0,217.0
|
||||
100x100_spaghetti_v4.txt,BFS,8.8661,205.0,205.0
|
||||
100x100_spaghetti_v4.txt,DFS,6.8166,2409.0,2409.0
|
||||
100x100_spaghetti_v4.txt,A*,6.1874,205.0,205.0
|
||||
100x100_spaghetti_v5.txt,BFS,8.3117,217.0,217.0
|
||||
100x100_spaghetti_v5.txt,DFS,6.3364,2071.0,2071.0
|
||||
100x100_spaghetti_v5.txt,A*,8.495,217.0,217.0
|
||||
100x100_spaghetti_v6.txt,BFS,8.212,243.0,243.0
|
||||
100x100_spaghetti_v6.txt,DFS,7.0348,1869.0,1869.0
|
||||
100x100_spaghetti_v6.txt,A*,12.8413,243.0,243.0
|
||||
100x100_spaghetti_v7.txt,BFS,8.3471,211.0,211.0
|
||||
100x100_spaghetti_v7.txt,DFS,6.1699,2283.0,2283.0
|
||||
100x100_spaghetti_v7.txt,A*,6.9637,211.0,211.0
|
||||
100x100_spaghetti_v8.txt,BFS,8.3499,221.0,221.0
|
||||
100x100_spaghetti_v8.txt,DFS,7.1166,2473.0,2473.0
|
||||
100x100_spaghetti_v8.txt,A*,9.5093,221.0,221.0
|
||||
100x100_spaghetti_v9.txt,BFS,8.5536,209.0,209.0
|
||||
100x100_spaghetti_v9.txt,DFS,5.4126,1939.0,1939.0
|
||||
100x100_spaghetti_v9.txt,A*,6.7365,209.0,209.0
|
||||
10x10_path_v1.txt,BFS,0.032,13.0,13.0
|
||||
10x10_path_v1.txt,DFS,0.0341,13.0,13.0
|
||||
10x10_path_v1.txt,A*,0.0399,13.0,13.0
|
||||
10x10_path_v10.txt,BFS,0.0323,13.0,13.0
|
||||
10x10_path_v10.txt,DFS,0.036,13.0,13.0
|
||||
10x10_path_v10.txt,A*,0.037,13.0,13.0
|
||||
10x10_path_v2.txt,BFS,0.0354,17.0,17.0
|
||||
10x10_path_v2.txt,DFS,0.0433,17.0,17.0
|
||||
10x10_path_v2.txt,A*,0.044,17.0,17.0
|
||||
10x10_path_v3.txt,BFS,0.0348,17.0,17.0
|
||||
10x10_path_v3.txt,DFS,0.0492,17.0,17.0
|
||||
10x10_path_v3.txt,A*,0.0439,17.0,17.0
|
||||
10x10_path_v4.txt,BFS,0.0476,29.0,29.0
|
||||
10x10_path_v4.txt,DFS,0.0475,29.0,29.0
|
||||
10x10_path_v4.txt,A*,0.0652,29.0,29.0
|
||||
10x10_path_v5.txt,BFS,0.0302,13.0,13.0
|
||||
10x10_path_v5.txt,DFS,0.0334,13.0,13.0
|
||||
10x10_path_v5.txt,A*,0.0371,13.0,13.0
|
||||
10x10_path_v6.txt,BFS,0.0307,13.0,13.0
|
||||
10x10_path_v6.txt,DFS,0.0339,13.0,13.0
|
||||
10x10_path_v6.txt,A*,0.0375,13.0,13.0
|
||||
10x10_path_v7.txt,BFS,0.0401,17.0,17.0
|
||||
10x10_path_v7.txt,DFS,0.0499,17.0,17.0
|
||||
10x10_path_v7.txt,A*,0.0489,17.0,17.0
|
||||
10x10_path_v8.txt,BFS,0.0615,29.0,29.0
|
||||
10x10_path_v8.txt,DFS,0.0536,29.0,29.0
|
||||
10x10_path_v8.txt,A*,0.0801,29.0,29.0
|
||||
10x10_path_v9.txt,BFS,0.0579,17.0,17.0
|
||||
10x10_path_v9.txt,DFS,0.046,17.0,17.0
|
||||
10x10_path_v9.txt,A*,0.0468,17.0,17.0
|
||||
30x30_empty_v1.txt,BFS,1.1046,55.0,55.0
|
||||
30x30_empty_v1.txt,DFS,0.7781,379.0,379.0
|
||||
30x30_empty_v1.txt,A*,1.9965,55.0,55.0
|
||||
30x30_empty_v10.txt,BFS,1.1246,55.0,55.0
|
||||
30x30_empty_v10.txt,DFS,0.7002,379.0,379.0
|
||||
30x30_empty_v10.txt,A*,2.0086,55.0,55.0
|
||||
30x30_empty_v2.txt,BFS,1.1401,55.0,55.0
|
||||
30x30_empty_v2.txt,DFS,0.7263,379.0,379.0
|
||||
30x30_empty_v2.txt,A*,2.0245,55.0,55.0
|
||||
30x30_empty_v3.txt,BFS,1.1038,55.0,55.0
|
||||
30x30_empty_v3.txt,DFS,0.7249,379.0,379.0
|
||||
30x30_empty_v3.txt,A*,2.007,55.0,55.0
|
||||
30x30_empty_v4.txt,BFS,1.1224,55.0,55.0
|
||||
30x30_empty_v4.txt,DFS,0.7053,379.0,379.0
|
||||
30x30_empty_v4.txt,A*,1.989,55.0,55.0
|
||||
30x30_empty_v5.txt,BFS,1.1294,55.0,55.0
|
||||
30x30_empty_v5.txt,DFS,0.7202,379.0,379.0
|
||||
30x30_empty_v5.txt,A*,2.1138,55.0,55.0
|
||||
30x30_empty_v6.txt,BFS,1.0843,55.0,55.0
|
||||
30x30_empty_v6.txt,DFS,0.7746,379.0,379.0
|
||||
30x30_empty_v6.txt,A*,2.009,55.0,55.0
|
||||
30x30_empty_v7.txt,BFS,1.1449,55.0,55.0
|
||||
30x30_empty_v7.txt,DFS,0.7076,379.0,379.0
|
||||
30x30_empty_v7.txt,A*,2.033,55.0,55.0
|
||||
30x30_empty_v8.txt,BFS,1.3196,55.0,55.0
|
||||
30x30_empty_v8.txt,DFS,0.7794,379.0,379.0
|
||||
30x30_empty_v8.txt,A*,1.9972,55.0,55.0
|
||||
30x30_empty_v9.txt,BFS,1.1088,55.0,55.0
|
||||
30x30_empty_v9.txt,DFS,0.7131,379.0,379.0
|
||||
30x30_empty_v9.txt,A*,2.0128,55.0,55.0
|
||||
50x50_deadends_v1.txt,BFS,1.7809,729.0,729.0
|
||||
50x50_deadends_v1.txt,DFS,1.7167,729.0,729.0
|
||||
50x50_deadends_v1.txt,A*,2.5217,729.0,729.0
|
||||
50x50_deadends_v10.txt,BFS,0.7362,261.0,261.0
|
||||
50x50_deadends_v10.txt,DFS,1.7627,261.0,261.0
|
||||
50x50_deadends_v10.txt,A*,0.9753,261.0,261.0
|
||||
50x50_deadends_v2.txt,BFS,0.9246,249.0,249.0
|
||||
50x50_deadends_v2.txt,DFS,1.7347,249.0,249.0
|
||||
50x50_deadends_v2.txt,A*,1.0804,249.0,249.0
|
||||
50x50_deadends_v3.txt,BFS,0.945,297.0,297.0
|
||||
50x50_deadends_v3.txt,DFS,1.7483,297.0,297.0
|
||||
50x50_deadends_v3.txt,A*,1.0832,297.0,297.0
|
||||
50x50_deadends_v4.txt,BFS,1.5487,413.0,413.0
|
||||
50x50_deadends_v4.txt,DFS,1.6526,413.0,413.0
|
||||
50x50_deadends_v4.txt,A*,1.9521,413.0,413.0
|
||||
50x50_deadends_v5.txt,BFS,0.9255,309.0,309.0
|
||||
50x50_deadends_v5.txt,DFS,1.7299,309.0,309.0
|
||||
50x50_deadends_v5.txt,A*,1.1469,309.0,309.0
|
||||
50x50_deadends_v6.txt,BFS,1.0637,337.0,337.0
|
||||
50x50_deadends_v6.txt,DFS,1.7728,337.0,337.0
|
||||
50x50_deadends_v6.txt,A*,1.3449,337.0,337.0
|
||||
50x50_deadends_v7.txt,BFS,0.7827,261.0,261.0
|
||||
50x50_deadends_v7.txt,DFS,1.6948,261.0,261.0
|
||||
50x50_deadends_v7.txt,A*,0.9527,261.0,261.0
|
||||
50x50_deadends_v8.txt,BFS,1.5551,565.0,565.0
|
||||
50x50_deadends_v8.txt,DFS,1.7707,565.0,565.0
|
||||
50x50_deadends_v8.txt,A*,2.3158,565.0,565.0
|
||||
50x50_deadends_v9.txt,BFS,0.6693,209.0,209.0
|
||||
50x50_deadends_v9.txt,DFS,1.052,209.0,209.0
|
||||
50x50_deadends_v9.txt,A*,0.7957,209.0,209.0
|
||||
|
BIN
skorohodovsa/task_2/practice/results.png
Normal file
BIN
skorohodovsa/task_2/practice/results.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 218 KiB |
11
skorohodovsa/task_2/requirements.txt
Normal file
11
skorohodovsa/task_2/requirements.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
sphinx
|
||||
myst-parser
|
||||
sphinxawesome-theme
|
||||
nbsphinx
|
||||
myst-nb
|
||||
tabulate
|
||||
bibtexparser
|
||||
pytest
|
||||
sphinxcontrib-mermaid
|
||||
matplotlib
|
||||
pandas
|
||||
257
skorohodovsa/task_2/source/models/base.py
Normal file
257
skorohodovsa/task_2/source/models/base.py
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
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 start(self) -> Optional[Cell]:
|
||||
for y in range(self._height):
|
||||
for x in range(self._width):
|
||||
if self[y, x].is_start:
|
||||
return self[y, x]
|
||||
return None
|
||||
|
||||
@property
|
||||
def exit(self) -> Optional[Cell]:
|
||||
for y in range(self._height):
|
||||
for x in range(self._width):
|
||||
if self[y, x].is_exit:
|
||||
return self[y, x]
|
||||
return None
|
||||
|
||||
@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)
|
||||
)
|
||||
1
skorohodovsa/task_2/source/settings.py
Normal file
1
skorohodovsa/task_2/source/settings.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
cell_mapping = {"wall": "#", "empty": " ", "start": "S", "exit": "E"}
|
||||
11
skorohodovsa/task_2/source/strategy/__init__.py
Normal file
11
skorohodovsa/task_2/source/strategy/__init__.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from source.strategy.algorithms import PathFindingStrategy
|
||||
from source.strategy.astar import AStarStrategy
|
||||
from source.strategy.bfs import BFSStrategy
|
||||
from source.strategy.dfs import DFSStrategy
|
||||
|
||||
__all__ = [
|
||||
"PathFindingStrategy",
|
||||
"BFSStrategy",
|
||||
"DFSStrategy",
|
||||
"AStarStrategy",
|
||||
]
|
||||
68
skorohodovsa/task_2/source/strategy/algorithms.py
Normal file
68
skorohodovsa/task_2/source/strategy/algorithms.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
from source.models.base import Maze, Cell
|
||||
|
||||
|
||||
class PathFindingStrategy(ABC):
|
||||
"""Интерфейс стратегии поиска пути в лабиринте."""
|
||||
|
||||
@abstractmethod
|
||||
def find_path(
|
||||
self, maze: Maze, start: Cell = None, exit: Cell = None
|
||||
) -> list[Cell]:
|
||||
"""Найти путь от start до exit.
|
||||
|
||||
Args:
|
||||
maze: Объект лабиринта.
|
||||
start: Стартовая клетка.
|
||||
exit: Целевая клетка.
|
||||
|
||||
Returns:
|
||||
Список клеток пути (от start до exit включительно).
|
||||
Пустой список, если путь не найден.
|
||||
"""
|
||||
|
||||
def _validate(
|
||||
self, maze: Maze, start: Optional[Cell], exit: Optional[Cell]
|
||||
) -> tuple[Optional[Cell], Optional[Cell]]:
|
||||
"""Подставляет start/exit из лабиринта если не переданы явно.
|
||||
|
||||
Raises:
|
||||
ValueError: Если старт или выход не найдены.
|
||||
"""
|
||||
if start is None:
|
||||
start = maze.start
|
||||
if exit is None:
|
||||
exit = maze.exit
|
||||
|
||||
if start is None:
|
||||
raise ValueError("Стартовая клетка не найдена в лабиринте")
|
||||
if exit is None:
|
||||
raise ValueError("Выходная клетка не найдена в лабиринте")
|
||||
|
||||
return start, exit
|
||||
|
||||
def _reconstruct_path(
|
||||
self, 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
|
||||
50
skorohodovsa/task_2/source/strategy/astar.py
Normal file
50
skorohodovsa/task_2/source/strategy/astar.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import heapq
|
||||
from typing import Optional
|
||||
|
||||
from source.models.base import Cell, Maze
|
||||
from source.strategy.algorithms import PathFindingStrategy
|
||||
|
||||
# ---------------------------------------------------------------------------- #
|
||||
# Моя азиатская жена называет меня расистом. Но как я могу #
|
||||
# быть расистом, если я женился на женщине низшей расы?! #
|
||||
# ---------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
def _manhattan(a: Cell, b: Cell) -> int:
|
||||
"""Манхэттенское расстояние между двумя клетками."""
|
||||
return abs(a.x - b.x) + abs(a.y - b.y)
|
||||
|
||||
|
||||
class AStarStrategy(PathFindingStrategy):
|
||||
"""Алгоритм A* с манхэттенской эвристикой."""
|
||||
|
||||
def find_path(
|
||||
self, maze: Maze, start: Optional[Cell] = None, exit: Optional[Cell] = None
|
||||
) -> list[Cell]:
|
||||
start, exit = self._validate(maze, start, exit)
|
||||
|
||||
g_score: dict[Cell, int] = {start: 0}
|
||||
came_from: dict[Cell, Optional[Cell]] = {start: None}
|
||||
|
||||
counter = 0
|
||||
open_heap: list[tuple[int, int, Cell]] = [
|
||||
(_manhattan(start, exit), counter, start)
|
||||
]
|
||||
|
||||
while open_heap:
|
||||
_, _, current = heapq.heappop(open_heap)
|
||||
|
||||
if current is exit:
|
||||
return self._reconstruct_path(came_from, exit)
|
||||
|
||||
for neighbor in maze.get_neighbors(current.x, current.y):
|
||||
tentative_g = g_score[current] + 1
|
||||
|
||||
if tentative_g < g_score.get(neighbor, float("inf")):
|
||||
g_score[neighbor] = tentative_g
|
||||
came_from[neighbor] = current
|
||||
f = tentative_g + _manhattan(neighbor, exit)
|
||||
counter += 1
|
||||
heapq.heappush(open_heap, (f, counter, neighbor))
|
||||
|
||||
return []
|
||||
34
skorohodovsa/task_2/source/strategy/bfs.py
Normal file
34
skorohodovsa/task_2/source/strategy/bfs.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from collections import deque
|
||||
from typing import Optional
|
||||
|
||||
from source.models.base import Cell, Maze
|
||||
from source.strategy.algorithms import PathFindingStrategy
|
||||
|
||||
|
||||
class BFSStrategy(PathFindingStrategy):
|
||||
"""Поиск в ширину (Breadth-First Search).
|
||||
|
||||
Гарантирует кратчайший путь по количеству шагов.
|
||||
Сложность: O(V + E) по времени и памяти.
|
||||
"""
|
||||
|
||||
def find_path(
|
||||
self, maze: Maze, start: Optional[Cell] = None, exit: Optional[Cell] = None
|
||||
) -> list[Cell]:
|
||||
start, exit = self._validate(maze, start, exit)
|
||||
|
||||
came_from: dict[Cell, Optional[Cell]] = {start: None}
|
||||
queue: deque[Cell] = deque([start])
|
||||
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
|
||||
if current is exit:
|
||||
return self._reconstruct_path(came_from, exit)
|
||||
|
||||
for neighbor in maze.get_neighbors(current.x, current.y):
|
||||
if neighbor not in came_from:
|
||||
came_from[neighbor] = current
|
||||
queue.append(neighbor)
|
||||
|
||||
return []
|
||||
38
skorohodovsa/task_2/source/strategy/dfs.py
Normal file
38
skorohodovsa/task_2/source/strategy/dfs.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
from typing import Optional
|
||||
|
||||
from source.models.base import Maze, Cell
|
||||
from source.strategy.algorithms import PathFindingStrategy
|
||||
|
||||
# ---------------------------------------------------------------------------- #
|
||||
# Как называется пресмыкающийся, который в прошлом был программистом? #
|
||||
# ---------------------------------------------------------------------------- #
|
||||
# крокодил #
|
||||
# ---------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
class DFSStrategy(PathFindingStrategy):
|
||||
"""Поиск в глубину (Depth-First Search).
|
||||
|
||||
Находит путь, но не гарантирует кратчайший.
|
||||
"""
|
||||
|
||||
def find_path(
|
||||
self, maze: Maze, start: Optional[Cell] = None, exit: Optional[Cell] = None
|
||||
) -> list[Cell]:
|
||||
start, exit = self._validate(maze, start, exit)
|
||||
|
||||
came_from: dict[Cell, Optional[Cell]] = {start: None}
|
||||
stack: list[Cell] = [start]
|
||||
|
||||
while stack:
|
||||
current = stack.pop()
|
||||
|
||||
if current is exit:
|
||||
return self._reconstruct_path(came_from, exit)
|
||||
|
||||
for neighbor in maze.get_neighbors(current.x, current.y):
|
||||
if neighbor not in came_from:
|
||||
came_from[neighbor] = current
|
||||
stack.append(neighbor)
|
||||
|
||||
return []
|
||||
94
skorohodovsa/task_2/source/strategy/solver.py
Normal file
94
skorohodovsa/task_2/source/strategy/solver.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import time
|
||||
from dataclasses import dataclass
|
||||
|
||||
from source.models.base import Maze, Cell
|
||||
from source.strategy import PathFindingStrategy
|
||||
|
||||
|
||||
@dataclass
|
||||
class SearchStats:
|
||||
"""Статистика выполнения поиска пути.
|
||||
|
||||
Attributes:
|
||||
elapsed_ms: Время выполнения в миллисекундах.
|
||||
visited_count: Количество посещённых клеток.
|
||||
path_length: Длина найденного пути (0 если путь не найден).
|
||||
path: Найденный путь — список клеток от старта до выхода.
|
||||
"""
|
||||
|
||||
elapsed_ms: float
|
||||
visited_count: int
|
||||
path_length: int
|
||||
path: list[Cell]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return (
|
||||
f"Время: {self.elapsed_ms:.3f} мс | "
|
||||
f"Посещено клеток: {self.visited_count} | "
|
||||
f"Длина пути: {self.path_length}"
|
||||
)
|
||||
|
||||
|
||||
class MazeSolver:
|
||||
"""Оркестратор поиска пути в лабиринте.
|
||||
|
||||
Принимает лабиринт и стратегию поиска, выполняет поиск
|
||||
и возвращает результат вместе со статистикой выполнения.
|
||||
|
||||
Example:
|
||||
solver = MazeSolver(maze, BFSStrategy())
|
||||
stats = solver.solve()
|
||||
print(stats)
|
||||
|
||||
solver.set_strategy(AStarStrategy())
|
||||
stats = solver.solve()
|
||||
"""
|
||||
|
||||
def __init__(self, maze: Maze, strategy: PathFindingStrategy) -> None:
|
||||
"""Инициализирует солвер с лабиринтом и стратегией поиска.
|
||||
|
||||
Args:
|
||||
maze: Объект лабиринта.
|
||||
strategy: Стратегия поиска пути.
|
||||
"""
|
||||
self._maze = maze
|
||||
self._strategy = strategy
|
||||
|
||||
def set_strategy(self, strategy: PathFindingStrategy) -> None:
|
||||
"""Заменяет текущую стратегию поиска.
|
||||
|
||||
Args:
|
||||
strategy: Новая стратегия поиска пути.
|
||||
"""
|
||||
self._strategy = strategy
|
||||
|
||||
def solve(
|
||||
self,
|
||||
start: Cell = None,
|
||||
exit: Cell = None,
|
||||
) -> SearchStats:
|
||||
"""Выполняет поиск пути и собирает статистику.
|
||||
|
||||
Если start или exit не переданы явно, стратегия найдёт
|
||||
их самостоятельно по флагам is_start / is_exit в лабиринте.
|
||||
|
||||
Args:
|
||||
start: Стартовая клетка (опционально).
|
||||
exit: Конечная клетка (опционально).
|
||||
|
||||
Returns:
|
||||
Объект SearchStats с временем выполнения, количеством
|
||||
посещённых клеток и длиной найденного пути.
|
||||
"""
|
||||
t_start = time.perf_counter()
|
||||
path = self._strategy.find_path(self._maze, start, exit)
|
||||
t_end = time.perf_counter()
|
||||
|
||||
elapsed_ms = (t_end - t_start) * 1000
|
||||
|
||||
return SearchStats(
|
||||
elapsed_ms=elapsed_ms,
|
||||
visited_count=len(path),
|
||||
path_length=len(path),
|
||||
path=path,
|
||||
)
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S # # # # # # # # # # #
|
||||
### ##### ### #### ### # ### # ### # ####### # ## # # # # # # # ###### ## # # # ### ## # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # ## # # ### # ### # ## # # # ##### # ##### ### ## ####### # ##### # ### # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ## ###### # # # # # ### ##### # ## ##### ### # # #### # #### ### ##### ### ## ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # # ## # ## # ### # ##### # # # ## ##### ### # # ### # # # # ### # # # ## # # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ##### # # # # ### # # # # # # # # # # ## # #### ### # ### # # ### ### # # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ### # # # # # ##### ### # ## # ### # # # # # # ### ##### ####### # ### ### # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # ### # ### # # # # ## # ### ### # ##### # # # ####### # ##### # # # # # ####### # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # #### ### ####### ### ## # ## ## # ######## ####### # ### ######## ### # # ### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# #### # # ## # ### #### # # #### ##### # # ## ### # # # # #### # ## ##### ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # # # ## ### # # # ### # # # # ##### ##### # # ###### # # # ## ## ## #### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # ########### # ####### # ## # # # #### # ### ### # # # ### # ### ### # # # # ## ####
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # #### ### # # # # ### # ##### ## ## # # ######### ####### # # ## # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # ### # # # ## # # # # ### ##### ## #### # # ###### ## # # # # # ### # ### # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ### ######### # # ### # ##### # ####### # # # # # # # # # ##### # ##### # # #### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### # # ### ## # # ### # # ### # # ### # # # #### ### ##### ### # # # # # ########## # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ### # # # ### # # # # # ### ### ##### # # # ## ########### # # #### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ##### # ### ##### # # # # # # # # ##### # # ####### ### # # # ## # # # # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## ### # ### # # # #### # ## # ##### # # # # # ### # #### ## ##### ####### # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # ## # ### ##### ##### # # # ##### # # # #### # # ###### ## # # # ### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ####### # # # #### # # # ## # ### # #### # # ##### ## ## # # # # # # ### # # # # ### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
### ### # # # # ## ###### ### # #### # # ##### ##### ### ## ## # ######### # # ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### #### # ### # ### # #### ### # ##### ### ## # ### # ##### ##### # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # ##### # ## ## ####### ### # ##### ##### # ##### # # # # # ##### # ##### ## # ### # ###
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### ########### # # ######### # # ### # ##### ### ### ### ##### ### ######### ### # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # ### # # ## #### # # ##### ## # ## # #### # # # # ### # # # # ### # # # # # # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ### # # ### # ##### ### # # # # # # ## ## # # # ## ###### # ## # ########## # ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ##### # ####### ### # # # ### ## # ###### # ### # # ### # ### ### # ### ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ## ## #### # ## ## # # # # ######### ### # # # ### # # # # # # ## ## # ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ##### ## ### ## ### # ### ##### # # # #### ##### # ### ######### # # #### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ## ## ### # # ## #### # ### # # # # # # ##### # ### ### ## #### ## ### ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ###### # # ####### ### ### # # ##### ##### ###### # # # ##### ##### # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### ### ### ######### ### # # ## #### ### # #### ### ##### # ######## # #######
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### ### # # # # ## ## # ### # ## # # ##### # ### ## ####### # ## ### # ### # ## # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ### # # ######### # ### # ### ####### ## # # ### # # # # ###### # ### # # # ## # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # # # ##### # ######### #### ## ## ##### ## ## ##### #### # # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# ### ##### # # ##### ####### # # # ##### # ##### ### ## ## # ### ### # ### ## #### ####### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## # #### #### # # # ### # ## #### # ## ## # # ##### # ##### # # # # # ### ##### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # # ### # # ##### ### ### ## # # # ### # # # ####### # ### ##### ## # # #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # ## ######## ### ####### ###### ##### # ############### # # ### # ##### # ### ### ## ## # #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # ### # ##### # # # # # ## # # ## # # # # ######### # # ##### # # ####### ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # # ##### # # # ### # # # # ####### ### # ## ## ### ## ###### # ### # # # ## ## #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # ## ## ### ######### ####### # ######### ### # ### # # # # # # # # ### #### ## ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # ### ##### ########## ## ### # # ### ##### # ### #### ##### ####### # # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ### # # # ### # ###### # # ##### ## # ######## # ### # #### #### ### # # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ## # # # # # ## # # ### # # # #### # # # # ## #### # # # # ## ### # ## ## # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # ########## # ####### # ### ### # ### ##### # # # ## ### ### ### # # ##### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # # ## # # # # # # # # ### ##### ##### # ### ## # ####### # ### # ### # # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ##### # #### # ## ## ##### # ######### # ## ### # #### ## # # # # # ######### #
|
||||
# # # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S# # # # # # # # # # # # # # #
|
||||
# # # # # # # # ### # ### # # # # # ## ##### # # ## # # ### # # ### # # ## # ### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### ### ##### ## # # ### # # # # # # # # # # # # ## # # # #### ### # # ## ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ###### ## # # ### ##### # # # ### ### ##### # # # # # ## # # ### ### # # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # ### ##### # ### ## # # # # # # ### ####### # ####### # # ## ## ###### #### ## ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ##### # # # ### # # # # ### # #### # ### ####### # #### #### # ### # # # #### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ## ### # ### # # ####### ### ### # ######### ### # # # ### # # ### # ### ######### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # #### # # ### # # #### # # # # # ####### # # ## ## ### # # ### ##### # # ## ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ##### ### # # ###### ## # ## ## # # ### # # ### # # ####### # ### # ## # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # ## ##### # # # # ## ## # # ####### # ### ##### # # ####### ## # # # ###### #### # ### # #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
##### ### ##### ##### ### ##### # ## ## # # ##### # ##### ####### # #### ### # ## ## # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
## # ####### ### # # # # # #### # # ### ##### #### # #### ## # # # # ## # ### ### # # ###
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # # ## ####### ### # ## # # # ## # # ####### # # # ### ### # # # # # # ##### # # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # ### ##### #### ## # # # ######### # ##### ### ### # ## ## ### # # ###### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### #### # ### # # ### # # ##### ## ### # ## ######### ### #### ### # # # ##### # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ##### ### ### # ####### ### # ## # ## # # # ##### # ### # #### #### ### ### ## ####
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### # # ### # # ##### # ### # # # #### ##### # # ## ##### # # # # # ### ### # # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## # # # ### ### # # # # ### # ##### ### # # # ## # # # # # ########### ### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### #### # ### # ######### ### ## ###### ######### ### ##### ## # ### # # ##### #### #####
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # # ## ### ##### # # ### ### ### ### # # ####### ####### # # ##### # # ### ### ##### ## # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ##### ### # ## # # # # ##### # # #### # ####### ##### # # ##### # ## ## # # ### ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ## # ##### ##### # ## ### ## # # # # # # ### ### # ### ### # ### # # # # ## # ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### ### ## # # # #### # #### # ####### ### # ### #### ## # # #### # ## # ### # #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
### # ### # # # #### # ######### # # ### # # # # ### ##### ###### ## # # #### #### ### ## #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ### # ### ##### # # ######### # ### ### ### ### # ####### ##### ####### # # ##### # #### ##
|
||||
# # # # # # # # # # # # # # # #
|
||||
### # # # ########### ### # # # ##### ##### ## #### # ###### #### #### # #### # ######### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ####### ##### # # # # ## ### #### ## # # ### ##### # ### # # # # ###### ## # # #### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ####### # # # ### ### ########### # # # # ## # ## # ###### ### # # ### # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ##### # ### ### # ### ### ### # # # ##### # # # # ### # ### ## ########### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ### # ### # ####### # # # # ## ### ### # ### # ### # ## # # # # # # ## ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ### # # # ##### ##### # # ### ##### ### # ### # ##### # ## ####### # # # # # # #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### ####### # ## ## ##### # # # ### ### ### ### # # # # # # ##### # ### ##### # # # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # ####### #### # ##### # ## # ## # #### #### # # ### ### ### # # # # ## ###### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ### # # # # ##### # ##### # # ######## ##### # # # ### # ##### ####### # ### # ## # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### # # # ## ## # ##### # ## # # # # # # # ##### ### #### # # # # ##### # # # ### # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
## ## # ### # # ### ### # ##### ### # # # ##### ## # # # ### ### # ##### ####### ##### #### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# #### # ## ## #### ##### # ### # ### ##### # ## # # # # ## ### ####### ####### ### # ##
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### #### # # ## ##### ### # # # ### # # ### # ####### # ### # ## #### # ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### # # ####### # # ## # # ### ### # ### # # ##### ## # # ## # # # ## # # #### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ##### # # # # # # # ### ### ### ########## ##### ### # ##### # # ##### ####### #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
##### ## ## # # # ## ### # # # ####### # ## # # # ######## ### ## #### # ##### ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # ## ## ####### # ## ### ########### ### ### # # #### ### # # #### # ## # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### # ##### # # ### ### # ### # # ### # ###### # ## ### # ## ## # # ### # ### ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ### # # # # # # # # # # # # # # ### ##### # # ### ### ## #### # # ### #### # # ## ## ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### ## #### ##### # ## ### # # # ##### # # # ##### # # # # ######### # # ### ##### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # ##### # # ## # # ## # # # # ## # ## # # # # # # ### ## ### # #### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # # # ##### # ### ##### # # # # ### ### # ### # ##### # # # ### # # # ##### ##### ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ##### ### ### # # # # ### # # ## ### ####### ######## # # ### ## ### ### ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ###### ## # # # # ## ###### ## ## ### ### ### # ### # # # # ####### # ### # ### #### # #
|
||||
# # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S# # # # # # # # # # #
|
||||
# # ### # # ### ### # ## ## ####### # ########### # ### # ## #### # ### # # ### # # ### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # ### ## # ## # # ###### #### ## ###### ### ##### ## #### ### # ## # # ####### # ##### ###
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ### #### ## ### #### ### # # ### ### ##### #### # # ##### ### # # ### ####### # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
##### ##### # # # # ### ### #### ##### # ####### ## ## ####### # ## # #### ## # ####### # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# #### ## ## ########## # # # ### ##### # ######## ## ## ###### #### ## #### ### # # ### ##
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ### ### # ####### # ## # ## # # # # # # # # # ###### # # # ### # # ##### # # # #### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # #### # #### # # #### # ##### # # # ### # # # # # ############# # # ## ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
### # ## ### # ### ## ##### ### # ### # # ## #### # ####### # ### # ### # # # # ## # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # #### ## ##### # ## # ##### # # ### # ### # # # # ####### ##### ### # # ## #### #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ######### ## #### ### # ##### ################# # # # ### #### # # ### # ## # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ## # ## # # ### # # # ##### ### # ### # # ### # #### ## # # ### # ###### ### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ### # # # # ### # ## ## # ##### # # # # ### # ##### ########### # ######## ####### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # ### ##### # # ### # ## # ### ### # # ## #### # # # # ##### # ### # # #### # # # # #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
####### ## # ###### ### # # ## ### #### ### # #### ## # ### # # # ### # #### ### # ### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
###### ## ## ### # ## # #### # # ##### # ## ## # ##### ### ### ##### # ### # # # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ####### # # # ## # # ### # ####### ## ### ## ### # # # # ## # # ### # ####### # # # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # ## ### # ## ## # # ##### ### ## ## #### # # ##### # # # ### ## ## ### ## # ### ## # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ######### ### # # ## # # ### ### ## ### ### # ##### # ## # ### # # ############ ### # ###
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # #### ## # ### #### ### ## #### ## # # ### # # ## # # ### ### ## # ###### ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ## #### # # ## ########### # # # # # ####### ## # ##### # # # # # #### # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # # ##### # # ### # ## ## # # ### ### ### ### # ### # # # #### ## # #### # ##### ### #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # ### # ## ## # # ### ####### ### # # ## ## # # ########### # # ########### # # # ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ##### ## #### # #### ####### # # ## ### ##### # ##### ##### ### # ######### # ###### # # #####
|
||||
# # # # # # # # # # # # # # #
|
||||
# # # ### ### ##### # ##### ### #### ## ## # # ##### ######### # ############# # ##### ###
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
##### # ######### # ### # # ### # # # ###### # ### ### # # ## ########## #### # ## ##### ## #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
### ###### # ### # ### # ## # #### ### # ### # # ### ### # # ### # # ### ####### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # ####### # ### ##### # # # ##### ### ## ##### ##### ### ### ### # # # ### # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ### ###### ### ###### ####### # # # ### ####### # #### ### # ##### ## # ####### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # ### # # ### # ## # # # # ##### # ### ### ####### # ###### #### ## # #### ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # # # # ## #### ### # # # ##### ### ## #### ### # # ### # ### ##### ## # # ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## # ### # ##### # # # # # ##### ################### # # ##### ## # # ### # # # ### #######
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ### # # ### # # # # # ### ### # ##### # ## # ##### # ## # # # ## ## # ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # ## ### ## # ## # # # # # # ##### ######### # ## ## # ### ##### ### #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ## ## # ## ### # ###### ## ## # ####### # # ### #### # # # ### ### #### # # #####
|
||||
# # # # # # # # # # # # # # # #
|
||||
# #### # ##### ### ## # #### ### ### # ### ######## # ## # # # # # # # ### ### ######## ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### # # # ### # #### # ####### # # ## # ### ####### ### # ### # # # # ### # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ##### ##### #### # # ######### # # # ### ####### # # ###### # # ####### ## # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### # ### # # # ### # # ### #### ######## # # ### # ### # # # # ### # ### ### ##### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # #### # # ### ##### # ### #### ### # ### # # ### # ## # ### # ######### # ## ####
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ### # ######### # # ### # # # # # ### #### ## # # # #### #### ### # ###### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ### # # ### ### ##### # # # ######## # # # # # ####### # ##### # ### ### # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### ##### ### ## ## # # # # # # ## # ### ### # ########### # ##### ## ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ##### # # ## # #### # # ###### # ## #### ### ### # # # # # ### # # # # # # ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### ## ## # # #### # # # ##### # ### # # # ### # # # ## #### # # ### # ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### #### ### ## ### # ### ### #### # # # ## #### # ### # # ## ### ########## ## ### # # # ##
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # ### ### # ### # ## # ## #### # ### # ### # ### # ### ### # # # ### ## # ## ##
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### # # # # #### # ### # ####### # # ### #### # # # ## ### # # ### # ## # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # ##### # ### # # # ### # # # ## # # # ### # # # # ###### #### ### ## ### # ### ##### #
|
||||
# # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S # # # # # # # # # # # #
|
||||
##### # # ### ####### ### # # ### ## # # # # ### # # # ## # # # # ##### # # # ### ## ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ###### # # # ###### # # # # #### # # # ### # # # # # # ### ##### # # ############# # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ### ## # #### ### # # # # # # # # ##### # #### ##### # # # # ### ### ### # ## ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ##### ## ### # # #### #### # # ## ###### ### # # # # ##### ### # ## ####### # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### # # ######### # ##### ##### # ## ### # ##### ####### # # # # # # # #### ## # # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # ## # ## ####### # # # ##### # ### ####### ## ### ##### # ## # # ###### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # # # ### # #### # ### ##### # ### ### # # ##### # # ### ######### ## ### # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## # # ### ####### ### # # ## ##### ## ## # ## # # # # ## # # # # ## ### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ### ### # # ### ### # # ### # #### ### # # ##### ### ######### # # # # # # # ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # ####### # # #### # # ## # ####### # ### ### # # # ####### ### ### ## # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # #### ## ## ### # ### # # #### ## #### # ##### ##### ### # # ## ## # ### # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### # # ## # ### ### # # # ## # ## # ## ## # ##### ### ## # ### ### # ### ###### # ###
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # # ######### ##### # # # # # #### ## ##### ####### # ##### # ## ##### ### ### ####### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # # # # ### ######### ### # ## # ### # # ### # ####### ##### ### ### ### # # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
## # # ### ### # ### # ##### ## ## # # ########### # # # ####### # # ## # # # # ### ##### # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
##### # ### ####### # ### ## ##### # # # ##### # # # ## # # ### # #### # ######## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # ### ##### # # # # # ## # # # # # # # #### ### # # ### # ### ### ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ##### # # ## ##### ######### # # ##### # ### # # ##### #### ## # ####### # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # ## ## # ## # ### # # # # ##### ##### # ### ## ### # ### # # # ##### # # ##### # # ## # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# #### # # #### ### # # # ### # ### ### ## # # ######### # ## # ##### # ##### ## # # # #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## ## ## # ## # ##### # ### ### ## ## # # ### # ### # # # ### # ### # # ####### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ## # # # ####### ### # ### # ####### # #### # # # # # # # # ### # ### # ##### # # # ### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
## #### ########### # # # # # ##### # #### ## ########### # # # ### # ### ### ## ## ##### # # #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # # ### # ### # # # ### # ## ## ### # ### ### # # ### ### #### ## ### # ### # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ##### # # ### # ####### # # # # # # # # ### ### # ## # ##### ### ### #### # # # ###
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # ## ## ### # # ##### ########### # ### ##### ### # ### # # ### ## ## # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## # # # # ### # # # #### ## ############# # ### # # # # ### ### # ### # # # # ### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ### ### ### # ### # # #### ## # ### # ### # ## ### # #### #### ### ### ##### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ####### ### ##### # # # ### # ## ### # # # ### ##### ##### # ### # # ### # # ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
## # # # # # # # # ### ### ## ### # ##### # # ## ###### ## ## # ##### ### # ####### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ###### ### #### ## ####### ## ## ### ### # ### ### # # ##### # # # # ### # ### ### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # ## ### ### # ### ######## ## # # # # ## ### ###### ## ####### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # ### # ## # ###### # # # # # # #### ## # ### # # ### ### # ###### ## ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### # ### # ##### ##### # ############# # ############ # # # ### # # # ## # ## # # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ############ # # # # ##### # ### # ## ##### ### # ##### #### # # ##### ### ##### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### # # # # # ## ### # # # # # # # # # ### # ## #### # #### # #### # ### #### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ###### ### # ### # # # # # ### ### # #### ### ## # ####### # #### # ##### ##### # # #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # ## ######### # ## # # ##### # ### # # # # # # # # #### ## # #### # ## # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # ### ### # ## ## ### # # ########### ####### #### # ###### ##### # # # ### ## ##
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# ### # # ##### ### ## ####### ### ####### ### ##### ### ###### ###### # # # # ### # # ## #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
### ### ##### # # # ######### # # ### # # ## ### ### # ### # # ##### # ##### ### # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # # # ## ##### # ####### # ### # ###### # # # # # # ## #### # # # # ##### # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ##### # # # # # # # #### ## ### ##### ### # ## # ##### ## # ## ##### # # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # # # # # ### # ###### # ### #### # ### # ##### # ##### ### # # # ### ### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # ### ## # # # ### # ##### # ## #### ### ###### # # ## # ###### ## ### # # ### ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ###### ### # # ### # ## ##### # # ### # # # ## # # ##### ########### ##### ##### # # ## ## ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # ######### # ## ## ### # # # ####### #### #### ## ######## ####### ### ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
### ## # # ### # ## ### ###### # # ### ##### ### # #### # ## # # # ####### # ### # ## ## #
|
||||
# # # # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S # # # # # # # # #
|
||||
# ######## # ##### ### ## #### # # ### ##### # ### # ### # ### ##### # # # ##### # ##### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### ##### # ### ## ## ##### ####### ### ##### # # ##### # # # # # ### ## ##### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ## ######## # ######## ### # # ### # ### # # ##### # # ##### ### ### # ## # ### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ## ## ### # # # # #### #### # # # # # ####### # # ##### ########### # ### # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ## # # # ##### # # ####### ### #### # ### # # # # # ### ## ######## # # # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # # ##### # ######### # # # ## ##### # # # # #### ###### # ## # ## # ### # # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## ## ## #### ####### ##### # # # # # # # # ######### ##### # # # ### # # # # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ### # ### ## # # # # # ###### ## # # ### ### # ### # # # ### # # ## ## ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ######### ### ## ###### ## # # ##### ##### # ### ##### # ### ## # ##### # # ##
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # #### # ## # # # # # # ### ### # # # # # ####### ### # ### ## #### ### # # ## ########## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ######### ### # # ### ##### ## # #### ## # ##### # # ### ##### ### # ##### # ##### ### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
### ####### # # ##### # ##### # #### ########### # # # # #### ## # ### # # #### # # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ####### # # # ### # ## ### # ##### ### # # # ### # # # ### ##### #### # ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ######### ################# ## ### # # ### # ######## ### # ##### ## # ## ## # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # ####### ### #### # # # ### # ### # ### ## ### # ### ### ### # # # # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
### # ####### # # # # # ### ##### # ### # #### # ## ######### ### ####### #### ### # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ##### ### # ## # # # ## ### ### ## ###### ### # # # ### # # # ## # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ####### # # # # # # # ### ### ## ## # ######## # # ### # ## ## ##### # # # ### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ######### # ### # ### # # #### ## # #### #### ## # ##### ## ######### # ### # ### ### ### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
## ### # ## ##### ##### ##### # # ## ## # # # ##### ##### # # ####### # # # # ## ## #### ###
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### # ##### ##### # ## # # # ##### # # # # ## ### ### ## # ### ### ## # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ######### # # ### # # # ## ## # # ## ## ## ##### ## ############# # ##### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # #### ### # # ####### # ### ###### # #### # # # # #### # # # ###### ## ##### # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # ### #### # ### # # ######## ### ### ### ### ## # # ### # ### ##### # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### # ### # ## ## # # # # ##### # ##### # ### # ### # # # ### ### # ### ##### ## ### # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ##### ## # # # # # ## ### # ## ## # # # # ### # # ### ### ### ######### # # ###### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ### ##### ##### ## ##### # # ### ##### ##### # ### #### # ### ##### ### ### # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
## ### # ## # # # # ## # # ## ##### # # ## ######### # # # ### ## #### # # # ## # ## ##
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # ## ## ### # # ### # ########### # # # ##### # # # ### # # ### ##### ### # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ##### ## # # ## #### ### # ### # # # # ## ## # # # ###### # # ### # ## ## ## ### ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # ### ##### ##### # ###### # # ###### # #### ## ### # # # # ### ## #### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # # # # # ### # # # ##### # # ###### #### ##### # # ## # ### # ##### # ### #####
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ### ########### ##### # # ## ## ### # ### # ### ## ### ### # ######### # ### ####### ### # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ### # ##### # # # ### ### ### ### ##### ### ### ###### ###### #### ## # # ### # ### # ###### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # #### # # ##### # #### ### # # ##### # ### ##### ####### # # ## # ####### ######## ## # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # ####### ### ##### ### # # # ### ######## ## # ### # ###### # ####### ### ### # ## # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
####### ##### # # ##### ####### ########## # ### # # # # ### ####### ## # # ### # # ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### # ### # ### #### # ### ###### # ### # # ### ### # ### ############# ### # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # #### ##### # # # ######### ### ### # ####### # # # # ##### ## ## ## # # ## # ## # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## ### ##### ### # # ### # ## #### ### # ##### ### # # # ### # #### # ## ### # # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ## # # # ## # # # # ####### ### ##### # ### # ##### # # # # # # # # # # # # ### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # # ## # # ## # ## ### # ### # #### #### # # ####### ### # ### # ##### ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # ### ### ## # # # # # # ## # ####### ## # # # # #### # # ##### ### ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
####### # #### ##### # # # # ## ### # # # ### ### ## # # # ### # ## ##### # # # # ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### ## # ## #### # # # ## # # ### # # # # # ####### ## ## ### # # ### # # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ### # # ## ## # ####### # # # # # # #### ## # # ### # ## # # # # ## ## # # # #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ## ####### # # ### # # # # # # # #### ## ### # ## #### ##### # ## ## ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### ### ### # # ####### # # # ## ### # # ### # # # ### ### ######### ### # # ###### #
|
||||
# # # # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S # # # # # # # #
|
||||
##### # ### #### ## ### ###### ### ### # # ## # # # ## ## ### ## # # # ### ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # ### ### # ### ### # #### # ### ### # # ### ### # # ##### ### # # ## # # #### ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### # ### ##### # # ## ## # # # ### ##### # ##### # ### # ##### # ###### #### # # # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## ## #### ####### # ### # #### # ## # ## #### ## # ### ####### # # ##### #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # ### # # # ## # # # # ### ## # ### # ######## ##### ### # ## # # ##### # # # # #####
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # ### ############# #### ### ##### # # # ### # ## # # # ## #### ### # # # ##### ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # # # ##### # #### # # # # ### # ### # ## ## ## # # #### ### # # # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # ####### ####### # ### # ## # # # ## ##### # ####### ### # ### ### # # ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ## ## #### # # ### ##### ### ### ### # #### # #### # # ##### ### # ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
#### ## ## # ### ## # # ## #### # ### # # # # ##### # # # # ### ## ###### ##### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ### # ### # # # ### # # # ## ### # ### # # ### # ## # # ## #### #### ## ## # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### ## # # ### ### ### ### # # # ####### ### ### # # ### # ### # ### # # # # ##### #### ### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ## ##### ### ### ##### # # # ## #### ##### ## # ### # ### ######### # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### ##### ### ##### # # # # # ## # # # # ### # # ### ##### ### # # ### # # # ## ####### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### #### # # # # ### # ##### # ### # ### # # # # ## # ##### # # # ######### ###### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ##### ##### ###### ###### # # # ### ### ### ## # ## # # # # ##### ### # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ### # # # # # ### ####### # ###### ## # ### ### ## ## ### # # ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # # # ### # ### # # # # ## ########## ### ##### ## # # ### # ## # # # ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## # ## # ### # # # # ##### ### # ###### # # ## ### # #### ## ### # ### ### # ## #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ## # # # # ####### ##### ### ### # # ##### ### ### # # ## #### # ### #### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # #### # ### # ### # ### # ##### # # # ## #### # # # # ### ### ### ###### ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ## ## # ## ### # ##### # # ##### # #### ##### ## ## ##### # ### ### # ### # ##
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ####### ## # ## ### ### ### # ## ## ##### # # # ### ## ## ##### ###### # ####### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ## #### ## # ## # # ### # ### # # # # #### # # # # # # ##### ### ### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ##### ### ## # # # # # # # # # # ## # ##### ##### ### # # # ### ####### ####### # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ### ### ##### ####### # # # ### # #### ### # # # ####### ### ## ## ####### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
### ######### # ##### # # # #### # ### # ### # # # # # ## ##### # ### ### # # # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ############# ### ### # # ## # ## ###### # ### # ## # # ### ### ####### # # # # # ## ## #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# #### ##### ##### ##### ##### # # # ### # # ## ## # # # ### ### ### ### ### # ##### ##### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
### ## #### ###### ##### ## ## # ## # # # # # ####### # # # ####### # ## ### ## ##### #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # ### ## ### ## #### ##### # ## # # ##### # # ##### # # ### ####### # ### ## # ### # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # ### #### # # ##### # # #### ### ## # ### # ### # # # ######### ### # # ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ## ## # ### # # # # ## # ### # ##### ### # # ######### ### # ### # ##### # #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ##### ## ###### # # # ## ##### # ### ### ### ### # # # ### # # ### # ######### # # # ##
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # #### ## ### # ##### ### # ##### ### ### # # ### ## # # # ##### # #### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # #### # ### ########## ## # # ### # # ####### ##### # ### # ### # ##### # ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ###### ### #### # ######### # ##### ### ### ######## ## ### # # # # ####### # # ##### ###
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # ###### ###### ### ### # ### # # ##### ### # ### # # ##### # #### # ###### ## # ### ### ### # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # ### # ##### ## ######## ### ##### ### ### ###### ########### # ####### ## ## # # ### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # #### ##### ## ######## # ########### ### ### ## # # ### # ####### # # ### ### # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # ##### # # ############# # # # # ### ##### # # ######## # ##### # # ### ### ## ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ## ## # # # # # ## # ##### # ## ###### ## ### # ### # # ### ##### # ##### ## ## ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ######### ### # # ######### # # # # # # ### ### # ## ## ## ## # # ### # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ##### # # ### # # # ## ## ### ## ## # ### # # ##### # # # # # #### ## # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ##### # # ### # # # # #### # # # ### # # ####### # # ##### # ## ### ### ###### ##### #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # ### # # ### # #### ### # ### # # #### # ######### ### ### ##### # # ### ##### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### ### ### # # # #### ### ## # # ##### # ### # ## # ## # ## #### ####### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # #### ##### # ####### # ## # # # # ### # ##### # ### ##### # ### # ####### # #
|
||||
# # # # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S# # # # # # # # # # # #
|
||||
# ### ### ## ##### ### # ### ### # # ### ## # # ##### ### # ### # # # ######### # ## ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ## ### # # # ### # # ### # # # # # # # # #### # # # # # # ##### # # ########## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
##### ###### ### ## ## # ### # # # ### # ############# # # # # # # ##### ####### # ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ### # # # # ### # ### # # ### # # # ######## ### # ## # # ### ##### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ##### # # # ### # # ### ### # # # # ## # # #### #### ## # # # ##### # ### ## ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### # # ### # # # # ## # # ### #### # # # ### # ### ### ### ### # # # ##### # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### # # ### ## # ### ### ### # # # # # #### ### ### # ## ### # # ############ # ##
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ##### ### # # # # # # ### # # ### # # # ### ##### ##### # # ### ### ## # # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### ## ## # ## #### ## # # # ## # # ### ### ### # ######### # # ### # ### # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### ###### ##### # # ########### # # ####### # # ### ### # # ### # ### # # # # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # ####### # # # ####### # #### ## # ## # ## # ### # # # ## ## # ### ## # #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### # # ################# ### ### # ### # # # # # # ### # # # ## # ## # ### ### # ##### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # #### ### # # # ##### # ##### ####### # # # # # # # ####### # ### ## # # ### ### # # #### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
### # # ### # ### ### # ## ## ##### ### # ## #### ### # #### ## ## ### # # ####### # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ### # ### # ## ##### # ##### # # ######### ### ### # # #### # #### ######## # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # # ## # # # ### # ##### # # # # # # # ## # # #### # # ### # ### # # ### # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # ## ### # ### # # # ### # # # # ####### ########### # # # ### # ###### ## ### # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### ### # # # # # # # # ####### # # ######## ###### #### ### ## ## # # # ### ## # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### # #### # # # # # ####### # # # ### ###### #### ## # ## #### # # ######### ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ###### #### # # # # ### # ### # # ### ### ### # ### # # ### # # # #### # ### ## # # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
####### ### ##### # ## # ## ## # ### # ### # ## # ### ####### # # # ### # # # ###### ## ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ## ##### ####### ####### ### # ### # ##### ##### ### ### # # # # # # ### ## # #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # ### ### ####### ######### ######## ##### # # # ############# ####### # ### ### # ###
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### ##### ### ##### # # # ### # ## # # # ### # ### # # # # ### # ##### ## ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
## # # # # # # ### # ##### # # ### ##### #### ############ ### ### # ###### ### # ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
##### # ## # # # #### ### ## # ##### ### # # #### ### ### # ##### # # ### ## ### # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### ##### ### # # ## # # # ##### # ####### ##### ### # ### ##### # # # ## # ## ###### ##
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
## # ##### ####### # ### # # # # # ##### ### # ### #### ### # ### ## # # ### # # ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # #### # # # ### ### ##### ##### # ### # # # ### # ##### # # ### # ### # ### # # # ### ####
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # # # # ### # # ### # # ### # ############# # # ##### ## # ## ### # ###### # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # ### ### ### # # #### ## # # # ##### # # # ## # # ## # # # # ### ## ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ##### ### ## # # ### # ##### # ####### # ## ## # # ### # # # # ## # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ####### # ######### ######## #### # # ## # ## ## # # # # # # # #### ##### # # # ###
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
## # # #### # # # ## # # ### ##### # # ### # # ### # ### # ## # ########### # # # ## ###### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ###### ####### # ## ########## # # ####### ## ## ### # # # ## # # # ####### # ## # # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # # # ## # ### # ## ## ## # # # # # #### ### # ### # # ## ## # ### #### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ## # ####### ##### # ##### # ### # # # # # # # # # ##### # ## # ### ##### # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### # # ##### ### ## ## ### # # # # ######### ##### #### # ### ### ## ### # ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # #### # # ## # # ### ####### # # ## ## #### ### # # # # # # ### # ########## # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ##### # ### # # # ### # ####### # # # ##### # # # # # # ##### # ## ## ### # ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### # # # ##### # # ### # ##### ### # ### ### # ### # ####### # # ####### # ### # ## ### ## #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# #### # # # ## #### # ### ## # ## # ### # ### #### # ### # ####### ######### # ##### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ####### ###### # # ## ## # ## ## #### # # ## ## #### ####### # ####### # # #######
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # ###### ## # #### ### # # #### #### #### ### ### # # ## ## # ### #####
|
||||
# # # # # # # # # # # # # #
|
||||
### # ## ## ##### ######### #### # # # # # ## # ######### ######### #### #### ### ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### ## ##### # ##### ### # # # # # ##### # ### ## # # ### ### # # # ####### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ############# # # # # ##### # # # # ### # ###### # # # ## # ####### # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ##### # # # ###### ## ### # # ##### # # ### # ### ### # # # # ### ## ## ### # # # # # ###
|
||||
# # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S # # # # # # # # # # # # # #
|
||||
# # # # ### # # #### ## # ##### # # # # # # # # ### ### # # ### # ##### # ### # # ## # ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
######### # ### # ##### # ## # # ##### # ######## ### ##### # # # # # ### # # ### ## # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ####### # # ### # ### ### # ##### ### ## # ## ### ### # # # # ## #### #### ## # ##
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
##### ##### ### ###### # # # ### # # # # ### # ### # ##### ####### # ### ##### # # ##### # #######
|
||||
# # # # # # # # # # # # # # #
|
||||
# ####### # # # ### # # # # ### # # ### #### # # ######### ## ## ### ### ## ## ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ### ### # ########### # # ### # ####### ##### # #### ###### # ### # # # ### # ### ## # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
### # #### #### # ####### # ####### # ## # # # # # # ##### ## ## # ##### ### ##### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### # # # # # ## ### # ### ### # # ### ## ## ##### # # # ### ## ######## ### ######### #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # ##### ##### # ### # # # # # # # ## # ### ## ## # # # ## ####### # # # # # ### ###### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### #### # # ## ### # ##### # # ### ### # # ###### ##### ### # ###### # ## ## ### # ## #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
## #### # ### # # ##### # # # # # ### ############ # ####### ###### ## ####### # # ## ## # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ##### # ### # ### ####### # # # ## ##### # ### # ######### #### # # # # # # ##### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # ### # # # # # ### # # # # # ### # # # # ##### ### # # # # # ####### ## ##
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## ##### ## # # ### # ## ## ## # #### # ### ##### # ####### # # # ### # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ###### # ### ## # ##### # # # # #### # # # # ### ######### ##### ##### ###### ## # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ###### # ### # # ##### # ### ##### # ### # # # # ## # ## ### # # ## # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
#### ### # # # # ######### ### ### # # # # ##### # ##### #### # # # ### ### ####### # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ## ##### #### # # #### ## # ###### #### ## # # ####### ## # ## ### ### ## ##### # #
|
||||
# # # # # # # # # # # # # # #
|
||||
## # ############ ##### ### # # ### # ###### ## # # ## # # # ### # # ### ## ##
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### ## ## ##### ## ####### ##### ### # # ### # # #### # ### # ### # #### #### # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # # # # # ### ### ## ###### # ##### # ##### # ### # ### # # ######## ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ## # # # # # # # ### # # # #### # ### ### ### # ## ### # # ### ### ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ##### # ## ### ## # # # # # ### # # # ######## ###### # ##### ### # # # ## # # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ## # # # ######### # # ## #### ############## # # ## # # ## # # ############# ### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # # ###### ## ######### # # # # # # ## # ########### ## # ## ###### ##### # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
######### # ### # # # #### ## ### # ### ### # ##### # # # ### # # # # ### # # # ### ## ## ## #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ##### ## # # ##### ## # ##### ##### # ### #### ###### ####### # # ### ########## #### # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ### ### # ## # # ### # # ### ##### # ####### ### ## # # ### ### # # ### ## ####
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ### ##### ## ##### ### ## ####### ## ### # # ### # # ######### # ######### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ## ###### # #### # # ####### # ### #### # ####### # # ##### # # ##### # ### ####### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
### ### # # # #### ############ ### ### ### # ### # # ### ### ### ##### # ####### # ## #### # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ### ##### # ### # ## # ########### #### ## # ## # ## ###### ## # # # # ### # ###### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ### # # # # # # ## # #### ##### # # ### ###### # ##### # # # # ## #### # # # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## ## #### ### ## # # ### ##### ##### ### ## ###### #### ### ## # ### # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ## ## # ### # # ### # # ### # ### # # # ## # #### #### # ### # # ### #### ## ######
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ###### ## # # # ### ### ####### ##### # ### # # ### # ### # # # # ## ## # # # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # # #### # # ### ### ### # # # ### # # ### # # # # ##### ####### # # ##### ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## ##### # ## ##### ## ## ### ### ### ### # # ##### #### ## # # ## ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # ##### # ## #### # ####### # # ### ### # # ## ## # # ########### # ### # # # #####
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ## ## # ## # #### ####### ## ### ## #### ###### # # ## #### # # ####### # ##### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
### # ########## #### # ##### # # ##### # ### ##### # #### ## # # # # ##### ### ## ### # # # ####
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # ##### # # # ####### ## ## ## ## ### # ##### # # ### ##### ### ##### ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # #### ## # ##### # ## ##### # ### # #### #### ##### # ## ####### # # ######### ## # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ##### # ####### # # # # ### ### # # ### ## # ### # ## # ### # ##### # ##### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### # ## # # ## ## # ### ## ## ## # ##### ### ####### ### ####### # # ## # ### # # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # #### #### # # # ## # #### #### ## ######## #### # # # ### # # # # # # # # ## ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## ## # ## ### ## # ### # ### # ## # # # ### # ### # # ## ####### # # ### ###### # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
## # ### ## # ## ### # # # ##### ### # ##### ##### # ## # ######## # # ## ### ### ### ## # # #
|
||||
# # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S# # # # # # # # #
|
||||
# ### # ##### ### # # # ## ### ######### ############# # # ########### # # # # ##### # #### # ###
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
### # ### # ### # # ##### ####### ####### # ### # ## # ### ## ## # ### ##### ##### ## ##### ## #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ## ## ### # ### #### ## # # ## ##### ### ## # # ### # # ### ##### # # ##### # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # # ### # # # ## ## #### #### ## ## # # ## # # # ### ### ## ##### ####### ### ###
|
||||
# # # # # # # # # # # # # # #
|
||||
# # ##### # ## ## #### # #### ## # # ### ### ####### ### ## ###### # ##### # # ##### ### ## #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
## # ### # # # ### ##### # # ##### ### # ### ### # # # # # ### ### # ### ###### # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # ## # # # ####### ### ### ## # ### # # # ### # # # ### ### # ### # ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ###### # # # ####### ##### #### # ###### # # ### # # ### # # # # ##### # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ####### # # # ### # # # ### ### # # # # ##### # ##### # # # # ## # ### # # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # #### ## # # ### ## ## # # ## # ### ###### ## # ### # ### # # # ##### ####### # ### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# #### #### ####### # ##### ## # ### ## ##### # ### ##### # ### # # ## # # # #### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
#### # ## # # # # # # ### ### ### # # # ### ####### # ##### # ##### ##### ####### # # # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ### ## # ### ### ## ### # # # #### ## # ### #### # ##### # ### # ##### ## ## ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ### ### ### # ## ## ## # # ### ##### # ### # # # ## ## # # # ### ####### # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### # ### # # ### ## # ### # # # ### # # ### # ##### # ##### ###### ### # ### ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ## ### ### # ## # # ### # # ## ##### # ##### # # # ### # ### ### # # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ### # ### # # # # ### # # # ##### ### # ## ## ####### # # ## ## ##### ### ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # ### # ### # ## #### # # # # # # # ### ## # # # ### ### ### ### # # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ######## ### ### ### # # #### ### ## # # # ##### # # # # #### ### # # ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # # ## ### # # # # # #### # ### # ## ##### # # # # # ####### # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## # ### # # # ### #### ### ## # # # #### ##### # # # ##### # #### ## ### # # #### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ##### # # # ## # # ##### ### # ### # ## #### # ### ## # # ### # ### ### # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # # ##### # ###### # ########### # ##### ### # ### # # ########## ### ##### # # # # # # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ####### ######### # # ##### # # # ## # # # ### # # # # ### ### ### ####### # ## ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # ### # ## ## # # # # # ### # ##### # # ### # # # # # # # # # ### # # ## # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
##### # # ##### # #### #### ### # # # ############# # # ### # ### ## #### # # #### # # ####
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # # ##### #### ### # #### # # # ### # #### ## # # # # ### # # ### ###### # ## # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # # ### # # ############# # # ##### # # # # ### ### # ## # ### # #### ## # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
## # # ### ## # ## # ## # ##### # # # ###### # ## ### # # # ### ### # ###### ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # ## # # ##### ##### # # # ##### # # # ### ### # # ### # # # # # ### ### ###### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ##### # ## # # ### ### ####### # # ### ### # # # ##### # # ############ # # # # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## ### # ### ### # # ### ### ### ##### # ##### ##### # ## ## # # # ### ### ###### ###### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ######## ## ##### ##### ### ##### ##### # # # # # # # # # # ### ### # # # # # #### # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ### # # # # ### ##### # # # # # ### ### # # # # ##### ##### # ## # ## # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # # # # # # ### # ##### # ### ### ### ## # # ### # # ### ###### ##### # # ### ## ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ## # # # # ## #### ### ###### # # # ###### ## # ## ## # ##### # # # ### ####### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### ### ### ### # # ### ####### # ## ## ### # # # # ########### # ### ##### ### # ## ##### #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ## ####### # ##### # ### # ## ## ######## # ### # #### # # ####### ### ####### #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ##### ##### # # ### # # ## ###### ## # ##### # #### # ### # # # # ##### # # ### # # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## # # # # ### # ## # # # ##### # ##### # ########### # ##### # ####### # ### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # # #### ### ##### # ##### # ##### # # ### # ### # # # ####### # # # # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### ####### ### # # ### # # ## # #### # ##### # # ### # ### ## ## # #### ## ## #####
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # ###### # ### # ##### # ## ######## # # # # # ######## ##### ####### # #### # ####### #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
##### # ### # # ### # ### ### ### ### ##### ## # # # # # ## ### # ### # ### # ## # ## # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # # # # ### # ### # # ### ### ### # # ####### #### # ### # # # ##### # ### # ### #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ### ### # # # ### # # ### # ######### ### ## ### ## ### # ### # ## ##### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # # ####### # ### ## # # ### ##### ## ### ##### # # # ## #### # ### # # ### ##### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### # ### ## # # ### # ##### # ##### # ### ## ##### ### # # ######### # # ### # # # # # #
|
||||
# # # # # # # # # # # E#
|
||||
###################################################################################################
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
###################################################################################################
|
||||
#S # # # # # # # #
|
||||
### # # ### # ### #### ## ##### # ### # ### ### #### # ### ### # ####### # # # ### # ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # ############### # ### ###### ### # ### ######## ### # # ##### # # # # # ### # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## # # ##### ## ## ### ### # ### #### #### # ### ### ### # ##### # ### ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### ### # # ##### ###### ######## ## #### # # # # # # ## # # # ### ### ### # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ####### ## # ############### ### # ##### # #### # # # # ### ## # # ### # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # ## ## ##### ### # ## ## #### # # # ##### ####### ### # ## ## # #### # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
### ### ### # ## #### # ### # # # ###### ### # ####### # # ## # # # # ### # ## ## # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # # # # ####### # ### ##### ####### # # # # ####### # # #### ###### # # ### # ## ### # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# #### # # ## ######## ### ##### # # ##### ###### # ##### #### ## ## ## # ### # #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ###### ##### ### # # # # # # ### # ## ## ### # #### # ### # # # # ### # # # ### ###### # ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # # ##### # #### #### ####### #### # ### ### # ## ###### # ## # # # # # # # # ### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ## # # ######## ## ## ### ### ### ####### # ### # # ### # ### #### # ## # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ### # # # # # # ## ### # # ## # # ### ### ### # # # # # ##### # # # # ### ### # #### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # # # ## # ### ### ##### # ### # # ####### # ####### ### # ###### # # ##### # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # ## ## # ##### # # # ## ####### # ## ### ######### # # ##### # #### ## ## # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # # #### #### #### ### # ####### # # # ## ##### # # ##### # ######### ### # ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
## # # # # # ## ### # # # # # ### ## ### # ### # # # # ####### # ## # ### ## # ### ### # ###
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# ##### # # # # ##### # # ##### ### # ##### # ##### # ## #### # ##### # # ####### # ### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # # # ### # # ### # # ## ## # # ### # #### # # # # ####### ### #### # ### ## ######
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # # #### ### #### ### # # ### ####### ###### # # # ##### # ################# # # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
#### # # ### # # # # ### # # ### # # ####### ### ### # # ## ## # # # # #### # ## ## ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ## # # # # # # ## ## # # # ### # ### # # ### ### ### ## # ### ######### # ## ## # ## ##
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ### # # ### ### # ### ## # ## # ## # ## ### # # # # # ### # # # # # ### ### # ### # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ## ### # # ### # # ##### # # ## ###### #### # ##### # # # # # # ### # ## # #### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ####### ## ## ### # # # ### # # # ## # # ## #### # # ####### # # # # ##### ## # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
####### ### # ### # ### ### # ########### # ### ### # # ###### ## # # # ####### ####### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ## # # #### #### # # #### # # ##### # # # # # # # # # # # ##### # # #### # # # # ##
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # # ## # # ####### ### ##### # # ####### # ### # # # # # # # ## ## #### #### ####### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ##### # ## ## ### ### ### # ##### # # ##### # # ###### #### # ## ## # ### # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # ### ### # # # # ### #### ########## # # ##### ### # # ## # # # ### # # ### ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
### # ### # # # # # ####### ## # ### ### ##### # # ##### ## # # ##### # # ##### ####### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # ###### # ### #### ## # ### ### # # ## # ### #### ### # # # ### #### # ## ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## # #### ###### # ## # ## ### # ### # # ### ## # # ##### ### ######### # # ## # # ###
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # # ######## ### # ### ### ### ##### # # # # # ### # ########## # ######### # # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### ## # # ## ## ##### # # # # # ### # ##### # # ### # ### # # # # ### ### ### ## ## # ##### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ##### # # # ## # # # # # # #### ############## ## # # # # ### # # ##### ##### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ## # ### ####### # ### # # #### # # ### ######### # ##### ####### # ### # ### ## # ### ###
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ## #### ## ## ### # # # ##### # # # ## # #### #### # ## ## ## # #### ### #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # ### ### ##### # # ##### # ### # ##### # # ### # ### # # ### # # # #### # ## #### # ### #
|
||||
# # # # # # # # # # # # # # # # # # # # #
|
||||
# ### # ##### # # # # ### # ### ### ### # # # # # # # # ### ###### ## # # # ######### # # ##
|
||||
# # # # # # # # # # # # # #
|
||||
# # # # #### ###### #### # #### ###### # ## #### # ## # ### ## # ###### ##### ############# #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # # ###### # ####### # # ### # # # # # #### #### ##### # ### ##### # # ### # ## #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# ### ## # # # # ##### # # # ### ##### # # # #### #### ####### # ### ### ### ### # #### # # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # ### ##### ### # #### ### # # # # ### ####### ##### ##### # #### ### ### ########## #####
|
||||
# # # # # # # # # # # # # # # # # # # # # # #
|
||||
# #### ##### ### ### # ###### # ### # # ### # # ### ## # ### ### ###### ##### ### # # ### # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # ### ##### # # # # # # # ### ##### # ### ## # ### # ######## # ## ## # # ##### # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# ### ## # ## # ####### ##### # # #### # # ##### ### # ###### # # ######## # # # # # ### ### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
####### # # ### ########## # # # ## ## # # ### ########### ##### ### ### # # ### # # # # #
|
||||
# # # # # # # # E#
|
||||
###################################################################################################
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v1.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v1.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S# #
|
||||
# # # ###
|
||||
# # # #
|
||||
# ##### #
|
||||
# # #
|
||||
##### # #
|
||||
# E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v10.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v10.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S # #
|
||||
### ### #
|
||||
# # # #
|
||||
# ### # #
|
||||
# # #
|
||||
# ##### #
|
||||
# E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v2.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v2.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S# # #
|
||||
# # # # #
|
||||
# # # #
|
||||
##### # #
|
||||
# # # #
|
||||
# ### # #
|
||||
# E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v3.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v3.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S #
|
||||
####### #
|
||||
# # #
|
||||
# # ### #
|
||||
# # # #
|
||||
# # # ###
|
||||
# # E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v4.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v4.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S# #
|
||||
# # ### #
|
||||
# # # #
|
||||
# ### # #
|
||||
# # # #
|
||||
# # ### #
|
||||
# # E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v5.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v5.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S # #
|
||||
##### # #
|
||||
# # # #
|
||||
# # # # #
|
||||
# # # #
|
||||
# ##### #
|
||||
# E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v6.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v6.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S # #
|
||||
##### # #
|
||||
# # # #
|
||||
# # # # #
|
||||
# # # #
|
||||
# ##### #
|
||||
# E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v7.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v7.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S# #
|
||||
# ### ###
|
||||
# # #
|
||||
### ### #
|
||||
# # # #
|
||||
# # # # #
|
||||
# #E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v8.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v8.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S # #
|
||||
### # # #
|
||||
# # # # #
|
||||
# # # # #
|
||||
# # # #
|
||||
# ### # #
|
||||
# #E#
|
||||
#########
|
||||
9
skorohodovsa/task_2/source/templates/10x10_path_v9.txt
Normal file
9
skorohodovsa/task_2/source/templates/10x10_path_v9.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#########
|
||||
#S# #
|
||||
# ##### #
|
||||
# # #
|
||||
### # # #
|
||||
# # # #
|
||||
# ##### #
|
||||
# E#
|
||||
#########
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v1.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v1.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # # #
|
||||
##### # ##### ### #
|
||||
# # # # # #
|
||||
# ### # # ##### # #
|
||||
# # # # # # #
|
||||
# # ######### # # #
|
||||
# # # # #
|
||||
# ######### # #####
|
||||
# # # # #
|
||||
# ### # # # ##### #
|
||||
# # # # # # #
|
||||
# # ### ####### # #
|
||||
# # # # # # #
|
||||
# # # ### ### ### #
|
||||
# # # # #
|
||||
# ### # ### ##### #
|
||||
# # # #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v10.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v10.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # # #
|
||||
##### # # # ##### #
|
||||
# # # # # # # #
|
||||
# # # # # ### # # #
|
||||
# # # # # # #
|
||||
# ##### ### ### # #
|
||||
# # # # #
|
||||
# ### ####### ### #
|
||||
# # # # # #
|
||||
### ##### # ### # #
|
||||
# # # # # #
|
||||
# ######### # #####
|
||||
# # # # #
|
||||
# # ##### # ##### #
|
||||
# # # # # #
|
||||
# ### # # # # ### #
|
||||
# # # # #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v2.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v2.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # # #
|
||||
######### # # # # #
|
||||
# # # # # #
|
||||
### ##### ##### ###
|
||||
# # # # # #
|
||||
# # # # # # # ### #
|
||||
# # # # # # # #
|
||||
# ### ##### ### # #
|
||||
# # # # # #
|
||||
# # ####### # ### #
|
||||
# # # # # #
|
||||
# ### # ##### ### #
|
||||
# # # # # #
|
||||
# ####### # ### # #
|
||||
# # # # # # #
|
||||
# # # ### # ### # #
|
||||
# # # #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v3.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v3.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # # # #
|
||||
### # # # ### # # #
|
||||
# # # # # # # #
|
||||
# # ##### # # # # #
|
||||
# # # # # # # #
|
||||
# # # # ### #######
|
||||
# # # # # #
|
||||
# # # ### ####### #
|
||||
# # # #
|
||||
# ### # ######### #
|
||||
# # # # #
|
||||
# # ##### ### #####
|
||||
# # # # # #
|
||||
# ### # ##### ### #
|
||||
# # # # # #
|
||||
####### # # ### # #
|
||||
# # # #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v4.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v4.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # # #
|
||||
### ####### # # ###
|
||||
# # # # #
|
||||
# ### ### ####### #
|
||||
# # # # #
|
||||
####### ####### # #
|
||||
# # # #
|
||||
### ### # ####### #
|
||||
# # # # #
|
||||
# ######### ### # #
|
||||
# # # # #
|
||||
### # # # ####### #
|
||||
# # # # # # #
|
||||
# ### # ### # # # #
|
||||
# # # # # # #
|
||||
# # # # ##### #####
|
||||
# # # #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v5.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v5.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S# # #
|
||||
# # ### # ####### #
|
||||
# # # # # # # #
|
||||
# # # # # ### ### #
|
||||
# # # # # #
|
||||
##### # # # # #####
|
||||
# # # # # # #
|
||||
# # # # ### ##### #
|
||||
# # # # # #
|
||||
# ####### ### ### #
|
||||
# # # # #
|
||||
# # ##### # # # ###
|
||||
# # # # # # # #
|
||||
# ### ####### # # #
|
||||
# # # # # #
|
||||
# # ### # ##### # #
|
||||
# # # #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v6.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v6.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # # #
|
||||
### # # ### ### ###
|
||||
# # # # #
|
||||
# ### ########### #
|
||||
# # # # # #
|
||||
### # # ### ### # #
|
||||
# # # # # # # #
|
||||
# # # # # ### ### #
|
||||
# # # # # #
|
||||
# ####### # ##### #
|
||||
# # # # # #
|
||||
# ### # # # # #####
|
||||
# # # # #
|
||||
########### ##### #
|
||||
# # # #
|
||||
# # ########### # #
|
||||
# # #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v7.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v7.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # # #
|
||||
##### # ### ### # #
|
||||
# # # # # # #
|
||||
# # # ##### ### # #
|
||||
# # # # # #
|
||||
# ####### # # ### #
|
||||
# # # # # #
|
||||
# # ########### # #
|
||||
# # # # #
|
||||
# ####### ### # ###
|
||||
# # # # #
|
||||
########### # ### #
|
||||
# # # # #
|
||||
# # # ### # ##### #
|
||||
# # # # # #
|
||||
# # ####### ##### #
|
||||
# # #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v8.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v8.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # #
|
||||
### # ### ####### #
|
||||
# # # # # # #
|
||||
# ### ##### # # # #
|
||||
# # # # #
|
||||
# ### ####### #####
|
||||
# # # #
|
||||
### ####### ##### #
|
||||
# # # # # #
|
||||
# # # # ##### ### #
|
||||
# # # # # #
|
||||
# # # ######### # #
|
||||
# # # # # #
|
||||
# # ########### ###
|
||||
# # # #
|
||||
# ########### ### #
|
||||
# #
|
||||
###################
|
||||
19
skorohodovsa/task_2/source/templates/20x20_noexit_v9.txt
Normal file
19
skorohodovsa/task_2/source/templates/20x20_noexit_v9.txt
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
###################
|
||||
#S # # #
|
||||
### # ##### # ### #
|
||||
# # # # # # #
|
||||
# # # # ####### # #
|
||||
# # # # #
|
||||
# ####### ####### #
|
||||
# # # # #
|
||||
####### ##### # # #
|
||||
# # # # #
|
||||
# ### ### # ##### #
|
||||
# # # # # #
|
||||
### ### # ### # ###
|
||||
# # # # # # # #
|
||||
# ### # # # ### # #
|
||||
# # # # # #
|
||||
# ### ####### ### #
|
||||
# # #
|
||||
###################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v1.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v1.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v10.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v10.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v2.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v2.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v3.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v3.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v4.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v4.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v5.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v5.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v6.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v6.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v7.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v7.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v8.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v8.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
30
skorohodovsa/task_2/source/templates/30x30_empty_v9.txt
Normal file
30
skorohodovsa/task_2/source/templates/30x30_empty_v9.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
##############################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
##############################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v1.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v1.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S # # # # # # # #
|
||||
##### # ### # ### # # # # ### ##### # # ####### #
|
||||
# # # # # # # # # # #
|
||||
### ##### ### # ################# ############# #
|
||||
# # # # # # # # #
|
||||
# ##### # # # ########### ##### # ##### # # #####
|
||||
# # # # # # # # # # # #
|
||||
# ### ### # ### ########### ######### ### ### # #
|
||||
# # # # # # # # # # # #
|
||||
# # ####### ### # ##### ##### # # ##### # # ### #
|
||||
# # # # # # # # # # # #
|
||||
##### # ##### ### ### ######### # ### ### ##### #
|
||||
# # # # # # # # # # #
|
||||
# ####### # # ##### ##### ### ### # ### #########
|
||||
# # # # # # # # # # # # #
|
||||
# ####### # # # # # # # ############# # # ##### #
|
||||
# # # # # # # # # # # # #
|
||||
### # # ####### # # ### ### # # ####### ### # # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ########### ### ### # # ##### ### # # ##### ###
|
||||
# # # # # # # # # # # #
|
||||
# # ##### # ####### # # ######### ######### ### #
|
||||
# # # # # # # # # # # #
|
||||
# ### # ##### ##### # # # # # # # # ######### # #
|
||||
# # # # # # # # # # # # # # #
|
||||
# # # ########### # ##### # ### # # # ######### #
|
||||
# # # # # # # # # # # # #
|
||||
# ##### # ### ### # # # # ########### # ### #####
|
||||
# # # # # # # # # # # # # #
|
||||
# ### # # # ### # ######### # # ##### ### ### # #
|
||||
# # # # # # # # # # # # # #
|
||||
### # # ### # # ### # ### # ##### # ### ####### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ####### ### # # # # # ####### # ### ####### # #
|
||||
# # # # # # # # # # # # # #
|
||||
##### # # ##### ### ### ### # ### # # # # # ### #
|
||||
# # # # # # # # # # # # # # #
|
||||
### # ### # # ### ### ####### # ##### # ##### # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # ### # # ### # ####### ##### ### # # ### # # #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# ##### ### # ##### # # ##### ### ### ### ### # #
|
||||
# # # # # # # # # # # # # # #
|
||||
### # ### ### # # # # ### # ### ### ### ### ### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# ######### # # # # # # ### # ### ### # # # #####
|
||||
# # # # # # E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v10.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v10.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S# # # # #
|
||||
# # ##### # # ### ########### ####### ### #######
|
||||
# # # # # # # # # # # #
|
||||
##### # # ### # ### ### # ##### ####### # # ### #
|
||||
# # # # # # # # # # # # #
|
||||
# ### # ### # ####### # # ####### ######### # ###
|
||||
# # # # # # # # # # # #
|
||||
# # ### # ### # # ####### # # # ############### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### ### ##### # # # ########### ####### # # # #
|
||||
# # # # # # # # # # # #
|
||||
### ### # # ##### ######### ### # # ### ### ### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# ### # # # # ##### ##### # # ### # # ### ##### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# ### # ##### ##### ### ##### # # ### # ### # ###
|
||||
# # # # # # # # # # # #
|
||||
# # ##### ### # ##### ####### # ### ######### # #
|
||||
# # # # # # # # # # # # #
|
||||
# ### # ### # ##### # # # ##### # ### ##### ### #
|
||||
# # # # # # # # # # # # # #
|
||||
####### # # ######### ####### # ### ### ##### # #
|
||||
# # # # # # # # # # # #
|
||||
### ### # ### # ### # # ### ############# # ### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### ### # # # # # ##### ##### # ##### # # ### #
|
||||
# # # # # # # # # # # # # #
|
||||
# # # # # ### ##### # # ### ######### # # ##### #
|
||||
# # # # # # # # # # # # # #
|
||||
# # ### ### ### ##### ### ### # ##### # ### ### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# ### # # ### # # # # # ####### # # ##### ### # #
|
||||
# # # # # # # # # # # # # # #
|
||||
# # ##### ### # # ##### ##### # # ##### # # ### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ##### ### ##### # # ### # ### ##### ##### # # #
|
||||
# # # # # # # # # # # #
|
||||
####### # ######### # # ####### # ### # ### #####
|
||||
# # # # # # # # # # # #
|
||||
### ##### ####### ####### ##### # # ### # ##### #
|
||||
# # # # # # # # # # # #
|
||||
# ### ##### ### # # # ##### # # # # ########### #
|
||||
# # # # # # # # # # #
|
||||
# # ### # ##### ####### ############# # ####### #
|
||||
# # # # # # # # # # # #
|
||||
# ### # ##### ### ### ##### # # # ####### # # ###
|
||||
# # # # # # # E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v2.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v2.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S # # # # # # # # #
|
||||
### ### ##### ### # # # # # # # ### # ### ### # #
|
||||
# # # # # # # # # # # # # # # # # # #
|
||||
# # # ### # ### # # # # # # ##### ##### ### # # #
|
||||
# # # # # # # # # # # # # # #
|
||||
# ##### ### ####### # # ####### ### ##### # # # #
|
||||
# # # # # # # # # # #
|
||||
# # ####### # ####### ### ### ### ### ######### #
|
||||
# # # # # # # # # # # # # #
|
||||
# # # ### # # ##### # # # ##### ### ### # ### ###
|
||||
# # # # # # # # # # # # #
|
||||
####### ######### # ####### # ### # ########### #
|
||||
# # # # # # # # # #
|
||||
# ### # # ##### # ########### # # ####### # ### #
|
||||
# # # # # # # # # # # # # #
|
||||
### ####### # # ### # ### # ### ##### ####### # #
|
||||
# # # # # # # # # # # #
|
||||
# ### # ##### ### # ### ### ####### ### ### # # #
|
||||
# # # # # # # # # # # #
|
||||
# # ########### # # # ### ### ### ############# #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### # ######### # # # ### ### ### # # # ### # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
####### # ####### # # ##### # ### ##### # # # ###
|
||||
# # # # # # # # #
|
||||
# ####### ######### ### ##### # ### ########### #
|
||||
# # # # # # # # # # # # #
|
||||
# ### # # ### # # ### ##### ##### ### ####### # #
|
||||
# # # # # # # # # # # # #
|
||||
# # # ##### ##### # # # # ####### # ### ####### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
### ### ##### # # # # # # ### # ##### # ### ### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# ### ######### ### # # ### # # ##### ### ##### #
|
||||
# # # # # # # # # # #
|
||||
# # ########### # # ##### ### ### ##### ### # ###
|
||||
# # # # # # # # # # # # #
|
||||
# ######### # ##### # ##### ### # # # ##### ### #
|
||||
# # # # # # # # # # #
|
||||
# # ### ### ########### ######### # ######### ###
|
||||
# # # # # # # # # # # #
|
||||
# ### # # # # ##### ####### # ####### # # # ### #
|
||||
# # # # # # # # # # # # # # #
|
||||
### # # ######### ### # # # # # ### ##### ### # #
|
||||
# # # # # # # # # # # # # # #
|
||||
# ### ### # ### # # ##### # ##### ##### ### ### #
|
||||
# # # # # # E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v3.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v3.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S# # # # # #
|
||||
# ##### # # # ####### # ##### ##### ### ##### # #
|
||||
# # # # # # # # # # # # #
|
||||
### # ### # ##### ##### # # ######### ### # ### #
|
||||
# # # # # # # # # # # # #
|
||||
# ##### ##### # # # ######### ##### # ##### # ###
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # ### # # # ##### # # # # # ##### ### # ### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ##### # # ### # # ########### # ####### ### ###
|
||||
# # # # # # # # # # # #
|
||||
# # ########### ### # ######### # # ########### #
|
||||
# # # # # # # # # # # #
|
||||
# ##### # # # ### ### ####### # ##### # ### # # #
|
||||
# # # # # # # # # # # # #
|
||||
##### ########### # ####### ##### # # ### # #####
|
||||
# # # # # # # # # #
|
||||
### ### # ######### ############### # # ####### #
|
||||
# # # # # # # # # #
|
||||
# ######### ### ##### # ### ### # ### # # ##### #
|
||||
# # # # # # # # # # # #
|
||||
# ##### # # # ### ### ### # # ### ##### # ### ###
|
||||
# # # # # # # # # # # # # # #
|
||||
##### # ##### # ### ##### # # # ### ##### # ### #
|
||||
# # # # # # # # # # # # #
|
||||
# # # ### # ########### ### # ######### ##### # #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### # # # # ####### # ### # # ######### #######
|
||||
# # # # # # # # # # # # # #
|
||||
# # ##### ### ##### ##### ### ### # # # ### ### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # # # # ### # ### # ##### # # # # # # ### # #
|
||||
# # # # # # # # # # # # # # #
|
||||
##### ### ####### # ##### # ######### ####### # #
|
||||
# # # # # # # # # # #
|
||||
# ##### ### ### # ######### # # ####### # ##### #
|
||||
# # # # # # # # # # # #
|
||||
### # ##### # ##### ### ##### ##### # ##### ### #
|
||||
# # # # # # # # # # # # #
|
||||
# # ### # ### # ### ##### # ####### # # ### # ###
|
||||
# # # # # # # # # # # # # #
|
||||
# ### ##### ##### ### ##### # ######### # ##### #
|
||||
# # # # # # # # #
|
||||
# ##### ##### # ### ### ####### #################
|
||||
# # # # # # # # # #
|
||||
##### ### ####### ### ####### ### # ##### ### # #
|
||||
# # # # #E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v4.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v4.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S# # # # # # #
|
||||
# ### # # ### ##### ##### ### ### # # # # #######
|
||||
# # # # # # # # # # # # #
|
||||
####### ### ##### ### ##### # # # ### # ####### #
|
||||
# # # # # # # # # # # # #
|
||||
# # ##### ### # ### ### ### ### # # ### # ### # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # # ### # ### ### # ####### # ####### ##### #
|
||||
# # # # # # # # # # # # # #
|
||||
### # ####### # # # ### # ############# ### # # #
|
||||
# # # # # # # # # # # #
|
||||
# # ####### ### # ######### ### # ### ### ##### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ##### # ### # # # # # ########### ### # # # ###
|
||||
# # # # # # # # # # # # # # #
|
||||
# ### ### # ##### # # # # # ##### ### # ####### #
|
||||
# # # # # # # # # # # # # #
|
||||
# # ### ####### ### ##### # # # # # ######### # #
|
||||
# # # # # # # # # # # # # #
|
||||
##### ### # ##### ######### # # ####### ### ### #
|
||||
# # # # # # # # # # # # #
|
||||
# ### # ### # # # # # # ##### ##### # ### ### # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # ### # ##### # # # ##### ### ##### # # # ### #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # ##### ### # ### # # # ##### # # ###
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # # ##### ### # # ### # ### # ##### ####### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# # # ######### # ### # ### ##### # # # # #######
|
||||
# # # # # # # # # # # # #
|
||||
# # ##### # # # ### ### ### ######### ##### # ###
|
||||
# # # # # # # # # # # # #
|
||||
# ######### # ### ### ### # ##### # ### ### ### #
|
||||
# # # # # # # # # # # # #
|
||||
# # # ####### # # # ######### # ##### ### ### # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
### # ####### # # # # # # # ### # # ### # ### # #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# ####### # ### # # # # ### # ### # # ### # ### #
|
||||
# # # # # # # # # # # # #
|
||||
# # ### ##### ####### ####### # # ####### # ### #
|
||||
# # # # # # # # # # # # #
|
||||
# ### ####### # # # ####### ### ### # # ##### ###
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # # # ##### ####### # ##### # # # ##### ### #
|
||||
# # # # # # E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v5.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v5.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S# # # # # # #
|
||||
# ### # # # # # # ### # ##### ####### ### # ### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# # ####### # ######### # # ##### # ### ### # ###
|
||||
# # # # # # # # # # # # # #
|
||||
##### # # # # ##### # ### # ####### # # # ##### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # ### # # # ### ####### # # # # ### ##### #####
|
||||
# # # # # # # # # # #
|
||||
# ### ##### ####### # # ########### # ### ##### #
|
||||
# # # # # # # # # # # # #
|
||||
# # ### ##### ##### # ### ### ######### ##### ###
|
||||
# # # # # # # # # #
|
||||
# ####### ### ####### # ############# # ####### #
|
||||
# # # # # # # # # # #
|
||||
# # # # ### # ### # # ### # ############### # ###
|
||||
# # # # # # # # # # # # #
|
||||
# # # ### ##### ### ### # ### # ########### ### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
####### ### # # # ### # ### # ### # # ### # # # #
|
||||
# # # # # # # # # # # # # # #
|
||||
### # ### # ####### # ### ##### # ##### ### # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # # ### ### ### # # ##### ##### # # ### #######
|
||||
# # # # # # # # # # # # #
|
||||
# ##### ### ### ### ### # # # ######### ####### #
|
||||
# # # # # # # # # # # # #
|
||||
# # ##### # # ##### # ### ####### # # # # # #####
|
||||
# # # # # # # # # # # # # #
|
||||
# ### ### # ##### ##### ### # ##### # ### ##### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# ### # ##### ##### # # # # ### ####### # # # ###
|
||||
# # # # # # # # # # # # # #
|
||||
# # ########### # ### ### ####### ### # # # ### #
|
||||
# # # # # # # # # # # #
|
||||
# ##### ########### ### # ### ##### # # # ##### #
|
||||
# # # # # # # # # # # # #
|
||||
### # # # # ####### ##### # ####### ####### #####
|
||||
# # # # # # # # # # #
|
||||
# ### # # # # ### ############### # ##### ##### #
|
||||
# # # # # # # # # # # #
|
||||
# # # ##### # # ######### # # ####### # ####### #
|
||||
# # # # # # # # # # # # # #
|
||||
# # # # # # # ##### # # ####### ### ####### ### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# # # # # ##### # # # # ##### ### # # ### #######
|
||||
# # # # # # # # E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v6.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v6.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S # # # # # # #
|
||||
### # ### ######### ####### ##### ### # # # # # #
|
||||
# # # # # # # # # # # # #
|
||||
# ##### # # ##### ##### # ### # # # ### ### #####
|
||||
# # # # # # # # # # # #
|
||||
############# ####### ####### ####### # ##### # #
|
||||
# # # # # # # # # #
|
||||
### # # ### ### # # # # ####### ######### ##### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### # # ##### # ##### # ### ### # ### ### ### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ##### # ######### # # ### # # ### # ### # # ###
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # # ### # # # ### ####### ### ### # ####### # #
|
||||
# # # # # # # # # # # # #
|
||||
# # ### ######### ### ### # # # # ##### ### ### #
|
||||
# # # # # # # # # # # # # # #
|
||||
### # ######### # # ### ### # # # # # ### # # ###
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ##### # # # # # ### # # # ####### ### ####### #
|
||||
# # # # # # # # # # # # #
|
||||
# # ##### # ### ####### # ### ### ########### # #
|
||||
# # # # # # # # # # # # #
|
||||
# ### ### # # # # ### # ##### # ##### # ####### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# ### # # # # # ### # ##### ####### # ### # # ###
|
||||
# # # # # # # # # # # # # #
|
||||
# # ### # # ####### ### # ####### # ####### ### #
|
||||
# # # # # # # # # # # # # #
|
||||
##### # # ##### # # # ####### # # # ### ### # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### ### # # # ######### ### # ##### ### #######
|
||||
# # # # # # # # # # #
|
||||
# # ### ### # ############# ####### ### ####### #
|
||||
# # # # # # # # # # #
|
||||
# ### ####### ##### # # ######### ### ##### # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # # # ### ##### # ### # ### # ### ### #########
|
||||
# # # # # # # # # # # # #
|
||||
### # ### ### ### ### ####### # ##### ### # # # #
|
||||
# # # # # # # # # # # # # #
|
||||
##### ##### ####### # # # ##### # # ####### ### #
|
||||
# # # # # # # # # #
|
||||
# ##### ##### ### ##### ##### ####### # ##### ###
|
||||
# # # # # # # # # # # # # # #
|
||||
# ### ### ####### # # # # # ### # # ### # ### # #
|
||||
# # # # # # # E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v7.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v7.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S# # # # # #
|
||||
# ##### # ##### # ### ####### # # ### # ##### ###
|
||||
# # # # # # # # # # # # # # #
|
||||
# # # ##### ### # # ### ### # ##### # ##### ### #
|
||||
# # # # # # # # # # # # # #
|
||||
##### # ##### ### ### ### # ### ######### # # # #
|
||||
# # # # # # # # # # # # #
|
||||
# ##### # ##### ######### # ##### # # # # ##### #
|
||||
# # # # # # # # # # #
|
||||
### # # ##### # ### ####### # ####### ###########
|
||||
# # # # # # # # # # #
|
||||
# ####### # ##### ####### # ### ### # # ####### #
|
||||
# # # # # # # # # # # # # #
|
||||
### # ######### ### # # ### # ### # ### # ##### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ##### # # # ### # # ####### ### ####### # # # #
|
||||
# # # # # # # # # # # # # #
|
||||
##### ##### # # ####### # ##### ##### # ### # ###
|
||||
# # # # # # # # # # # # # # #
|
||||
# ##### # ### ### # # # # # ### # # # ### # ### #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # # ####### ### # ##### ### # # ##### ### # # #
|
||||
# # # # # # # # # # #
|
||||
# ### ##### ### ### ########### ### # ### ### # #
|
||||
# # # # # # # # # # # # # #
|
||||
### ### # ### ### ##### # # ##### # ### ### # # #
|
||||
# # # # # # # # # # # # # # # # #
|
||||
# # # # ### ### ### # # ##### ##### ### ### ### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### ####### ####### ### # ### # ### ### ### # #
|
||||
# # # # # # # # # #
|
||||
####### # ##### # # ####### ####### ### # # ### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# # # ##### # # ### # # ##### # # ### ### ### ###
|
||||
# # # # # # # # # # # # #
|
||||
# ####### ### ### ### ### # ########### ### ### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### # ##### # ### ### # ### ####### # # # # ###
|
||||
# # # # # # # # # # # # #
|
||||
########### # # # ### # ####### # # # # ####### #
|
||||
# # # # # # # # # # # # #
|
||||
# ### ### # ##### # # ##### ##### # # ### ##### #
|
||||
# # # # # # # # # # # # # #
|
||||
### # ####### # ### ### # ################# ### #
|
||||
# # # # # # # # # # #
|
||||
# ### # # ####### # # ### # ##### # ### ##### ###
|
||||
# # # # # # # E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v8.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v8.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S# # # # # #
|
||||
# # ####### # # ####### ### # # ##### ### # #####
|
||||
# # # # # # # # # # # # #
|
||||
####### ############# # # ##### # # ### ####### #
|
||||
# # # # # # # # # #
|
||||
### # ### ##### ####### # # # # ######### #######
|
||||
# # # # # # # # # # # #
|
||||
# ### # # # # # # # ############# ##### ##### # #
|
||||
# # # # # # # # # # # # # #
|
||||
### ##### # ##### ### ####### # ### # ### # ### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ##### # ##### # # ### ### # # # ### # # ##### #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # ##### # ##### # ### # ##### # ##### # #####
|
||||
# # # # # # # # # # # # # #
|
||||
# # # # # ### # ##### # ######### # # ####### # #
|
||||
# # # # # # # # # # # # #
|
||||
# # ############# # ##### # ### # ##### ### ### #
|
||||
# # # # # # # # # # # #
|
||||
# ##### # ##### # ########### ####### # # ### ###
|
||||
# # # # # # # # # # #
|
||||
# ####### # # ############# # ### ######### # # #
|
||||
# # # # # # # # # # #
|
||||
##### # ### ### ############# # ### # # # ##### #
|
||||
# # # # # # # # # # # # #
|
||||
# ##### # # # ### ##### ### ### ####### # # #####
|
||||
# # # # # # # # # # # # # #
|
||||
### # ### ### # ### # ### # # # # ########### # #
|
||||
# # # # # # # # # # # #
|
||||
# ##### ######### ########### ##### ##### # ### #
|
||||
# # # # # # # # # # #
|
||||
### ### # # ######### ### # ### ##### ##### # # #
|
||||
# # # # # # # # # # # #
|
||||
# ### ########### # ##### ######### # # ####### #
|
||||
# # # # # # # # # #
|
||||
# ##### ### # # ### # ### # ######### ##### ### #
|
||||
# # # # # # # # # # # #
|
||||
######### ########### # ##### # # ##### ##### ###
|
||||
# # # # # # # # # # #
|
||||
### # ##### # # ####### ### ##### # ##### # ### #
|
||||
# # # # # # # # # # # # #
|
||||
# # ### # # # # # ### ############### # # #######
|
||||
# # # # # # # # # # # # # # #
|
||||
# ### ##### # # # # ####### # ### # # ### ##### #
|
||||
# # # # # # # # # # # # #
|
||||
# ##### ##### ##### # ### # ### ########### # # #
|
||||
# # # # # E#
|
||||
#################################################
|
||||
49
skorohodovsa/task_2/source/templates/50x50_deadends_v9.txt
Normal file
49
skorohodovsa/task_2/source/templates/50x50_deadends_v9.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#################################################
|
||||
#S # # # # # # #
|
||||
##### # # ##### # # # # # # ### # # ##### ##### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# ####### # # # # # # ##### # ####### ######### #
|
||||
# # # # # # # # # # # # # #
|
||||
########### ### # ### # # ######### ### # # #####
|
||||
# # # # # # # # # # # #
|
||||
# ### # # # ##### # ### # # ######### ### ##### #
|
||||
# # # # # # # # # # # # # # #
|
||||
# # ##### # # ### ##### # # # ### # # # ##### # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # ####### ### # # ##### # ### ####### ####### #
|
||||
# # # # # # # # # # # # #
|
||||
# ####### ### # ####### # ### ##### ##### # # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # # # # ####### ### # # # ######### # # # # # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# ##### ########### ### # # # # # # ### ##### # #
|
||||
# # # # # # # # # # # # #
|
||||
##### # # ##### ##### ### ##### ##### ##### # ###
|
||||
# # # # # # # # # # #
|
||||
# ### ##### ##### # # # ######### ### # ####### #
|
||||
# # # # # # # # # # # # #
|
||||
# # # # ##### ### ##### # # ####### ### # #######
|
||||
# # # # # # # # # # # # # #
|
||||
# # # ##### ######### ### ### # # # # ### ##### #
|
||||
# # # # # # # # # # # #
|
||||
# ### ############# ### # # # # ####### ####### #
|
||||
# # # # # # # # # # # # #
|
||||
### ### ### ### # # # ### ####### ### # # ### # #
|
||||
# # # # # # # # # # # # # #
|
||||
# ### ### ### ####### # ### # # ### ### ### #####
|
||||
# # # # # # # # # # # # #
|
||||
# ######### ##### # ### # ### ### ####### # ### #
|
||||
# # # # # # # # # # # # #
|
||||
# ### # ### # # # # ### ### ### # # # ###########
|
||||
# # # # # # # # # # # # # #
|
||||
### # # # # ### # ########### # ######### ##### #
|
||||
# # # # # # # # # #
|
||||
# ### # ####### ####################### # # # # #
|
||||
# # # # # # # # # #
|
||||
# ####### ### # # ### ######### # ######### # ###
|
||||
# # # # # # # # # # # # #
|
||||
# # ####### ##### # ### ### ####### # # ####### #
|
||||
# # # # # # # # # # # # # #
|
||||
# ####### ##### # ### # # ### # # # ####### # # #
|
||||
# # # # # # E#
|
||||
#################################################
|
||||
13
skorohodovsa/task_2/source/view/__init__.py
Normal file
13
skorohodovsa/task_2/source/view/__init__.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from source.view.observer import Observer, ConsoleView, Event
|
||||
from source.view.command import Player, Command, MoveCommand, CommandHistory, DIRECTIONS
|
||||
|
||||
__all__ = [
|
||||
"Observer",
|
||||
"ConsoleView",
|
||||
"Event",
|
||||
"Player",
|
||||
"Command",
|
||||
"MoveCommand",
|
||||
"CommandHistory",
|
||||
"DIRECTIONS",
|
||||
]
|
||||
158
skorohodovsa/task_2/source/view/command.py
Normal file
158
skorohodovsa/task_2/source/view/command.py
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Optional
|
||||
|
||||
from source.models.base import Maze, Cell
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------- #
|
||||
# Игрок #
|
||||
# ---------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
class Player:
|
||||
"""Хранит текущее положение игрока в лабиринте.
|
||||
|
||||
Attributes:
|
||||
cell: Текущая клетка игрока.
|
||||
"""
|
||||
|
||||
def __init__(self, cell: Cell) -> None:
|
||||
"""Инициализирует игрока на заданной клетке.
|
||||
|
||||
Args:
|
||||
cell: Начальная клетка игрока.
|
||||
"""
|
||||
self.cell = cell
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Player(x={self.cell.x}, y={self.cell.y})"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------- #
|
||||
# Интерфейс команды #
|
||||
# ---------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
class Command(ABC):
|
||||
"""Интерфейс команды с поддержкой отмены."""
|
||||
|
||||
@abstractmethod
|
||||
def execute(self) -> bool:
|
||||
"""Выполняет команду.
|
||||
|
||||
Returns:
|
||||
True если команда выполнена успешно, False иначе.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def undo(self) -> None:
|
||||
"""Отменяет команду, восстанавливая предыдущее состояние."""
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------- #
|
||||
# Команда перемещения #
|
||||
# ---------------------------------------------------------------------------- #
|
||||
|
||||
DIRECTIONS = {
|
||||
"w": (0, -1),
|
||||
"s": (0, 1),
|
||||
"a": (-1, 0),
|
||||
"d": (1, 0),
|
||||
}
|
||||
|
||||
|
||||
class MoveCommand(Command):
|
||||
"""Перемещает игрока в заданном направлении.
|
||||
|
||||
Сохраняет предыдущую клетку для возможности отмены хода.
|
||||
"""
|
||||
|
||||
def __init__(self, player: Player, direction: str, maze: Maze) -> None:
|
||||
"""Инициализирует команду перемещения.
|
||||
|
||||
Args:
|
||||
player: Объект игрока.
|
||||
direction: Направление ('w', 'a', 's', 'd').
|
||||
maze: Объект лабиринта для проверки проходимости.
|
||||
|
||||
Raises:
|
||||
ValueError: Если направление не распознано.
|
||||
"""
|
||||
if direction not in DIRECTIONS:
|
||||
raise ValueError(
|
||||
f"Неизвестное направление '{direction}'. Используй: w/a/s/d"
|
||||
)
|
||||
|
||||
self._player = player
|
||||
self._direction = direction
|
||||
self._maze = maze
|
||||
self._prev_cell: Optional[Cell] = None
|
||||
|
||||
def execute(self) -> bool:
|
||||
"""Перемещает игрока если целевая клетка проходима.
|
||||
|
||||
Returns:
|
||||
True если перемещение выполнено, False если клетка непроходима.
|
||||
"""
|
||||
dx, dy = DIRECTIONS[self._direction]
|
||||
target = self._maze.get_cell(
|
||||
self._player.cell.x + dx,
|
||||
self._player.cell.y + dy,
|
||||
)
|
||||
|
||||
if target is None or not target.is_possible():
|
||||
return False
|
||||
|
||||
self._prev_cell = self._player.cell
|
||||
self._player.cell = target
|
||||
return True
|
||||
|
||||
def undo(self) -> None:
|
||||
"""Возвращает игрока на предыдущую клетку."""
|
||||
if self._prev_cell is not None:
|
||||
self._player.cell = self._prev_cell
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------- #
|
||||
# История команд #
|
||||
# ---------------------------------------------------------------------------- #
|
||||
|
||||
|
||||
class CommandHistory:
|
||||
"""Хранит историю выполненных команд и позволяет отменять их.
|
||||
|
||||
Example:
|
||||
history = CommandHistory()
|
||||
cmd = MoveCommand(player, 'w', maze)
|
||||
if cmd.execute():
|
||||
history.push(cmd)
|
||||
|
||||
history.undo() # отменяет последний успешный ход
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._history: list[Command] = []
|
||||
|
||||
def push(self, command: Command) -> None:
|
||||
"""Добавляет выполненную команду в историю.
|
||||
|
||||
Args:
|
||||
command: Успешно выполненная команда.
|
||||
"""
|
||||
self._history.append(command)
|
||||
|
||||
def undo(self) -> bool:
|
||||
"""Отменяет последнюю команду из истории.
|
||||
|
||||
Returns:
|
||||
True если отмена выполнена, False если история пуста.
|
||||
"""
|
||||
if not self._history:
|
||||
print("Нечего отменять.")
|
||||
return False
|
||||
self._history.pop().undo()
|
||||
return True
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Очищает историю команд."""
|
||||
self._history.clear()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user