2026-rff_mp/YaroslavtsevAS/docs/2-nd-lab/main.py

221 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import sys
import os
from collections import deque
class Tile:
def __init__(self, x, y):
self._x = x
self._y = y
self._wall = False
self._entry = False
self._goal = False
@property
def x(self): return self._x
@property
def y(self): return self._y
@property
def is_wall(self): return self._wall
@is_wall.setter
def is_wall(self, value): self._wall = value
@property
def is_entry(self): return self._entry
@is_entry.setter
def is_entry(self, value): self._entry = value
@property
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
class Labyrinth:
def __init__(self, width, height):
self._width = width
self._height = height
self._grid = [[Tile(x, y) for x in range(width)] for y in range(height)]
self._start = None
self._exit = None
@property
def width(self): return self._width
@property
def height(self): return self._height
@property
def start(self): return self._start
@property
def exit(self): return self._exit
def tile_at(self, x, y):
if 0 <= x < self._width and 0 <= y < self._height:
return self._grid[y][x]
return None
def configure_tile(self, x, y, kind):
tile = self.tile_at(x, y)
if tile is None: return
if kind == 'wall':
tile.is_wall = True
elif kind == 'entry':
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
tile.is_goal = True
tile.is_wall = False
self._exit = tile
elif kind == 'floor':
tile.is_wall = False
def neighbours(self, tile):
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
class TextLabyrinthBuilder(LabyrinthBuilder):
def build(self, filename):
with open(filename, 'r', encoding='utf-8') as f:
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(lines):
for x, ch in enumerate(row):
if ch == '#':
lab.configure_tile(x, y, 'wall')
elif ch == 'S':
lab.configure_tile(x, y, 'entry')
entries += 1
elif ch == 'E':
lab.configure_tile(x, y, 'goal')
exits += 1
else:
lab.configure_tile(x, y, 'floor')
if entries != 1 or exits != 1:
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))
print(' ЛАБИРИНТ')
print('=' * (lab.width * 2 + 4))
for y in range(lab.height):
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=' ')
print()
print('=' * (lab.width * 2 + 4))
print(' S вход E выход # стена . пол')
if __name__ == '__main__':
maze_file = sys.argv[1] if len(sys.argv) > 1 else 'maze1.txt'
builder = TextLabyrinthBuilder()
labyrinth = builder.build(maze_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("Путь не найден!")