2026-rff_mp/GorkinMM/docs/data/2-nd-exercize/WayOutOfTheMaze.py
2026-05-25 00:52:58 +03:00

285 lines
10 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 csv
import time
import os
import random
from collections import deque
import heapq
import matplotlib.pyplot as plt
import pandas as pd
class Cell:
def __init__(self, x, y):
self.x = x
self.y = y
self.is_wall = False
self.is_start = False
self.is_exit = False
def isPassable(self):
return not self.is_wall
class Maze:
def __init__(self, width, height):
self.width = width
self.height = height
self.cells = []
self.start = None
self.exit = None
def getCell(self, x, y):
if 0 <= x < self.width and 0 <= y < self.height:
return self.cells[y][x]
return None
def getNeighbors(self, cell):
neighbors = []
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
neighbor = self.getCell(cell.x + dx, cell.y + dy)
if neighbor and neighbor.isPassable():
neighbors.append(neighbor)
return neighbors
class MazeBuilder:
def buildFromFile(self, filename):
raise NotImplementedError
class TextFileMazeBuilder(MazeBuilder):
def buildFromFile(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)
maze.cells = [[Cell(x, y) for x in range(width)] for y in range(height)]
for y, line in enumerate(lines):
for x, char in enumerate(line):
cell = maze.cells[y][x]
if char == '#':
cell.is_wall = True
elif char == 'S':
cell.is_start = True
maze.start = cell
elif char == 'E':
cell.is_exit = True
maze.exit = cell
if maze.start is None or maze.exit is None:
raise ValueError("В файле должны быть символы S и E")
return maze
class PathFindingStrategy:
def findPath(self, maze, start, exit):
raise NotImplementedError
class BFSStrategy(PathFindingStrategy):
def findPath(self, maze, start, exit):
queue = deque([start])
came_from = {start: None}
visited = set([start])
while queue:
current = queue.popleft()
if current == exit:
break
for neighbor in maze.getNeighbors(current):
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
came_from[neighbor] = current
path = self._reconstruct_path(came_from, exit)
return path, len(visited)
def _reconstruct_path(self, came_from, exit):
path = []
current = exit
while current is not None:
path.append(current)
current = came_from.get(current)
path.reverse()
return path if path and path[0] == came_from.get(exit) or path[0] == exit else []
class DFSStrategy(PathFindingStrategy):
def findPath(self, maze, start, exit):
stack = [start]
came_from = {start: None}
visited = set([start])
while stack:
current = stack.pop()
if current == exit:
break
for neighbor in maze.getNeighbors(current):
if neighbor not in visited:
visited.add(neighbor)
stack.append(neighbor)
came_from[neighbor] = current
path = self._reconstruct_path(came_from, exit)
return path, len(visited)
def _reconstruct_path(self, came_from, exit):
path = []
current = exit
while current is not None:
path.append(current)
current = came_from.get(current)
path.reverse()
return path
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):
open_set = []
counter = 0
heapq.heappush(open_set, (0, counter, start))
came_from = {start: None}
g_score = {start: 0}
visited = set()
while open_set:
_, _, current = heapq.heappop(open_set)
if current in visited:
continue
visited.add(current)
if current == exit:
break
for neighbor in maze.getNeighbors(current):
tentative_g = g_score[current] + 1
if neighbor not in g_score or tentative_g < g_score[neighbor]:
came_from[neighbor] = current
g_score[neighbor] = tentative_g
f_score = tentative_g + self.heuristic(neighbor, exit)
counter += 1
heapq.heappush(open_set, (f_score, counter, neighbor))
path = self._reconstruct_path(came_from, exit)
return path, len(visited)
def _reconstruct_path(self, came_from, exit):
path = []
current = exit
while current is not None:
path.append(current)
current = came_from.get(current)
path.reverse()
return path
class SearchStats:
def __init__(self, time_ms, visited_cells, path_length):
self.time_ms = time_ms
self.visited_cells = visited_cells
self.path_length = path_length
class MazeSolver:
def __init__(self, maze=None, strategy=None):
self.maze = maze
self.strategy = strategy
def setStrategy(self, strategy):
self.strategy = strategy
def solve(self):
if not self.maze or not self.strategy:
return None
start_time = time.perf_counter()
path, visited_count = self.strategy.findPath(self.maze, self.maze.start, self.maze.exit)
end_time = time.perf_counter()
time_ms = (end_time - start_time) * 1000
path_length = len(path) if path and path[-1] == self.maze.exit else 0
return SearchStats(round(time_ms, 4), visited_count, path_length)
def create_maze_with_walls(size, wall_probability=0.3):
maze = Maze(size, size)
maze.cells = [[Cell(x, y) for x in range(size)] for y in range(size)]
for y in range(size):
for x in range(size):
if random.random() < wall_probability:
maze.cells[y][x].is_wall = True
maze.start = maze.cells[0][0]
maze.exit = maze.cells[size-1][size-1]
maze.start.is_start = True
maze.exit.is_exit = True
maze.start.is_wall = False
maze.exit.is_wall = False
return maze
def create_empty_maze(size):
maze = Maze(size, size)
maze.cells = [[Cell(x, y) for x in range(size)] for y in range(size)]
maze.start = maze.cells[0][0]
maze.exit = maze.cells[size-1][size-1]
maze.start.is_start = True
maze.exit.is_exit = True
return maze
def create_no_exit_maze(size, wall_probability=0.3):
maze = create_maze_with_walls(size, wall_probability)
maze.exit.is_wall = True
return maze
def run_experiment():
maze_configs = {
"10x10_simple": {"size": 10, "type": "normal", "wall_prob": 0.1},
"50x50_with_deadends": {"size": 50, "type": "normal", "wall_prob": 0.3},
"100x100_complex": {"size": 100, "type": "normal", "wall_prob": 0.35},
"empty": {"size": 30, "type": "empty"},
"no_exit": {"size": 30, "type": "no_exit", "wall_prob": 0.3},
}
strategies = {
"BFS": BFSStrategy(),
"DFS": DFSStrategy(),
"AStar": AStarStrategy()
}
results = []
for maze_name, config in maze_configs.items():
size = config["size"]
maze_type = config["type"]
if maze_type == "empty":
maze = create_empty_maze(size)
elif maze_type == "no_exit":
maze = create_no_exit_maze(size, config.get("wall_prob", 0.3))
else:
maze = create_maze_with_walls(size, config.get("wall_prob", 0.3))
for strat_name, strategy in strategies.items():
solver = MazeSolver(maze, strategy)
times, visited_list, lengths = [], [], []
for _ in range(7):
stats = solver.solve()
times.append(stats.time_ms)
visited_list.append(stats.visited_cells)
lengths.append(stats.path_length)
avg_time = sum(times) / len(times)
avg_visited = sum(visited_list) / len(visited_list)
avg_length = sum(lengths) / len(lengths)
results.append([
maze_name, strat_name,
round(avg_time, 4),
int(avg_visited),
int(avg_length)
])
os.makedirs("results", exist_ok=True)
csv_path = "results/results.csv"
with open(csv_path, "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["лабиринт", "стратегия", "время_мс", "посещено_клеток", "длина_пути"])
writer.writerows(results)
df = pd.read_csv(csv_path)
plt.figure(figsize=(12, 6))
for strat in df["стратегия"].unique():
subset = df[df["стратегия"] == strat]
plt.plot(subset["лабиринт"], subset["время_мс"], marker='o', label=strat)
plt.title("Сравнение времени работы алгоритмов")
plt.xlabel("Лабиринт")
plt.ylabel("Время (мс)")
plt.legend()
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig("results/time_comparison.png")
plt.close()
plt.figure(figsize=(12, 6))
for strat in df["стратегия"].unique():
subset = df[df["стратегия"] == strat]
plt.plot(subset["лабиринт"], subset["посещено_клеток"], marker='o', label=strat)
plt.title("Количество посещённых клеток")
plt.xlabel("Лабиринт")
plt.ylabel("Посещено клеток")
plt.legend()
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig("results/visited_comparison.png")
plt.close()
if __name__ == "__main__":
run_experiment()