diff --git a/lukovnikovde/docs/data/2-nd-exercise/main.py b/lukovnikovde/docs/data/2-nd-exercise/main.py index 478d1f3..1966589 100644 --- a/lukovnikovde/docs/data/2-nd-exercise/main.py +++ b/lukovnikovde/docs/data/2-nd-exercise/main.py @@ -1,5 +1,9 @@ import sys import os +from collections import deque +import heapq +import time + class Tile: """Клетка лабиринта.""" @@ -92,26 +96,125 @@ class TextLabyrinthLoader(LabyrinthLoader): return lab -if __name__ == "__main__": - # Простая проверка загрузки и вывода лабиринта - loader = TextLabyrinthLoader() - lab = loader.load("maze/maze1.txt") # временно создадим позже +class Pathfinder: + def find_path(self, lab, start, goal): + raise NotImplementedError - os.system('cls' if os.name == 'nt' else 'clear') - print("=" * (lab.width * 2 + 4)) - print(" ЛАБИРИНТ (загружен)") - print("=" * (lab.width * 2 + 4)) - for y in range(lab.height): - print(" ", end='') - for x in range(lab.width): - t = lab.get_tile(x, y) - if t == lab.start_tile: - print('S', end=' ') - elif t == lab.exit_tile: - print('E', end=' ') - elif t.wall: - print('#', end=' ') - else: - print('.', end=' ') - print() - print("=" * (lab.width * 2 + 4)) \ No newline at end of file + def _build_path(self, came_from, start, goal): + path = [] + cur = goal + while cur is not None: + path.append(cur) + cur = came_from.get(cur) + path.reverse() + return path + + def visited_count(self): + return getattr(self, '_visited', 0) + + +class BFS(Pathfinder): + def find_path(self, lab, start, goal): + q = deque([start]) + parent = {start: None} + visited = {start} + while q: + cur = q.popleft() + if cur == goal: + self._visited = len(visited) + return self._build_path(parent, start, goal) + for nb in lab.neighbours(cur): + if nb not in visited: + visited.add(nb) + parent[nb] = cur + q.append(nb) + self._visited = len(visited) + return [] + + +class DFS(Pathfinder): + def find_path(self, lab, start, goal): + stack = [start] + parent = {start: None} + visited = {start} + while stack: + cur = stack.pop() + if cur == goal: + self._visited = len(visited) + return self._build_path(parent, start, goal) + for nb in lab.neighbours(cur): + if nb not in visited: + visited.add(nb) + parent[nb] = cur + stack.append(nb) + self._visited = len(visited) + return [] + + +class AStar(Pathfinder): + def _heuristic(self, a, b): + return abs(a.x - b.x) + abs(a.y - b.y) + + def find_path(self, lab, start, goal): + heap = [] + counter = 0 + start_f = self._heuristic(start, goal) + heapq.heappush(heap, (start_f, counter, start)) + counter += 1 + parent = {} + g = {start: 0} + f = {start: start_f} + visited = set() + while heap: + cur_f, _, cur = heapq.heappop(heap) + visited.add(cur) + if cur == goal: + self._visited = len(visited) + return self._build_path(parent, start, goal) + if cur_f > f.get(cur, float('inf')): + continue + for nb in lab.neighbours(cur): + new_g = g[cur] + 1 + if new_g < g.get(nb, float('inf')): + parent[nb] = cur + g[nb] = new_g + new_f = new_g + self._heuristic(nb, goal) + f[nb] = new_f + heapq.heappush(heap, (new_f, counter, nb)) + counter += 1 + self._visited = len(visited) + return [] + + +class LabyrinthSolver: + def __init__(self, lab): + self.lab = lab + self.strategy = None + + def set_strategy(self, strategy): + self.strategy = strategy + + def solve(self): + if not self.strategy: + return None + t0 = time.perf_counter() + path = self.strategy.find_path(self.lab, self.lab.start_tile, self.lab.exit_tile) + t1 = time.perf_counter() + return { + 'time_ms': (t1 - t0) * 1000, + 'visited': self.strategy.visited_count(), + 'length': len(path) + } + + +if __name__ == "__main__": + loader = TextLabyrinthLoader() + lab = loader.load("maze/maze1.txt") + print("Загружен лабиринт, пробуем BFS...") + solver = LabyrinthSolver(lab) + solver.set_strategy(BFS()) + stats = solver.solve() + if stats['length'] > 0: + print(f"BFS: время={stats['time_ms']:.3f}мс, посещено={stats['visited']}, длина={stats['length']}") + else: + print("Путь не найден") \ No newline at end of file