diff --git a/volkovim/__init__.py b/volkovim/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/volkovim/docs/data/Figure_1.png b/volkovim/docs/data/Figure_1.png new file mode 100644 index 0000000..9ad574f Binary files /dev/null and b/volkovim/docs/data/Figure_1.png differ diff --git a/volkovim/docs/data/Figure_2.png b/volkovim/docs/data/Figure_2.png new file mode 100644 index 0000000..a1d9f65 Binary files /dev/null and b/volkovim/docs/data/Figure_2.png differ diff --git a/volkovim/docs/data/Figure_3.png b/volkovim/docs/data/Figure_3.png new file mode 100644 index 0000000..db5b0df Binary files /dev/null and b/volkovim/docs/data/Figure_3.png differ diff --git a/volkovim/docs/data/results.csv b/volkovim/docs/data/results.csv new file mode 100644 index 0000000..c909858 --- /dev/null +++ b/volkovim/docs/data/results.csv @@ -0,0 +1,91 @@ +Structure,Order,Operation,Time +LinkedList,random,insert,0.02272110000012617 +LinkedList,random,find,0.00020389999917824753 +LinkedList,random,delete,0.00012740000056510326 +LinkedList,random,insert,0.022962700000789482 +LinkedList,random,find,0.00019210000027669594 +LinkedList,random,delete,0.00013299999955052044 +LinkedList,random,insert,0.023047099999530474 +LinkedList,random,find,0.000189200000022538 +LinkedList,random,delete,0.00013699999908567406 +LinkedList,random,insert,0.0228056000014476 +LinkedList,random,find,0.00019530000099621248 +LinkedList,random,delete,0.0001320000010309741 +LinkedList,random,insert,0.022504299999127397 +LinkedList,random,find,0.0001827000014600344 +LinkedList,random,delete,0.00012650000098801684 +HashTable,random,insert,0.006552200000442099 +HashTable,random,find,6.84000006003771e-05 +HashTable,random,delete,4.3999998524668626e-05 +HashTable,random,insert,0.006576800000402727 +HashTable,random,find,6.790000043110922e-05 +HashTable,random,delete,4.459999945538584e-05 +HashTable,random,insert,0.006557500000781147 +HashTable,random,find,6.779999966965988e-05 +HashTable,random,delete,4.2499999835854396e-05 +HashTable,random,insert,0.006657900001300732 +HashTable,random,find,6.579999899258837e-05 +HashTable,random,delete,3.990000004705507e-05 +HashTable,random,insert,0.0066283999985898845 +HashTable,random,find,6.589999975403771e-05 +HashTable,random,delete,4.270000135875307e-05 +BST,random,insert,0.005961099999694852 +BST,random,find,5.790000068373047e-05 +BST,random,delete,5.5900000006658956e-05 +BST,random,insert,0.005897299999560346 +BST,random,find,5.229999987932388e-05 +BST,random,delete,5.080000119050965e-05 +BST,random,insert,0.005889399999432499 +BST,random,find,5.32000012753997e-05 +BST,random,delete,4.749999970954377e-05 +BST,random,insert,0.006325000000288128 +BST,random,find,5.310000051395036e-05 +BST,random,delete,5.1900000471505336e-05 +BST,random,insert,0.00589380000019446 +BST,random,find,5.149999924469739e-05 +BST,random,delete,5.140000030223746e-05 +LinkedList,sorted,insert,0.021485899998879177 +LinkedList,sorted,find,0.0001861000000644708 +LinkedList,sorted,delete,0.00012830000014218967 +LinkedList,sorted,insert,0.020360300000902498 +LinkedList,sorted,find,0.00017280000065511558 +LinkedList,sorted,delete,0.00010759999895526562 +LinkedList,sorted,insert,0.02137589999983902 +LinkedList,sorted,find,0.00017079999997804407 +LinkedList,sorted,delete,8.949999937613029e-05 +LinkedList,sorted,insert,0.019899599999916973 +LinkedList,sorted,find,0.0001748000013321871 +LinkedList,sorted,delete,0.00011620000077527948 +LinkedList,sorted,insert,0.019986600000265753 +LinkedList,sorted,find,0.0001812999998946907 +LinkedList,sorted,delete,0.00011749999976018444 +HashTable,sorted,insert,0.005906399999730638 +HashTable,sorted,find,6.240000038815197e-05 +HashTable,sorted,delete,3.930000093532726e-05 +HashTable,sorted,insert,0.005912500000704313 +HashTable,sorted,find,6.050000047252979e-05 +HashTable,sorted,delete,3.8800000766059384e-05 +HashTable,sorted,insert,0.005913900000450667 +HashTable,sorted,find,6.050000047252979e-05 +HashTable,sorted,delete,3.96999985241564e-05 +HashTable,sorted,insert,0.005919999999605352 +HashTable,sorted,find,6.190000021888409e-05 +HashTable,sorted,delete,3.749999996216502e-05 +HashTable,sorted,insert,0.005881000000954373 +HashTable,sorted,find,6.089999988034833e-05 +HashTable,sorted,delete,3.7900001188972965e-05 +BST,sorted,insert,0.033791699999710545 +BST,sorted,find,0.000340900000082911 +BST,sorted,delete,0.00026059999981953297 +BST,sorted,insert,0.03474140000071202 +BST,sorted,find,0.0003206999990652548 +BST,sorted,delete,0.0002024000004894333 +BST,sorted,insert,0.03431230000023788 +BST,sorted,find,0.0003130000004603062 +BST,sorted,delete,0.00025209999876096845 +BST,sorted,insert,0.03444429999944987 +BST,sorted,find,0.0003271999994467478 +BST,sorted,delete,0.00017320000006293412 +BST,sorted,insert,0.03425440000137314 +BST,sorted,find,0.0003316999991511693 +BST,sorted,delete,0.00022639999951934442 diff --git a/volkovim/docs/report_1.docx b/volkovim/docs/report_1.docx new file mode 100644 index 0000000..08a385e Binary files /dev/null and b/volkovim/docs/report_1.docx differ diff --git a/volkovim/task1/__init__.py b/volkovim/task1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/volkovim/task1/histograms.py b/volkovim/task1/histograms.py new file mode 100644 index 0000000..7ae1594 --- /dev/null +++ b/volkovim/task1/histograms.py @@ -0,0 +1,45 @@ +import csv +import matplotlib.pyplot as plt +from collections import defaultdict + + +data = defaultdict(list) + +with open("results.csv", "r") as f: + reader = csv.DictReader(f) + + for row in reader: + key = (row["Structure"], row["Order"], row["Operation"]) + data[key].append(float(row["Time"])) + + +def avg(key): + return sum(data[key]) / len(data[key]) + + +structures = ["LinkedList", "HashTable", "BST"] +orders = ["random", "sorted"] +operations = ["insert", "find", "delete"] + + +for op in operations: + plt.figure() + + labels = [] + values = [] + + for struct in structures: + for order in orders: + key = (struct, order, op) + labels.append(f"{struct}\n{order}") + values.append(avg(key)) + + plt.bar(labels, values) + + plt.title(f"{op} (Histogram)") + plt.ylabel("Time (sec)") + plt.xticks(rotation=30) + plt.grid(axis="y") + + plt.tight_layout() + plt.show() \ No newline at end of file diff --git a/volkovim/task1/main.py b/volkovim/task1/main.py new file mode 100644 index 0000000..163dbe5 --- /dev/null +++ b/volkovim/task1/main.py @@ -0,0 +1,66 @@ +import csv + +from structures.LinkedList import ll_insert, ll_find, ll_delete +from structures.HashTable import ht_insert, ht_find, ht_delete +from structures.BinaryTree import bst_insert, bst_find, bst_delete + +from util.randomNames import generate_test_data +from util.timeTester import run_test + + +def main(): + structures = [ + ("LinkedList", ll_insert, ll_find, ll_delete), + ("HashTable", ht_insert, ht_find, ht_delete), + ("BST", bst_insert, bst_find, bst_delete) + ] + + modes = [ + ("random", False), + ("sorted", True) + ] + + results = [] + + N = 10000 + REPEATS = 5 + + for mode_name, is_sorted in modes: + base_records = generate_test_data(N, is_sorted) + + for struct_name, ins, fnd, dele in structures: + for i in range(REPEATS): + + stats = run_test(base_records, ins, fnd, dele) + + results.append([ + struct_name, + mode_name, + "insert", + stats["insert"] + ]) + + results.append([ + struct_name, + mode_name, + "find", + stats["find"] + ]) + + results.append([ + struct_name, + mode_name, + "delete", + stats["delete"] + ]) + + with open("results.csv", "w", newline="") as f: + writer = csv.writer(f) + writer.writerow(["Structure", "Order", "Operation", "Time"]) + writer.writerows(results) + + print("Готово: записано", len(results), "строк") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/volkovim/task1/structures/BinaryTree.py b/volkovim/task1/structures/BinaryTree.py new file mode 100644 index 0000000..b98af00 --- /dev/null +++ b/volkovim/task1/structures/BinaryTree.py @@ -0,0 +1,83 @@ +def bst_insert(root: dict | None, name: str, phone: str) -> dict: + new_node = {'name': name, 'phone': phone, 'left': None, 'right': None} + + if root is None: + return new_node + + current = root + + while True: + if name == current['name']: + current['phone'] = phone + return root + + if name < current['name']: + if current['left'] is None: + current['left'] = new_node + return root + current = current['left'] + else: + if current['right'] is None: + current['right'] = new_node + return root + current = current['right'] + + +def bst_find(root: dict | None, name: str) -> str | None: + current = root + + while current is not None: + if name == current['name']: + return current['phone'] + + if name < current['name']: + current = current['left'] + else: + current = current['right'] + + return None + + +def bst_min(node: dict) -> dict: + while node['left'] is not None: + node = node['left'] + return node + + +def bst_delete(root: dict | None, name: str) -> dict | None: + if root is None: + return None + + if name < root['name']: + root['left'] = bst_delete(root['left'], name) + + elif name > root['name']: + root['right'] = bst_delete(root['right'], name) + + else: + if root['left'] is None: + return root['right'] + + if root['right'] is None: + return root['left'] + + successor = bst_min(root['right']) + root['name'] = successor['name'] + root['phone'] = successor['phone'] + root['right'] = bst_delete(root['right'], successor['name']) + + return root + + +def bst_list_all(root: dict | None) -> list: + result = [] + + def inorder(node): + if node is None: + return + inorder(node['left']) + result.append((node['name'], node['phone'])) + inorder(node['right']) + + inorder(root) + return result \ No newline at end of file diff --git a/volkovim/task1/structures/HashTable.py b/volkovim/task1/structures/HashTable.py new file mode 100644 index 0000000..dd22790 --- /dev/null +++ b/volkovim/task1/structures/HashTable.py @@ -0,0 +1,51 @@ +from structures.LinkedList import * + + +def hash_func(name: str, size: int) -> int: + total = 0 + + for i, ch in enumerate(name): + total += ord(ch) * (i + 1) + + return total % size + + +def ht_insert(buckets: list | None, name: str, phone: str, size: int = 50) -> list: + if buckets is None: + buckets = [None] * size + + index = hash_func(name, len(buckets)) + buckets[index] = ll_insert(buckets[index], name, phone) + + return buckets + + +def ht_find(buckets: list | None, name: str) -> str | None: + if not buckets: + return None + + index = hash_func(name, len(buckets)) + return ll_find(buckets[index], name) + + +def ht_delete(buckets: list | None, name: str) -> list | None: + if not buckets: + return buckets + + index = hash_func(name, len(buckets)) + buckets[index] = ll_delete(buckets[index], name) + + return buckets + + +def ht_list_all(buckets: list | None) -> list: + if not buckets: + return [] + + result = [] + + for bucket in buckets: + if bucket is not None: + result.extend(ll_list_all(bucket)) + + return sorted(result, key=lambda x: x[0]) diff --git a/volkovim/task1/structures/LinkedList.py b/volkovim/task1/structures/LinkedList.py new file mode 100644 index 0000000..091bae4 --- /dev/null +++ b/volkovim/task1/structures/LinkedList.py @@ -0,0 +1,60 @@ +def ll_insert(head: dict | None, name: str, phone: str) -> dict: + if head is None: + return {'name': name, 'phone': phone, 'next': None} + + current = head + + while current is not None: + if current['name'] == name: + current['phone'] = phone + return head + + if current['next'] is None: + break + + current = current['next'] + + current['next'] = {'name': name, 'phone': phone, 'next': None} + return head + + +def ll_find(head: dict | None, name: str) -> str | None: + current = head + + while current is not None: + if current['name'] == name: + return current['phone'] + current = current['next'] + + return None + + +def ll_delete(head: dict | None, name: str) -> dict | None: + if head is None: + return None + + if head['name'] == name: + return head['next'] + + current = head + + while current['next'] is not None: + if current['next']['name'] == name: + current['next'] = current['next']['next'] + return head + + current = current['next'] + + return head + + +def ll_list_all(head: dict | None) -> list: + records = [] + current = head + + while current is not None: + records.append((current['name'], current['phone'])) + current = current['next'] + + records.sort(key=lambda item: item[0]) + return records diff --git a/volkovim/task1/structures/__init__.py b/volkovim/task1/structures/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/volkovim/task1/util/__init__.py b/volkovim/task1/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/volkovim/task1/util/randomNames.py b/volkovim/task1/util/randomNames.py new file mode 100644 index 0000000..79ff6d3 --- /dev/null +++ b/volkovim/task1/util/randomNames.py @@ -0,0 +1,44 @@ +import random + + +names_pool = ( + "Иван", "Александр", "Михаил", "Дмитрий", "Сергей", "Андрей", "Алексей", "Николай", "Владимир", "Евгений", +"Павел", "Илья", "Роман", "Артём", "Константин", "Виктор", "Георгий", "Максим", "Василий", "Олег", +"Юрий", "Валерий", "Антон", "Кирилл", "Степан", "Денис", "Тимур", "Григорий", "Леонид", "Фёдор", +"Никита", "Ярослав", "Руслан", "Богдан", "Станислав", "Вячеслав", "Арсений", "Егор", "Захар", "Даниил", +"Матвей", "Тимофей", "Пётр", "Лев", "Семён", "Платон", "Давид", "Глеб", "Родион", "Святослав", +"Владислав", "Елисей", "Анатолий", "Ростислав", "Всеволод", "Мирослав", "Игнат", "Клим", "Евстафий", "Аркадий", +"Игорь", "Лука", "Филипп", "Альберт", "Эдуард", "Роберт", "Артур", "Карл", "Борис", "Вадим", +"Эмиль", "Оскар", "Геннадий", "Марк", "Виталий", "Назар", "Мирон", "Савелий", "Фома", "Еремей", +"Нестор", "Прохор", "Авраам", "Севастьян", "Евдоким", "Трофим", "Кузьма", "Фадей", "Наум", "Аким", +"Лаврентий", "Ипполит", "Панкрат", "Афанасий", "Евдокия", "Прасковья", "Марфа", "Агафья", "Ульяна", "Пелагея", +"Дарья", "Анастасия", "Екатерина", "Ольга", "Татьяна", "Светлана", "Наталья", "Ирина", "Вера", "Людмила", +"Галина", "Любовь", "Надежда", "Анна", "Мария", "Елена", "Юлия", "Ксения", "Полина", "Виктория", +"Алина", "Дарина", "Валерия", "Софья", "Вероника", "Арина", "Кира", "Милана", "Алиса", "Ева", +"Агата", "Злата", "Яна", "Василиса", "Стефания", "Диана", "Карина", "Лидия", "Алла", "Раиса" +) + +non_existent_names = ( + "Ноль", "Целковый", "Полушка", "Четвертушка", "Осьмушка" +) + + +def generate_phone(): + return str(random.randint(10000000000, 99999999999)) + + +def generate_test_data(n=10000, sorted_data=False): + records = [(random.choice(names_pool), generate_phone()) for _ in range(n)] + + if sorted_data: + records.sort(key=lambda x: x[0]) + + return records + + +def generate_find_set(): + return random.sample(names_pool, 100) + list(non_existent_names) + + +def generate_delete_set(): + return random.sample(names_pool, 50) \ No newline at end of file diff --git a/volkovim/task1/util/timeTester.py b/volkovim/task1/util/timeTester.py new file mode 100644 index 0000000..3a321b0 --- /dev/null +++ b/volkovim/task1/util/timeTester.py @@ -0,0 +1,30 @@ +import time +import random +from util.randomNames import names_pool, generate_find_set + + +def run_test(records, insert_func, find_func, delete_func): + structure = None + + start = time.perf_counter() + for name, phone in records: + structure = insert_func(structure, name, phone) + insert_time = time.perf_counter() - start + + find_set = generate_find_set() + + start = time.perf_counter() + for name in find_set: + find_func(structure, name) + find_time = time.perf_counter() - start + + start = time.perf_counter() + for name in random.sample(names_pool, 50): + structure = delete_func(structure, name) + delete_time = time.perf_counter() - start + + return { + "insert": insert_time, + "find": find_time, + "delete": delete_time + } \ No newline at end of file diff --git a/volkovim/task2/builders/maze_builder.py b/volkovim/task2/builders/maze_builder.py new file mode 100644 index 0000000..b5894f4 --- /dev/null +++ b/volkovim/task2/builders/maze_builder.py @@ -0,0 +1,8 @@ +from abc import ABC, abstractmethod + + +class MazeBuilder(ABC): + + @abstractmethod + def buildFromFile(self, filename): + pass \ No newline at end of file diff --git a/volkovim/task2/builders/text_file_builder.py b/volkovim/task2/builders/text_file_builder.py new file mode 100644 index 0000000..91f6823 --- /dev/null +++ b/volkovim/task2/builders/text_file_builder.py @@ -0,0 +1,102 @@ +from builders.maze_builder import MazeBuilder +from core.cell import Cell +from core.maze import Maze + + +class TextFileMazeBuilder(MazeBuilder): + + def buildFromFile(self, filename): + + with open(filename, "r", encoding="utf-8") as source: + raw_lines = [line.rstrip("\n") for line in source] + + if not raw_lines: + raise ValueError("Maze file is empty") + + expected_width = len(raw_lines[0]) + + blueprint = [] + start_cell = None + exit_cell = None + + for y_index, raw in enumerate(raw_lines): + + if len(raw) != expected_width: + raise ValueError( + f"Broken maze shape at line {y_index + 1}" + ) + + row_pack = [] + + for x_index, symbol in enumerate(raw): + + current = None + + if symbol == "#": + current = Cell( + x_index, + y_index, + isWall=True + ) + + elif symbol == " ": + current = Cell( + x_index, + y_index + ) + + elif symbol == "S": + + if start_cell: + raise ValueError( + "Multiple start cells detected" + ) + + current = Cell( + x_index, + y_index, + isStart=True + ) + + start_cell = current + + elif symbol == "E": + + if exit_cell: + raise ValueError( + "Multiple exit cells detected" + ) + + current = Cell( + x_index, + y_index, + isExit=True + ) + + exit_cell = current + + else: + raise ValueError( + f"Unsupported symbol '{symbol}' " + f"at ({x_index}, {y_index})" + ) + + row_pack.append(current) + + blueprint.append(row_pack) + + if start_cell is None: + raise ValueError( + "Start cell S not found" + ) + + if exit_cell is None: + raise ValueError( + "Exit cell E not found" + ) + + return Maze( + blueprint, + start_cell=start_cell, + exit_cell=exit_cell + ) \ No newline at end of file diff --git a/volkovim/task2/command/command.py b/volkovim/task2/command/command.py new file mode 100644 index 0000000..9dbbf3f --- /dev/null +++ b/volkovim/task2/command/command.py @@ -0,0 +1,12 @@ +from abc import ABC, abstractmethod + + +class Command(ABC): + + @abstractmethod + def execute(self): + pass + + @abstractmethod + def undo(self): + pass \ No newline at end of file diff --git a/volkovim/task2/command/move_command.py b/volkovim/task2/command/move_command.py new file mode 100644 index 0000000..6f96288 --- /dev/null +++ b/volkovim/task2/command/move_command.py @@ -0,0 +1,65 @@ +from command.command import Command + + +class MoveCommand(Command): + + def __init__( + self, + player, + maze, + direction + ): + self.player = player + self.maze = maze + self.direction = direction + self.previous = None + + def _targetCell(self): + + offsets = { + "W": (0, -1), + "S": (0, 1), + "A": (-1, 0), + "D": (1, 0) + } + + dx, dy = offsets.get( + self.direction.upper(), + (0, 0) + ) + + x, y = self.player.getPosition() + + return self.maze.getCell( + x + dx, + y + dy + ) + + def execute(self): + + destination = self._targetCell() + + if destination is None: + return False + + if not destination.isPassable(): + return False + + self.previous = self.player.current + + self.player.place( + destination + ) + + return True + + def undo(self): + + if self.previous is None: + return False + + self.player.place( + self.previous + ) + + return True \ No newline at end of file diff --git a/volkovim/task2/command/player.py b/volkovim/task2/command/player.py new file mode 100644 index 0000000..9f8249b --- /dev/null +++ b/volkovim/task2/command/player.py @@ -0,0 +1,13 @@ +class Player: + def __init__(self, start_cell): + self.current = start_cell + + def place(self, cell): + self.current = cell + + def getPosition(self): + return self.current.getPosition() + + def __str__(self): + x, y = self.getPosition() + return f"Player({x}, {y})" \ No newline at end of file diff --git a/volkovim/task2/core/cell.py b/volkovim/task2/core/cell.py new file mode 100644 index 0000000..d642eda --- /dev/null +++ b/volkovim/task2/core/cell.py @@ -0,0 +1,40 @@ +class Cell: + def __init__( + self, + x: int, + y: int, + isWall: bool = False, + isStart: bool = False, + isExit: bool = False + ): + self.x = x + self.y = y + self.isWall = isWall + self.isStart = isStart + self.isExit = isExit + + def isPassable(self) -> bool: + return self.isWall is False + + def getPosition(self): + return self.x, self.y + + def __str__(self): + if self.isStart: + return "S" + + if self.isExit: + return "E" + + if self.isWall: + return "#" + + return " " + + def __repr__(self): + return ( + f"Cell(x={self.x}, y={self.y}, " + f"wall={self.isWall}, " + f"start={self.isStart}, " + f"exit={self.isExit})" + ) \ No newline at end of file diff --git a/volkovim/task2/core/maze.py b/volkovim/task2/core/maze.py new file mode 100644 index 0000000..6a60931 --- /dev/null +++ b/volkovim/task2/core/maze.py @@ -0,0 +1,59 @@ +from core.cell import Cell + + +class Maze: + def __init__(self, cells_map, start_cell=None, exit_cell=None): + self.cells = cells_map + self.start = start_cell + self.exit = exit_cell + + self.height = len(cells_map) + self.width = len(cells_map[0]) if self.height else 0 + + def getCell(self, x: int, y: int): + if y < 0 or y >= self.height: + return None + + if x < 0 or x >= self.width: + return None + + return self.cells[y][x] + + def getNeighbors(self, current: Cell): + reachable = [] + + top = self.getCell(current.x, current.y - 1) + right = self.getCell(current.x + 1, current.y) + bottom = self.getCell(current.x, current.y + 1) + left = self.getCell(current.x - 1, current.y) + + for candidate in (top, right, bottom, left): + if candidate is None: + continue + + if candidate.isPassable(): + reachable.append(candidate) + + return reachable + + def hasStart(self): + return self.start is not None + + def hasExit(self): + return self.exit is not None + + def size(self): + return self.width, self.height + + def __str__(self): + rows = [] + + for line in self.cells: + visual = "" + + for cell in line: + visual += str(cell) + + rows.append(visual) + + return "\n".join(rows) \ No newline at end of file diff --git a/volkovim/task2/core/search_stats.py b/volkovim/task2/core/search_stats.py new file mode 100644 index 0000000..9407acb --- /dev/null +++ b/volkovim/task2/core/search_stats.py @@ -0,0 +1,22 @@ +class SearchStats: + def __init__( + self, + strategy_name, + elapsed_ms, + visited_cells, + path_length + ): + self.strategy_name = strategy_name + self.elapsed_ms = elapsed_ms + self.visited_cells = visited_cells + self.path_length = path_length + + def __str__(self): + lines = [ + f"Strategy: {self.strategy_name}", + f"Time: {self.elapsed_ms:.3f} ms", + f"Visited: {self.visited_cells}", + f"Path length: {self.path_length}" + ] + + return "\n".join(lines) \ No newline at end of file diff --git a/volkovim/task2/experiments/benchmark.py b/volkovim/task2/experiments/benchmark.py new file mode 100644 index 0000000..e563600 --- /dev/null +++ b/volkovim/task2/experiments/benchmark.py @@ -0,0 +1,93 @@ +import csv + +from solver.maze_solver import MazeSolver + + +class BenchmarkRunner: + + def __init__( + self, + maze, + strategies, + cycles=5 + ): + self.maze = maze + self.strategies = strategies + self.cycles = cycles + + def launch(self): + + report = [] + + for strategy in self.strategies: + + solver = MazeSolver( + self.maze, + strategy + ) + + total_time = 0 + total_visited = 0 + total_path = 0 + + for _ in range(self.cycles): + + _, stats = solver.solve() + + total_time += stats.time_ms + total_visited += stats.visited_cells + total_path += stats.path_length + + report.append( + { + "maze": "", + "strategy": + strategy.__class__.__name__, + "time_ms": + round( + total_time / self.cycles, + 4 + ), + "visited_cells": + round( + total_visited / self.cycles, + 2 + ), + "path_length": + round( + total_path / self.cycles, + 2 + ) + } + ) + + return report + + def exportCSV( + self, + filename, + results + ): + + with open( + filename, + "w", + newline="", + encoding="utf-8" + ) as file: + + writer = csv.DictWriter( + file, + fieldnames=[ + "maze", + "strategy", + "time_ms", + "visited_cells", + "path_length" + ] + ) + + writer.writeheader() + + for row in results: + writer.writerow(row) \ No newline at end of file diff --git a/volkovim/task2/experiments/plots.py b/volkovim/task2/experiments/plots.py new file mode 100644 index 0000000..b0da4d2 --- /dev/null +++ b/volkovim/task2/experiments/plots.py @@ -0,0 +1,161 @@ +import csv +import matplotlib.pyplot as plt + + +class ChartBuilder: + + def __init__( + self, + csv_file + ): + self.csv_file = csv_file + + def _read(self): + + rows = [] + + with open( + self.csv_file, + "r", + encoding="utf-8" + ) as file: + + reader = csv.DictReader(file) + + for row in reader: + rows.append(row) + + return rows + + def buildTimeChart(self): + + rows = self._read() + + labels = [] + values = [] + + for row in rows: + + labels.append( + f"{row['maze']}\n" + f"{row['strategy']}" + ) + + values.append( + float( + row["time_ms"] + ) + ) + + plt.figure() + + plt.bar( + labels, + values + ) + + plt.title( + "Search Time" + ) + + plt.ylabel( + "Milliseconds" + ) + + plt.xticks( + rotation=45 + ) + + plt.tight_layout() + + plt.show() + + def buildVisitedChart(self): + + rows = self._read() + + labels = [] + values = [] + + for row in rows: + + labels.append( + f"{row['maze']}\n" + f"{row['strategy']}" + ) + + values.append( + float( + row[ + "visited_cells" + ] + ) + ) + + plt.figure() + + plt.bar( + labels, + values + ) + + plt.title( + "Visited Cells" + ) + + plt.ylabel( + "Cells" + ) + + plt.xticks( + rotation=45 + ) + + plt.tight_layout() + + plt.show() + + def buildPathChart(self): + + rows = self._read() + + labels = [] + values = [] + + for row in rows: + + labels.append( + f"{row['maze']}\n" + f"{row['strategy']}" + ) + + values.append( + float( + row[ + "path_length" + ] + ) + ) + + plt.figure() + + plt.bar( + labels, + values + ) + + plt.title( + "Path Length" + ) + + plt.ylabel( + "Cells" + ) + + plt.xticks( + rotation=45 + ) + + plt.tight_layout() + + plt.show() \ No newline at end of file diff --git a/volkovim/task2/experiments/results.csv b/volkovim/task2/experiments/results.csv new file mode 100644 index 0000000..230672b --- /dev/null +++ b/volkovim/task2/experiments/results.csv @@ -0,0 +1,16 @@ +maze,strategy,time_ms,visited_cells,path_length +small.txt,BFSStrategy,0.0676,53.0,23.0 +small.txt,DFSStrategy,0.0527,31.0,31.0 +small.txt,AStarStrategy,0.0682,46.0,23.0 +medium.txt,BFSStrategy,0.7144,717.0,431.0 +medium.txt,DFSStrategy,0.6878,737.0,431.0 +medium.txt,AStarStrategy,0.6968,591.0,431.0 +large.txt,BFSStrategy,2.103,2491.0,1171.0 +large.txt,DFSStrategy,2.7719,3019.0,1243.0 +large.txt,AStarStrategy,2.5953,1995.0,1171.0 +empty.txt,BFSStrategy,0.027,19.0,8.0 +empty.txt,DFSStrategy,0.0115,8.0,8.0 +empty.txt,AStarStrategy,0.0151,8.0,8.0 +blocked.txt,BFSStrategy,0.002,1.0,0.0 +blocked.txt,DFSStrategy,0.0013,1.0,0.0 +blocked.txt,AStarStrategy,0.0019,1.0,0.0 diff --git a/volkovim/task2/main.py b/volkovim/task2/main.py new file mode 100644 index 0000000..8f7bb39 --- /dev/null +++ b/volkovim/task2/main.py @@ -0,0 +1,70 @@ +from builders.text_file_builder import TextFileMazeBuilder + +from strategies.bfs import BFSStrategy +from strategies.dfs import DFSStrategy +from strategies.astar import AStarStrategy + +from experiments.benchmark import BenchmarkRunner +from experiments.plots import ChartBuilder + + +builder = TextFileMazeBuilder() + +maze_files = [ + "small.txt", + "medium.txt", + "large.txt", + "empty.txt", + "blocked.txt" +] + +all_results = [] + +for maze_file in maze_files: + + print() + print("Loading:", maze_file) + + maze = builder.buildFromFile( + f"mazes/{maze_file}" + ) + + runner = BenchmarkRunner( + maze, + [ + BFSStrategy(), + DFSStrategy(), + AStarStrategy() + ], + cycles=10 + ) + + results = runner.launch() + + for row in results: + row["maze"] = maze_file + + all_results.extend(results) + +runner.exportCSV( + "experiments/results.csv", + all_results +) + +print() +print("CSV created") + +charts = ChartBuilder( + "experiments/results.csv" +) + +print("Time chart...") +charts.buildTimeChart() + +print("Visited chart...") +charts.buildVisitedChart() + +print("Path chart...") +charts.buildPathChart() + +print("Done") \ No newline at end of file diff --git a/volkovim/task2/mazes/blocked.txt b/volkovim/task2/mazes/blocked.txt new file mode 100644 index 0000000..179e497 --- /dev/null +++ b/volkovim/task2/mazes/blocked.txt @@ -0,0 +1,3 @@ +########## +#S#####E## +########## \ No newline at end of file diff --git a/volkovim/task2/mazes/empty.txt b/volkovim/task2/mazes/empty.txt new file mode 100644 index 0000000..d502d43 --- /dev/null +++ b/volkovim/task2/mazes/empty.txt @@ -0,0 +1,5 @@ +########## +#S E# +# # +# # +########## \ No newline at end of file diff --git a/volkovim/task2/mazes/large.txt b/volkovim/task2/mazes/large.txt new file mode 100644 index 0000000..0ce5102 --- /dev/null +++ b/volkovim/task2/mazes/large.txt @@ -0,0 +1,100 @@ +S # # # # # # # # # # # # # # + # # ####### ### # ##### # # # # # # # # ### # # ### # # ### # ##### # ##### ### ### ### ######### # + # # # # # # # # # # # # # # # # # # # # # # # # # # + # ##### ##### ### ### ##### ##### ####### # ##### ####### # # # ####### ##### # ######### ### ### # + # # # # # # # # # # # # # # # # # # # # # # # # # + # # ##### # # ##### # # ####### # ### ##### # # # # ### ### ######### ##### # ##### # # ### ### # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ##### # # # ### ### ####### # # ### ######### ### ### ### ### # # # # # ### ##### # # ### # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # + ### # ### # ##### ##### ######### ######### # ##### ### # ####### # ##### ### ####### # ### # ### # + # # # # # # # # # # # # # # # # # # # # # + # ################# # ### # # # # # ########### ############# # ##### ##### ##### # ##### ### # ### + # # # # # # # # # # # # # # # # # # # # # # + ### # ### # ### ####### # ##### ######### ### # ### ### # ####### # ### ####### ##### # ### # # # # + # # # # # # # # # # # # # # # # # # # # # # # + ######### ### ### # ### # ####### ##### # # # ####### ##### ####### # ########### ####### ######### + # # # # # # # # # # # # # # # # # # # # # +## # ####### ### ##### # ####### ### # # # # ##### ### ########### # # # ### # ##### # # ### ##### # + # # # # # # # # # # # # # # # # # # # # # # # # # + ### # # ########### ######### # ### # # ### ### ####### ### # # # # ##### # ### ##### # # ### ### # + # # # # # # # # # # # # # # # # # # # # # # # # # # # +## ##### # # # # # ### # ### ##### # # # ##### # ### # ### # # # # # # # ##### ### ####### # ### ### + # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ##### ### # # # # # ######### ### ### ### # ##### ##### ### # ##### # ######### ############# ### # + # # # # # # # # # # # # # # # # # # # # # # # # + # ##### # # ### ### ##### # ### ### ##### # # ### # ##### ##### ### ### # ##### ### # # ### # # ### + # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # ####### ####### ### ### ######### ### # # ##### # ### ########### ### # ### ####### ### # # # + # # # # # # # # # # # # # # # # # # # # # # + ################### # # # # ### # # # ######### # # ### ############### # ### ##### ### ### ##### # + # # # # # # # # # # # # # # # # # # # # # + ##### # # ### # # ### # ####### # # ##### # # ### ##### # ####### # # # ######### # ######### ##### + # # # # # # # # # # # # # # # # # # # # # # # # # # # # +#### # # ### # # ### # ### ####### ##### # # ### # # ##### # # ####### # # # ##### ### # # ####### # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +## # # # # ### ### # ### ### # # # ### # ####### ### # ########### # ##### ##### # # ### ### ##### # + # # # # # # # # # # # # # # # # # # # # # # # # # # + ########### # # ########### ### # # # # # ### # # ### # ####### # ######### # # ### # ######### ### + # # # # # # # # # # # # # # # # # # # # # # # # +######## # # # ### # ########### # # ####### ### # # # ####### ### # # # ### # ### ######### # # # # + # # # # # # # # # # # # # # # # # # # # # # # + ##### ##### ### # # # ####### ### ### # # # # ############# # ##### # ### ##### ######### ### ### # + # # # # # # # # # # # # # # # # # # # # # # # # # + ### ### # ### # ##### # # # ### # # # ### # ### ### # # # # ########### ####### # ##### ####### ### + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # +## ####### # ##### # ### ### # # # # # # # ####### ### # # # # # # # # ### # # ##### # # # # ##### # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ### # # # ##### ####### # ### # ### ### ####### # # ### ####### ### ####### # ### ####### # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # + # ##### ##### # # ### ### # ######### ### ### ### ### ####### ##### # ######### ##### # ### # ### # + # # # # # # # # # # # # # # # # # # # # # # # # # + # # ####### # ##### ### # # # # # ### ### # ### ### ######### # ####### # ####### ### ######### ### + # # # # # # # # # # # # # # # # # # # # # # # # # # # + ######### # ### ### # # # # # # ####### ##### ### # # ##### # ### # # ####### # # # ### # # # # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # +## # # # ### ##### # ######### # # ####### ### ### # ##### ######### # # # ####### ### # # # # ### # + # # # # # # # # # # # # # # # # # # # # # # # + ### ######### ####### # # ######### # # ### ### ### ##### ### # ########### ######### # # # ### ### + # # # # # # # # # # # # # # # # # # # # # # # # +## ######### # ### # ####### ### # ### ### # # ####### # ### ##### ### # # ### # ### ##### # # ### # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # ### ### ### # # # ##### # ### # ### ### # # # ####### ##### ### ### # ##### # ##### # ### # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # ### ### # ### # ######### # ######### # ### # # # ### ##### ### # ### # # ### # ##### ##### # + # # # # # # # # # # # # # # # # # # # # # # # # # # + ##### # ### ##### ####### ### # ##### # # # ### ### # ##### # # ### # ####### ##### # # # ### # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # + ### # ### ######### # ##### ### # ### # # # ############# ### ### # ##### # ### ######### # ### # # + # # # # # # # # # # # # # # # # # # # # # # # # +## # # # ### ##### # ##### # ### ### # # # ### # # ######### # # ##### ### ####### ### # ######### # + # # # # # # # # # # # # # # # # # # # # # # # # # # # + # ############### ### ####### ### # ### ####### # # # # ######### # ### # # # ##### ##### # # ##### + # # # # # # # # # # # # # # # # # # # # # # + ##### # # ### # ### ######### # ######### # # ### # # ####### # ########### # # # ##### ####### # # + # # # # # # # # # # # # # # # # # # # # # # # +## # ####### ### # # # ############# ### ### ### ### ##### # # ### ########### ##### # ### ### # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ### ### # # # ##### ### # # ##### ####### # # ### ### # # ##### # # ####### ##### # # # # # ##### # + # # # # # # # # # # # # # # # # # # # # # # # # # # # +###### ##### ### # ### ### # # ########### # # # ##### # ### ##### # # ### ######### ### ##### # ### + # # # # # # # # # # # # # # # # # # # # # # # + # # # # # ### ####### ######### # # ######### ### # ##### ##### ##### ### # # # # # # ### ####### # + # # # # # # # # # # # # # # # # # # # # # # # # +#### # ##### # # # ##### ### # ### # ##### ### # ##### ### ######### ### ### ########### # # ##### # + # # # # # # # # # # # # # # # # # # # # # # # # # # # + ######### ##### # ####### # # # ##### # ### # # # # ####### ##### # # ### ### # # # # ### # # ### # + # # # # # # # # # # # # # # # # # # # # # # # # # # + # ### # ##### # # # ############### ### ######### ### # ##### # # ######### # ### # ### # # # # ### + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # + # # # ####### # ### # ##### ##### # # ### # ### # # ##### # ### # # # ####### # ##### # # # ### # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # # + ##### # ### # # # ##### ##### # # # ### ### # # # # ######### # ########### # # ##### ####### # # # + # # # # # # # # # # # # # # # # # # # # # # # # # # # +## # # # # ##### # ### ####### ########### ### # # ####### # # ##### # # # ### ##### ##### # ##### # + # # # # # # # # # # # # # # # # # # # # # # + ### ##### ########### # ### # # ####### # # # ############# # ### ### # ### ######### # ### ### # # + # # # # # # # # # # # # # # # # # # # # # # # # # + ##### # # # ##### ####### # ### # # ##### # ### ####### ######### # ########### ### # ######### # # + # # # # # # # # # +################################################################################################## E \ No newline at end of file diff --git a/volkovim/task2/mazes/medium.txt b/volkovim/task2/mazes/medium.txt new file mode 100644 index 0000000..6eba40a --- /dev/null +++ b/volkovim/task2/mazes/medium.txt @@ -0,0 +1,50 @@ +S # # # # + ####### ### # # ########### ##### # ##### ##### # + # # # # # # # # # # +## # # ### ####### ##### ####### # ### ######### # + # # # # # # # # # # # # # + ####### # # # # ### ##### # ### ### # # # # ### # + # # # # # # # # # # # # # # # # # + # ### # ### # ### ### # # ### ### ####### # # ### + # # # # # # # # # # # # # # + # # ############# # ### ### # ######### # # ### # + # # # # # # # # # + ########### ########### # ##### ### ### # # # ### + # # # # # # # # # # # # # # + # # ####### # ### # ##### ### ### ### ### # # # # + # # # # # # # # # # # # # # # # +###### ### ### # # # # # # # ### ### ##### # # # # + # # # # # # # # # # # # # # # + ### # # ######### ### # # # # ####### ##### # # # + # # # # # # # # # # # # # # + ### ### # ##### # # ######### # # # # ##### # # # + # # # # # # # # # # # # +## # ######### # # ### ### # ### ######### ##### # + # # # # # # # # # # # # + ##### # ### # ### ##### # # # ####### ##### # # # + # # # # # # # # # # # # # # # +## # ##### # # ##### ##### ### ### # ### # # # ### + # # # # # # # # # # # # # + ##### # ### # # ##### ### # ### ######### # ##### + # # # # # # # # # # # + # ####### ######### ### ####### # # ####### ### # + # # # # # # # # # # # # # # + # # ####### # # ##### # # ### ### # # # # ##### # + # # # # # # # # # # # # # # # # + # ##### # ####### # # # # # ### # ### # # # ### # + # # # # # # # # # # # # # # # + # ########### # ### ####### ### # ### # # # # # # + # # # # # # # # # # # # # + # # ####### ##### ########### ##### # # ##### # # + # # # # # # # # # # # + ### ### ### # ############### # # # ##### ### ### + # # # # # # # # # # # # # # + # ### ### # ### ##### # # # # # ##### # ### # # # + # # # # # # # # # # # # # # + # # ####### # ### ######### ######### ### # # # # + # # # # # # # # # # # # # # # + ##### # ####### # # # ### # # # # # ### ### # # # + # # # # # # # # # # # # # # # +## ### ##### ####### ### # # ### ##### # ### ### # + # # # # +################################################ E \ No newline at end of file diff --git a/volkovim/task2/mazes/small.txt b/volkovim/task2/mazes/small.txt new file mode 100644 index 0000000..439365c --- /dev/null +++ b/volkovim/task2/mazes/small.txt @@ -0,0 +1,10 @@ +S # + ### ### # + # # # +## # # ### + # # # + ####### # + # # +## # ##### + +######## E \ No newline at end of file diff --git a/volkovim/task2/observer/console_view.py b/volkovim/task2/observer/console_view.py new file mode 100644 index 0000000..05f31fc --- /dev/null +++ b/volkovim/task2/observer/console_view.py @@ -0,0 +1,64 @@ +from observer.observer import Observer + + +class ConsoleView(Observer): + + def update(self, event): + + event_type = event.get("type") + + if event_type == "maze_loaded": + print("[VIEW] Maze loaded") + + elif event_type == "search_started": + print("[VIEW] Search started") + + elif event_type == "search_finished": + print("[VIEW] Search completed") + + elif event_type == "path_found": + print( + f"[VIEW] Path length: " + f"{event.get('length')}" + ) + + def render( + self, + maze, + path=None + ): + route_marks = set() + + if path: + for cell in path: + route_marks.add( + cell.getPosition() + ) + + screen = [] + + for row in maze.cells: + + visual_row = "" + + for cell in row: + + position = cell.getPosition() + + if ( + position in route_marks + and not cell.isStart + and not cell.isExit + ): + visual_row += "*" + + else: + visual_row += str(cell) + + screen.append( + visual_row + ) + + print( + "\n".join(screen) + ) \ No newline at end of file diff --git a/volkovim/task2/observer/observer.py b/volkovim/task2/observer/observer.py new file mode 100644 index 0000000..3a8886f --- /dev/null +++ b/volkovim/task2/observer/observer.py @@ -0,0 +1,8 @@ +from abc import ABC, abstractmethod + + +class Observer(ABC): + + @abstractmethod + def update(self, event): + pass \ No newline at end of file diff --git a/volkovim/task2/report/report_2.docx b/volkovim/task2/report/report_2.docx new file mode 100644 index 0000000..1143cd7 Binary files /dev/null and b/volkovim/task2/report/report_2.docx differ diff --git a/volkovim/task2/requirements.txt b/volkovim/task2/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/volkovim/task2/solver/maze_solver.py b/volkovim/task2/solver/maze_solver.py new file mode 100644 index 0000000..cbee4ca --- /dev/null +++ b/volkovim/task2/solver/maze_solver.py @@ -0,0 +1,73 @@ +import time + +from solver.search_stats import SearchStats + + +class MazeSolver: + + def __init__( + self, + maze, + strategy + ): + self.maze = maze + self.strategy = strategy + self.observers = [] + + def addObserver( + self, + observer + ): + self.observers.append( + observer + ) + + def notify( + self, + event + ): + for observer in self.observers: + observer.update(event) + + def setStrategy( + self, + strategy + ): + self.strategy = strategy + + def solve(self): + + self.notify( + "search_started" + ) + + start_time = time.perf_counter() + + path, visited_cells = ( + self.strategy.findPath( + self.maze, + self.maze.start, + self.maze.exit + ) + ) + + finish_time = ( + time.perf_counter() + ) + + elapsed_ms = ( + finish_time + - start_time + ) * 1000 + + stats = SearchStats( + elapsed_ms, + visited_cells, + len(path) + ) + + self.notify( + "search_finished" + ) + + return path, stats \ No newline at end of file diff --git a/volkovim/task2/solver/search_stats.py b/volkovim/task2/solver/search_stats.py new file mode 100644 index 0000000..9553063 --- /dev/null +++ b/volkovim/task2/solver/search_stats.py @@ -0,0 +1,22 @@ +class SearchStats: + + def __init__( + self, + time_ms, + visited_cells, + path_length + ): + self.time_ms = time_ms + self.visited_cells = visited_cells + self.path_length = path_length + + def __str__(self): + + return ( + f"Time: " + f"{self.time_ms:.4f} ms | " + f"Visited: " + f"{self.visited_cells} | " + f"Path length: " + f"{self.path_length}" + ) \ No newline at end of file diff --git a/volkovim/task2/strategies/astar.py b/volkovim/task2/strategies/astar.py new file mode 100644 index 0000000..0907a75 --- /dev/null +++ b/volkovim/task2/strategies/astar.py @@ -0,0 +1,107 @@ +import heapq +from itertools import count +from strategies.strategy import PathFindingStrategy + + +class AStarStrategy(PathFindingStrategy): + + def _estimate(self, current, target): + return ( + abs(current.x - target.x) + + abs(current.y - target.y) + ) + + def findPath(self, maze, start, exit): + + frontier = [] + + sequence = count() + + heapq.heappush( + frontier, + ( + self._estimate(start, exit), + next(sequence), + 0, + start + ) + ) + + ancestry = {} + + travel_cost = { + start.getPosition(): 0 + } + + explored = set() + + explored_count = 0 + + while frontier: + + _, _, spent, current = heapq.heappop( + frontier + ) + + current_mark = current.getPosition() + + if current_mark in explored: + continue + + explored.add(current_mark) + explored_count += 1 + + if current == exit: + break + + for neighbor in maze.getNeighbors(current): + + mark = neighbor.getPosition() + + new_cost = spent + 1 + + if ( + mark not in travel_cost + or new_cost < travel_cost[mark] + ): + + travel_cost[mark] = new_cost + ancestry[mark] = current + + priority = ( + new_cost + + self._estimate( + neighbor, + exit + ) + ) + + heapq.heappush( + frontier, + ( + priority, + next(sequence), + new_cost, + neighbor + ) + ) + + if ( + exit.getPosition() not in ancestry + and exit != start + ): + return [], explored_count + + route = [] + cursor = exit + + while cursor != start: + route.append(cursor) + cursor = ancestry[ + cursor.getPosition() + ] + + route.append(start) + route.reverse() + + return route, explored_count \ No newline at end of file diff --git a/volkovim/task2/strategies/bfs.py b/volkovim/task2/strategies/bfs.py new file mode 100644 index 0000000..0860126 --- /dev/null +++ b/volkovim/task2/strategies/bfs.py @@ -0,0 +1,51 @@ +from collections import deque +from strategies.strategy import PathFindingStrategy + + +class BFSStrategy(PathFindingStrategy): + + def findPath(self, maze, start, exit): + + frontier = deque([start]) + + visited = { + start.getPosition() + } + + ancestry = {} + + explored_count = 0 + + while frontier: + + current = frontier.popleft() + explored_count += 1 + + if current == exit: + break + + for neighbor in maze.getNeighbors(current): + + mark = neighbor.getPosition() + + if mark in visited: + continue + + visited.add(mark) + ancestry[mark] = current + frontier.append(neighbor) + + if exit.getPosition() not in visited: + return [], explored_count + + route = [] + cursor = exit + + while cursor != start: + route.append(cursor) + cursor = ancestry[cursor.getPosition()] + + route.append(start) + route.reverse() + + return route, explored_count \ No newline at end of file diff --git a/volkovim/task2/strategies/dfs.py b/volkovim/task2/strategies/dfs.py new file mode 100644 index 0000000..3b12bdb --- /dev/null +++ b/volkovim/task2/strategies/dfs.py @@ -0,0 +1,52 @@ +from strategies.strategy import PathFindingStrategy + + +class DFSStrategy(PathFindingStrategy): + + def findPath(self, maze, start, exit): + + frontier = [start] + + visited = { + start.getPosition() + } + + ancestry = {} + + explored_count = 0 + + while frontier: + + current = frontier.pop() + explored_count += 1 + + if current == exit: + break + + neighbors = maze.getNeighbors(current) + + for neighbor in reversed(neighbors): + + point = neighbor.getPosition() + + if point in visited: + continue + + visited.add(point) + ancestry[point] = current + frontier.append(neighbor) + + if exit.getPosition() not in visited: + return [], explored_count + + route = [] + cursor = exit + + while cursor != start: + route.append(cursor) + cursor = ancestry[cursor.getPosition()] + + route.append(start) + route.reverse() + + return route, explored_count \ No newline at end of file diff --git a/volkovim/task2/strategies/dijkstra.py b/volkovim/task2/strategies/dijkstra.py new file mode 100644 index 0000000..e69de29 diff --git a/volkovim/task2/strategies/strategy.py b/volkovim/task2/strategies/strategy.py new file mode 100644 index 0000000..ef3376e --- /dev/null +++ b/volkovim/task2/strategies/strategy.py @@ -0,0 +1,8 @@ +from abc import ABC, abstractmethod + + +class PathFindingStrategy(ABC): + + @abstractmethod + def findPath(self, maze, start, exit): + pass \ No newline at end of file