From 1dcc56d0bc06046e1f6bed8cdc8a4541ddaba90f Mon Sep 17 00:00:00 2001 From: YaroslavtsevAS Date: Sun, 24 May 2026 15:33:58 +0000 Subject: [PATCH] [2] add BFS and DFS --- YaroslavtsevAS/docs/2-nd-lab/main.py | 205 ++++++++++++++++----------- 1 file changed, 126 insertions(+), 79 deletions(-) diff --git a/YaroslavtsevAS/docs/2-nd-lab/main.py b/YaroslavtsevAS/docs/2-nd-lab/main.py index d5dfe12..e8c4e0e 100644 --- a/YaroslavtsevAS/docs/2-nd-lab/main.py +++ b/YaroslavtsevAS/docs/2-nd-lab/main.py @@ -1,8 +1,8 @@ import sys import os +from collections import deque class Tile: - """Клетка лабиринта""" def __init__(self, x, y): self._x = x self._y = y @@ -11,43 +11,25 @@ class Tile: self._goal = False @property - def x(self): - return self._x - + def x(self): return self._x @property - def y(self): - return self._y - + def y(self): return self._y @property - def is_wall(self): - return self._wall - + def is_wall(self): return self._wall @is_wall.setter - def is_wall(self, value): - self._wall = value - + def is_wall(self, value): self._wall = value @property - def is_entry(self): - return self._entry - + def is_entry(self): return self._entry @is_entry.setter - def is_entry(self, value): - self._entry = value - + def is_entry(self, value): self._entry = value @property - def is_goal(self): - return self._goal - + def is_goal(self): return self._goal @is_goal.setter - def is_goal(self, value): - self._goal = value - - def can_walk(self): - return not self._wall + def is_goal(self, value): self._goal = value + def can_walk(self): return not self._wall class Labyrinth: - """Лабиринт – двумерная сетка клеток""" def __init__(self, width, height): self._width = width self._height = height @@ -56,20 +38,13 @@ class Labyrinth: self._exit = None @property - def width(self): - return self._width - + def width(self): return self._width @property - def height(self): - return self._height - + def height(self): return self._height @property - def start(self): - return self._start - + def start(self): return self._start @property - def exit(self): - return self._exit + def exit(self): return self._exit def tile_at(self, x, y): if 0 <= x < self._width and 0 <= y < self._height: @@ -78,19 +53,16 @@ class Labyrinth: def configure_tile(self, x, y, kind): tile = self.tile_at(x, y) - if tile is None: - return + if tile is None: return if kind == 'wall': tile.is_wall = True elif kind == 'entry': - if self._start: - self._start.is_entry = False + if self._start: self._start.is_entry = False tile.is_entry = True tile.is_wall = False self._start = tile elif kind == 'goal': - if self._exit: - self._exit.is_goal = False + if self._exit: self._exit.is_goal = False tile.is_goal = True tile.is_wall = False self._exit = tile @@ -98,31 +70,26 @@ class Labyrinth: tile.is_wall = False def neighbours(self, tile): - result = [] - for dx, dy in ((0, -1), (0, 1), (-1, 0), (1, 0)): - nx, ny = tile.x + dx, tile.y + dy - nbr = self.tile_at(nx, ny) - if nbr and nbr.can_walk(): - result.append(nbr) - return result + res = [] + for dx, dy in ((0,-1),(0,1),(-1,0),(1,0)): + nb = self.tile_at(tile.x+dx, tile.y+dy) + if nb and nb.can_walk(): + res.append(nb) + return res class LabyrinthBuilder: - def build(self, filename): - raise NotImplementedError - + def build(self, filename): raise NotImplementedError class TextLabyrinthBuilder(LabyrinthBuilder): def build(self, filename): with open(filename, 'r', encoding='utf-8') as f: - raw = [line.rstrip('\n') for line in f] - h = len(raw) - w = max(len(line) for line in raw) if h > 0 else 0 - entries = 0 - exits = 0 + lines = [line.rstrip('\n') for line in f] + h = len(lines) + w = max(len(l) for l in lines) if h else 0 + entries = exits = 0 lab = Labyrinth(w, h) - - for y, row in enumerate(raw): + for y, row in enumerate(lines): for x, ch in enumerate(row): if ch == '#': lab.configure_tile(x, y, 'wall') @@ -135,13 +102,80 @@ class TextLabyrinthBuilder(LabyrinthBuilder): else: lab.configure_tile(x, y, 'floor') if entries != 1 or exits != 1: - raise ValueError( - f"Лабиринт должен содержать ровно один вход (S) и один выход (E). " - f"Найдено: S={entries}, E={exits}" - ) + raise ValueError(f"Некорректный лабиринт: найдено S={entries}, E={exits}") return lab +class Pathfinder: + def find_path(self, lab, start, goal): raise NotImplementedError + + def _build_path(self, predecessors, start, goal): + path = [] + cur = goal + while cur is not None: + path.append(cur) + cur = predecessors.get(cur) + path.reverse() + return path + + @property + def visited_count(self): + return getattr(self, '_visited', 0) + + +class BFS_Pathfinder(Pathfinder): + def find_path(self, lab, start, goal): + q = deque([start]) + preds = {start: None} + seen = {start} + while q: + cur = q.popleft() + if cur == goal: + self._visited = len(seen) + return self._build_path(preds, start, goal) + for nb in lab.neighbours(cur): + if nb not in seen: + seen.add(nb) + preds[nb] = cur + q.append(nb) + self._visited = len(seen) + return [] + + +class DFS_Pathfinder(Pathfinder): + def find_path(self, lab, start, goal): + stack = [start] + preds = {start: None} + seen = {start} + while stack: + cur = stack.pop() + if cur == goal: + self._visited = len(seen) + return self._build_path(preds, start, goal) + for nb in lab.neighbours(cur): + if nb not in seen: + seen.add(nb) + preds[nb] = cur + stack.append(nb) + self._visited = len(seen) + 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 self._strategy is None: + return None + path = self._strategy.find_path(self._lab, self._lab.start, self._lab.exit) + return path + + def show_labyrinth(lab): os.system('cls' if os.name == 'nt' else 'clear') print('=' * (lab.width * 2 + 4)) @@ -151,24 +185,37 @@ def show_labyrinth(lab): print(' ', end='') for x in range(lab.width): t = lab.tile_at(x, y) - if t == lab.start: - print('S', end=' ') - elif t == lab.exit: - print('E', end=' ') - elif t.is_wall: - print('#', end=' ') - else: - print('.', end=' ') + if t == lab.start: print('S', end=' ') + elif t == lab.exit: print('E', end=' ') + elif t.is_wall: print('#', end=' ') + else: print('.', end=' ') print() print('=' * (lab.width * 2 + 4)) print(' S – вход E – выход # – стена . – пол') if __name__ == '__main__': - if len(sys.argv) < 2: - print("Использование: python main.py <путь к файлу лабиринта>") - sys.exit(1) - maze_file = sys.argv[1] + maze_file = sys.argv[1] if len(sys.argv) > 1 else 'maze1.txt' builder = TextLabyrinthBuilder() labyrinth = builder.build(maze_file) - show_labyrinth(labyrinth) \ No newline at end of file + show_labyrinth(labyrinth) + + solver = LabyrinthSolver(labyrinth) + print("\nВыберите алгоритм: 1 – BFS, 2 – DFS") + choice = input("> ").strip() + if choice == '1': + solver.set_strategy(BFS_Pathfinder()) + print("Запущен BFS...") + elif choice == '2': + solver.set_strategy(DFS_Pathfinder()) + print("Запущен DFS...") + else: + print("Неверный выбор, выход.") + sys.exit(0) + + path = solver.solve() + if path: + print(f"Путь найден! Длина: {len(path)} клеток") + print(f"Посещено клеток: {solver._strategy.visited_count}") + else: + print("Путь не найден!") \ No newline at end of file