2026-rff_mp/VolkovVA/cod.py
2026-05-25 06:35:06 +03:00

234 lines
7.8 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from collections import deque
import heapq
import time
from abc import ABC, abstractmethod
# Модель 
class Cell:
def __init__(self, x, y, is_wall=False, is_start=False, is_exit=False):
        self.x = x
        self.y = y
        self.is_wall = is_wall
        self.is_start = is_start
        self.is_exit = is_exit
        self.visited = False  
    def is_passable(self):
        return not self.is_wall
class Player:
def __init__(self, start_cell):
self.current_cell = start_cell
class Maze:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.cells = [[Cell(x, y) for x in range(width)] for y in range(height)]
        self.start_cell = self.cells[0][0]
self.exit_cell = self.cells[height-1][width-1]
 
    def get_cell(self, x, y):
        if 0 <= x < self.width and 0 <= y < self.height:
            return self.cells[y][x]
        return None
    def get_neighbors(self, cell):
        neighbors = []
        directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        for dx, dy in directions:
            neighbor = self.get_cell(cell.x + dx, cell.y + dy)
            if neighbor and neighbor.is_passable():
                neighbors.append(neighbor)
        return neighbors
# Строитель 
class MazeBuilder:
    def buildFromFile(self, filename):
        with open(filename, 'r') as f:
            lines = f.readlines()
        height = len(lines)
        width = len(lines[0].strip())
        maze = Maze(width, height)
        for y, line in enumerate(lines):
            for x, char in enumerate(line.strip()):
                cell = maze.get_cell(x, y)
                if char == '#': cell.is_wall = True
                elif char == 'S':
                    cell.is_start = True
                    maze.start_cell = cell
                elif char == 'E':
                    cell.is_exit = True
                    maze.exit_cell = cell
        if not maze.start_cell or not maze.exit_cell:
            raise ValueError("Лабиринт сломан")
        return maze
#  Strategy 
class PathFindingStrategy:
    def findPath(self, maze, start, exit):
        raise NotImplementedError("Этот метод должен быть реализован в стратегии!")
    def _reconstruct_path(self, parents, current):
        path = []
        while current:
            path.append(current)
            current = parents.get(current)
        return path[::-1]
class Command(ABC):
@abstractmethod
def execute(self): pass
@abstractmethod
def undo(self): pass
class MoveCommand(Command):
def __init__(self, player, dx, dy, maze):
self.player = player
self.dx = dx
self.dy = dy
self.maze = maze
self.prev_cell = None
def execute(self):
self.prev_cell = self.player.current_cell
target = self.maze.get_cell(self.prev_cell.x + self.dx, self.prev_cell.y + self.dy)
if target and target.is_passable():
self.player.current_cell = target
return True
return False
def undo(self):
self.player.current_cell = self.prev_cell
class BFSStrategy(PathFindingStrategy):
    def findPath(self, maze, start, exit):
        queue = deque([start])
        parents = {start: None}
        start.visited = True
        while queue:
            current = queue.popleft()
            if current == exit:
                return self._reconstruct_path(parents, exit)
            for neighbor in maze.get_neighbors(current):
                if not neighbor.visited:
                    neighbor.visited = True
                    parents[neighbor] = current
                    queue.append(neighbor)
        return []
class AStarStrategy(PathFindingStrategy):
    def _heuristic(self, a, b):
        return abs(a.x - b.x) + abs(a.y - b.y)
    def findPath(self, maze, start, exit):
        heap = [(0, start)]
        parents = {start: None}
        g_score = {start: 0}
        while heap:
            _, current = heapq.heappop(heap)
            if current == exit:
                return self._reconstruct_path(parents, exit)
            for neighbor in maze.get_neighbors(current):
                new_g = g_score[current] + 1
                if new_g < g_score.get(neighbor, float('inf')):
                    parents[neighbor] = current
                    g_score[neighbor] = new_g
                    f = new_g + self._heuristic(neighbor, exit)
                    heapq.heappush(heap, (f, neighbor))
        return []
# Статистика 
class SearchStats:
    def __init__(self, time_ms, visited, length):
        self.time_ms = time_ms
        self.visited = visited
        self.length = length
# Паттерн Observer 
class Observer(ABC):
    @abstractmethod
    def update(self, event: str, data=None):
        pass
class ConsoleView(Observer):
    def update(self, event: str, data=None):
        if event == "path_found":
            print(f"Событие '{event}': время={data.time_ms:.2f}мс, посещено={data.visited}, путь={data.length}")
        elif event == "maze_loaded":
            print(f"Событие '{event}': Лабиринт загружен.")
# --- 6. Оркестратор (MazeSolver) ---
class MazeSolver:
def __init__(self, maze, player):
self.maze = maze
self.player = player
self.strat = None
self._observers = []
self._history = [] # Стек для undo
def attach(self, observer):
self._observers.append(observer)
def notify(self, event, data=None):
for obs in self._observers:
obs.update(event, data)
# Метод для ручного управления (Command)
def move_player(self, dx, dy):
cmd = MoveCommand(self.player, dx, dy, self.maze)
if cmd.execute():
self._history.append(cmd)
self.notify("player_moved", self.player.current_cell)
else:
self.notify("error", "Стена!")
def undo_move(self):
if self._history:
cmd = self._history.pop()
cmd.undo()
self.notify("player_moved", self.player.current_cell)
    def solve(self):
        if not self.strat: return None
        
        t0 = time.perf_counter()
        path = self.strat.findPath(self.maze, self.maze.start_cell, self.maze.exit_cell)
        t1 = time.perf_counter()
        
        visited_count = sum(c.visited for row in self.maze.cells for c in row)
        stats = SearchStats((t1 - t0) * 1000, visited_count, len(path))
        
        self.notify("path_found", stats)
        return stats
# --- Запуск ---
if __name__ == "__main__":
maze = Maze(10, 10)
player = Player(maze.start_cell)
solver = MazeSolver(maze, player)
solver.attach(ConsoleView())
solver.move_player(1, 0)
solver.undo_move()
print("Используйте WASD для движения, Z для отмены хода, Q для выхода")
while True:
cmd = input("Введите команду: ").lower()
if cmd == 'd':
solver.move_player(1, 0)
elif cmd == 'a':
solver.move_player(-1, 0)
elif cmd == 's':
solver.move_player(0, 1)
elif cmd == 'w':
solver.move_player(0, -1)
elif cmd == 'z':
solver.undo_move()
elif cmd == 'q':
break