Паттерн 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 time
import csv import csv
from collections import deque
import heapq import heapq
from collections import deque
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import matplotlib.pyplot as plt
import pandas as pd
from dataclasses import dataclass from dataclasses import dataclass
from typing import List, Tuple, Optional, Dict, Any import os
import random
@dataclass
class Cell: class Cell:
"""Клетка лабиринта""" """Клетка лабиринта"""
x: int def __init__(self, x, y, is_wall=False):
y: int self.x = x
is_wall: bool = False self.y = y
is_start: bool = False self.is_wall = is_wall
is_exit: bool = False self.is_start = False
weight: int = 1 self.is_exit = False
def is_passable(self) -> bool: def is_passable(self):
return not self.is_wall 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: class Maze:
"""Лабиринт""" """Лабиринт"""
def __init__(self, width, height):
def __init__(self, width: int, height: int):
self.width = width self.width = width
self.height = height self.height = height
self._cells: List[List[Optional[Cell]]] = [[None for _ in range(width)] for _ in range(height)] self.cells = [[Cell(x, y) for x in range(width)] for y in range(height)]
self.start: Optional[Cell] = None self.start = None
self.exit: Optional[Cell] = None self.exit = None
def set_cell(self, x: int, y: int, cell: Cell) -> None: def get_cell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height: if 0 <= x < self.width and 0 <= y < self.height:
self._cells[y][x] = cell return self.cells[y][x]
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 None return None
def get_neighbors(self, cell: Cell) -> List[Cell]: def get_neighbors(self, cell):
neighbors = [] neighbors = []
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
for dx, dy in directions:
nx, ny = cell.x + dx, cell.y + dy nx, ny = cell.x + dx, cell.y + dy
neighbor = self.get_cell(nx, ny) nb = self.get_cell(nx, ny)
if neighbor and neighbor.is_passable(): if nb and nb.is_passable():
neighbors.append(neighbor) neighbors.append(nb)
return neighbors return neighbors
def get_all_cells(self) -> List[Cell]: def __str__(self):
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:
result = "" result = ""
for y in range(self.height): for y in range(self.height):
for x in range(self.width): for x in range(self.width):
@ -85,3 +62,121 @@ class Maze:
result += " " result += " "
result += "\n" result += "\n"
return result 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)