Update Ezhovnd/2 задание.py

This commit is contained in:
Ezhovnd 2026-09-05 08:32:56 +00:00
parent fd367d2c1a
commit 46fd403eb8

View File

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