2026-rff_mp/BoriskovaDV/docs/data/2-nd-exercise/main.py

207 lines
6.2 KiB
Python

import sys
import os
from collections import deque
import heapq
class GridPoint:
def __init__(self, x, y):
self.x = x
self.y = y
self.blocked = False
self.is_start = False
self.is_exit = False
def can_step(self):
return not self.blocked
class Labyrinth:
def __init__(self, w, h):
self.w = w
self.h = h
self.grid = [[GridPoint(x, y) for x in range(w)] for y in range(h)]
self.start_point = None
self.exit_point = None
def get_point(self, x, y):
if 0 <= x < self.w and 0 <= y < self.h:
return self.grid[y][x]
return None
def set_point(self, x, y, typ):
p = self.get_point(x, y)
if not p:
return
if typ == 'wall':
p.blocked = True
elif typ == 'start':
if self.start_point:
self.start_point.is_start = False
p.is_start = True
p.blocked = False
self.start_point = p
elif typ == 'exit':
if self.exit_point:
self.exit_point.is_exit = False
p.is_exit = True
p.blocked = False
self.exit_point = p
elif typ == 'path':
p.blocked = False
def neighbors(self, p):
dirs = [(0, -1), (0, 1), (-1, 0), (1, 0)]
res = []
for dx, dy in dirs:
nx, ny = p.x + dx, p.y + dy
nb = self.get_point(nx, ny)
if nb and nb.can_step():
res.append(nb)
return res
class MazeLoader:
def load(self, filename):
raise NotImplementedError
class TextMazeLoader(MazeLoader):
def load(self, filename):
with open(filename, 'r') as f:
lines = [line.rstrip('\n') for line in f]
h = len(lines)
w = max(len(line) for line in lines) if h > 0 else 0
start_cnt = 0
exit_cnt = 0
lab = Labyrinth(w, h)
for y, line in enumerate(lines):
for x, ch in enumerate(line):
if ch == '#':
lab.set_point(x, y, 'wall')
elif ch == 'S':
lab.set_point(x, y, 'start')
start_cnt += 1
elif ch == 'E':
lab.set_point(x, y, 'exit')
exit_cnt += 1
else:
lab.set_point(x, y, 'path')
if start_cnt != 1 or exit_cnt != 1:
raise ValueError(f"Need exactly one S and one E. Found S={start_cnt}, E={exit_cnt}")
return lab
class SearchAlgorithm:
def find_way(self, lab, start, goal):
raise NotImplementedError
def _build_path(self, prev, start, goal):
path = []
cur = goal
while cur:
path.append(cur)
cur = prev.get(cur)
path.reverse()
return path
def get_visited(self):
return getattr(self, '_visited', 0)
class BreadthFirst(SearchAlgorithm):
def find_way(self, lab, start, goal):
q = deque([start])
prev = {start: None}
seen = {start}
while q:
cur = q.popleft()
if cur == goal:
self._visited = len(seen)
return self._build_path(prev, start, goal)
for nb in lab.neighbors(cur):
if nb not in seen:
seen.add(nb)
prev[nb] = cur
q.append(nb)
self._visited = len(seen)
return []
class DepthFirst(SearchAlgorithm):
def find_way(self, lab, start, goal):
stack = [start]
prev = {start: None}
seen = {start}
while stack:
cur = stack.pop()
if cur == goal:
self._visited = len(seen)
return self._build_path(prev, start, goal)
for nb in lab.neighbors(cur):
if nb not in seen:
seen.add(nb)
prev[nb] = cur
stack.append(nb)
self._visited = len(seen)
return []
class AStar(SearchAlgorithm):
def _dist(self, a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
def find_way(self, lab, start, goal):
heap = []
cnt = 0
start_f = self._dist(start, goal)
heapq.heappush(heap, (start_f, cnt, start))
cnt += 1
prev = {}
g = {start: 0}
f = {start: start_f}
seen = set()
while heap:
cur_f, _, cur = heapq.heappop(heap)
seen.add(cur)
if cur == goal:
self._visited = len(seen)
return self._build_path(prev, start, goal)
if cur_f > f.get(cur, float('inf')):
continue
for nb in lab.neighbors(cur):
new_g = g[cur] + 1
if new_g < g.get(nb, float('inf')):
prev[nb] = cur
g[nb] = new_g
new_f = new_g + self._dist(nb, goal)
f[nb] = new_f
heapq.heappush(heap, (new_f, cnt, nb))
cnt += 1
self._visited = len(seen)
return []
class TextView:
def display(self, lab):
os.system('cls' if os.name == 'nt' else 'clear')
print("=" * (lab.w * 2 + 4))
print(" LABYRINTH")
print("=" * (lab.w * 2 + 4))
for y in range(lab.h):
print(" ", end='')
for x in range(lab.w):
p = lab.get_point(x, y)
if p == lab.start_point:
print('S', end=' ')
elif p == lab.exit_point:
print('E', end=' ')
elif p.blocked:
print('#', end=' ')
else:
print('.', end=' ')
print()
print("=" * (lab.w * 2 + 4))
print(" S - start E - exit # - wall . - path")
if __name__ == "__main__":
loader = TextMazeLoader()
lab = loader.load("maze/maze1.txt")
view = TextView()
view.display(lab)
print("\nTesting BFS...")
bfs = BreadthFirst()
path = bfs.find_way(lab, lab.start_point, lab.exit_point)
print(f"BFS path length: {len(path)} visited: {bfs.get_visited()}")