Паттерн Strategy

This commit is contained in:
tseremonnikovaaa 2026-05-24 20:09:28 +03:00
parent 02842819e9
commit 4c14b6d0b3

View File

@ -1,74 +1,51 @@
import os
import time
import csv
from collections import deque
import heapq
from collections import deque
from abc import ABC, abstractmethod
import matplotlib.pyplot as plt
import pandas as pd
from dataclasses import dataclass
from typing import List, Tuple, Optional, Dict, Any
import random
import os
@dataclass
class Cell:
"""Клетка лабиринта"""
x: int
y: int
is_wall: bool = False
is_start: bool = False
is_exit: bool = False
weight: int = 1
def is_passable(self) -> bool:
def __init__(self, x, y, is_wall=False):
self.x = x
self.y = y
self.is_wall = is_wall
self.is_start = False
self.is_exit = False
def is_passable(self):
return not self.is_wall
def __hash__(self):
return hash((self.x, self.y))
def __eq__(self, other):
return self.x == other.x and self.y == other.y if other else False
class Maze:
"""Лабиринт"""
def __init__(self, width: int, height: int):
def __init__(self, width, height):
self.width = width
self.height = height
self._cells: List[List[Optional[Cell]]] = [[None for _ in range(width)] for _ in range(height)]
self.start: Optional[Cell] = None
self.exit: Optional[Cell] = None
def set_cell(self, x: int, y: int, cell: Cell) -> None:
self.cells = [[Cell(x, y) for x in range(width)] for y in range(height)]
self.start = None
self.exit = None
def get_cell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
self._cells[y][x] = cell
def get_cell(self, x: int, y: int) -> Optional[Cell]:
if 0 <= x < self.width and 0 <= y < self.height:
return self._cells[y][x]
return self.cells[y][x]
return None
def get_neighbors(self, cell: Cell) -> List[Cell]:
def get_neighbors(self, cell):
neighbors = []
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
for dx, dy in directions:
for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
nx, ny = cell.x + dx, cell.y + dy
neighbor = self.get_cell(nx, ny)
if neighbor and neighbor.is_passable():
neighbors.append(neighbor)
nb = self.get_cell(nx, ny)
if nb and nb.is_passable():
neighbors.append(nb)
return neighbors
def get_all_cells(self) -> List[Cell]:
cells = []
for y in range(self.height):
for x in range(self.width):
cell = self.get_cell(x, y)
if cell:
cells.append(cell)
return cells
def __str__(self) -> str:
def __str__(self):
result = ""
for y in range(self.height):
for x in range(self.width):
@ -85,3 +62,121 @@ class Maze:
result += " "
result += "\n"
return result
class MazeBuilder(ABC):
@abstractmethod
def build_from_file(self, filename):
pass
class TextFileMazeBuilder(MazeBuilder):
def build_from_file(self, filename):
with open(filename, 'r', encoding='utf-8') as f:
lines = [line.rstrip('\n') for line in f.readlines()]
height = len(lines)
width = max(len(line) for line in lines)
maze = Maze(width, height)
for y, line in enumerate(lines):
for x, ch in enumerate(line):
cell = maze.get_cell(x, y)
if ch == '#':
cell.is_wall = True
elif ch == 'S':
cell.is_start = True
maze.start = cell
elif ch == 'E':
cell.is_exit = True
maze.exit = cell
else:
cell.is_wall = False
return maze
class PathFindingStrategy(ABC):
@abstractmethod
def find_path(self, maze, start, exit):
pass
class BFSStrategy(PathFindingStrategy):
"""Поиск в ширину"""
def find_path(self, maze, start, exit):
visited = set()
if start == exit:
return [start], 1
queue = deque([start])
visited.add(start)
parent = {start: None}
while queue:
current = queue.popleft()
for nb in maze.get_neighbors(current):
if nb not in visited:
visited.add(nb)
parent[nb] = current
if nb == exit:
path = []
node = nb
while node is not None:
path.append(node)
node = parent[node]
path.reverse()
return path, len(visited)
queue.append(nb)
return [], len(visited)
class DFSStrategy(PathFindingStrategy):
"""Поиск в глубину"""
def find_path(self, maze, start, exit):
visited = set()
stack = [(start, [start])]
while stack:
current, path = stack.pop()
if current == exit:
return path, len(visited)
visited.add(current)
for nb in maze.get_neighbors(current):
if nb not in visited:
stack.append((nb, path + [nb]))
return [], len(visited)
class AStarStrategy(PathFindingStrategy):
"""Алгоритм A"""
def heuristic(self, cell, exit):
return abs(cell.x - exit.x) + abs(cell.y - exit.y)
def find_path(self, maze, start, exit):
open_set = []
counter = 0
heapq.heappush(open_set, (0, counter, start))
counter += 1
came_from = {}
g_score = {start: 0}
f_score = {start: self.heuristic(start, exit)}
visited = set()
while open_set:
_, _, current = heapq.heappop(open_set)
visited.add(current)
if current == exit:
path = []
node = current
while node in came_from:
path.append(node)
node = came_from[node]
path.append(start)
path.reverse()
return path, len(visited)
for nb in maze.get_neighbors(current):
tentative_g = g_score[current] + 1
if tentative_g < g_score.get(nb, float('inf')):
came_from[nb] = current
g_score[nb] = tentative_g
f = tentative_g + self.heuristic(nb, exit)
heapq.heappush(open_set, (f, counter, nb))
counter += 1
return [], len(visited)