2026-rff_mp/dyachenkoas/docs/data2/main.py

425 lines
14 KiB
Python
Raw Normal View History

import sys
import os
import time
import csv
from collections import deque
import heapq
import matplotlib.pyplot as plt
import numpy as np
# ========== Модель данных ==========
class Tile:
def __init__(self, x, y):
self._x = x
self._y = y
self._wall = False
self._entry = False
self._goal = False
@property
def x(self): return self._x
@property
def y(self): return self._y
@property
def is_wall(self): return self._wall
@is_wall.setter
def is_wall(self, value): self._wall = value
@property
def is_entry(self): return self._entry
@is_entry.setter
def is_entry(self, value): self._entry = value
@property
def is_goal(self): return self._goal
@is_goal.setter
def is_goal(self, value): self._goal = value
def can_walk(self):
return not self._wall
class Labyrinth:
def __init__(self, width, height):
self._width = width
self._height = height
self._grid = [[Tile(x, y) for x in range(width)] for y in range(height)]
self._start = None
self._exit = None
@property
def width(self): return self._width
@property
def height(self): return self._height
@property
def start(self): return self._start
@property
def exit(self): return self._exit
def tile_at(self, x, y):
if 0 <= x < self._width and 0 <= y < self._height:
return self._grid[y][x]
return None
def configure_tile(self, x, y, kind):
tile = self.tile_at(x, y)
if tile is None:
return
if kind == 'wall':
tile.is_wall = True
elif kind == 'entry':
if self._start:
self._start.is_entry = False
tile.is_entry = True
tile.is_wall = False
self._start = tile
elif kind == 'goal':
if self._exit:
self._exit.is_goal = False
tile.is_goal = True
tile.is_wall = False
self._exit = tile
elif kind == 'floor':
tile.is_wall = False
def neighbours(self, tile):
res = []
for dx, dy in ((0, -1), (0, 1), (-1, 0), (1, 0)):
nb = self.tile_at(tile.x + dx, tile.y + dy)
if nb and nb.can_walk():
res.append(nb)
return res
# ========== Загрузка из файла ==========
class TextLabyrinthBuilder:
def build(self, filename):
with open(filename, 'r', encoding='utf-8') as f:
lines = [line.rstrip('\n') for line in f]
h = len(lines)
w = max(len(l) for l in lines) if h else 0
lab = Labyrinth(w, h)
for y, row in enumerate(lines):
for x, ch in enumerate(row):
if ch == '#':
lab.configure_tile(x, y, 'wall')
elif ch == 'S':
lab.configure_tile(x, y, 'entry')
elif ch == 'E':
lab.configure_tile(x, y, 'goal')
elif ch == ' ':
lab.configure_tile(x, y, 'floor')
return lab
# ========== Алгоритмы поиска ==========
class BFS_Pathfinder:
def find_path(self, lab, start, goal):
if goal is None:
self._visited = 0
return []
q = deque([start])
preds = {start: None}
seen = {start}
while q:
cur = q.popleft()
if cur == goal:
self._visited = len(seen)
return self._build_path(preds, start, goal)
for nb in lab.neighbours(cur):
if nb not in seen:
seen.add(nb)
preds[nb] = cur
q.append(nb)
self._visited = len(seen)
return []
def _build_path(self, preds, start, goal):
path = []
cur = goal
while cur is not None:
path.append(cur)
cur = preds.get(cur)
path.reverse()
return path
@property
def visited_count(self):
return getattr(self, '_visited', 0)
class DFS_Pathfinder:
def find_path(self, lab, start, goal):
if goal is None:
self._visited = 0
return []
stack = [start]
preds = {start: None}
seen = {start}
while stack:
cur = stack.pop()
if cur == goal:
self._visited = len(seen)
return self._build_path(preds, start, goal)
for nb in lab.neighbours(cur):
if nb not in seen:
seen.add(nb)
preds[nb] = cur
stack.append(nb)
self._visited = len(seen)
return []
def _build_path(self, preds, start, goal):
path = []
cur = goal
while cur is not None:
path.append(cur)
cur = preds.get(cur)
path.reverse()
return path
@property
def visited_count(self):
return getattr(self, '_visited', 0)
class AStar_Pathfinder:
def _heuristic(self, a, b):
return abs(a.x - b.x) + abs(a.y - b.y)
def find_path(self, lab, start, goal):
if goal is None:
self._visited = 0
return []
heap = []
cnt = 0
f_start = self._heuristic(start, goal)
heapq.heappush(heap, (f_start, cnt, start))
cnt += 1
preds = {}
g = {start: 0}
f = {start: f_start}
seen = set()
while heap:
cur_f, _, cur = heapq.heappop(heap)
seen.add(cur)
if cur == goal:
self._visited = len(seen)
return self._build_path(preds, start, goal)
if cur_f > f.get(cur, float('inf')):
continue
for nb in lab.neighbours(cur):
tent_g = g[cur] + 1
if tent_g < g.get(nb, float('inf')):
preds[nb] = cur
g[nb] = tent_g
new_f = tent_g + self._heuristic(nb, goal)
f[nb] = new_f
heapq.heappush(heap, (new_f, cnt, nb))
cnt += 1
self._visited = len(seen)
return []
def _build_path(self, preds, start, goal):
path = []
cur = goal
while cur is not None:
path.append(cur)
cur = preds.get(cur)
path.reverse()
return path
@property
def visited_count(self):
return getattr(self, '_visited', 0)
class LabyrinthSolver:
def __init__(self, lab):
self._lab = lab
self._strategy = None
def set_strategy(self, strategy):
self._strategy = strategy
def solve(self):
start_t = time.perf_counter()
path = self._strategy.find_path(self._lab, self._lab.start, self._lab.exit)
elapsed = (time.perf_counter() - start_t) * 1000
return {
'time_ms': elapsed,
'visited': self._strategy.visited_count,
'length': len(path)
}
# ========== Графики ==========
def create_charts(results, save_filename='maze_performance.png'):
mazes = list(set([r['maze'] for r in results]))
strategies = ['BFS', 'DFS', 'AStar']
data = {s: {m: None for m in mazes} for s in strategies}
for r in results:
data[r['strategy']][r['maze']] = r
times = {s: [data[s][m]['time_ms'] for m in mazes] for s in strategies}
visited = {s: [data[s][m]['visited_cells'] for m in mazes] for s in strategies}
lengths = {s: [data[s][m]['path_length'] for m in mazes] for s in strategies}
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
x = np.arange(len(mazes))
width = 0.25
# Время
axes[0].bar(x - width, times['BFS'], width, label='BFS', color='blue', alpha=0.7)
axes[0].bar(x, times['DFS'], width, label='DFS', color='green', alpha=0.7)
axes[0].bar(x + width, times['AStar'], width, label='A*', color='red', alpha=0.7)
axes[0].set_xlabel('Лабиринты')
axes[0].set_ylabel('Время (мс)')
axes[0].set_title('Время выполнения алгоритмов')
axes[0].set_xticks(x)
axes[0].set_xticklabels(mazes, rotation=15, ha='right', fontsize=8)
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# Посещённые клетки
axes[1].bar(x - width, visited['BFS'], width, label='BFS', color='blue', alpha=0.7)
axes[1].bar(x, visited['DFS'], width, label='DFS', color='green', alpha=0.7)
axes[1].bar(x + width, visited['AStar'], width, label='A*', color='red', alpha=0.7)
axes[1].set_xlabel('Лабиринты')
axes[1].set_ylabel('Количество клеток')
axes[1].set_title('Посещённые клетки')
axes[1].set_xticks(x)
axes[1].set_xticklabels(mazes, rotation=15, ha='right', fontsize=8)
axes[1].legend()
axes[1].grid(True, alpha=0.3)
# Длина пути
axes[2].bar(x - width, lengths['BFS'], width, label='BFS', color='blue', alpha=0.7)
axes[2].bar(x, lengths['DFS'], width, label='DFS', color='green', alpha=0.7)
axes[2].bar(x + width, lengths['AStar'], width, label='A*', color='red', alpha=0.7)
axes[2].set_xlabel('Лабиринты')
axes[2].set_ylabel('Длина пути')
axes[2].set_title('Длина найденного пути')
axes[2].set_xticks(x)
axes[2].set_xticklabels(mazes, rotation=15, ha='right', fontsize=8)
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.suptitle('Сравнение алгоритмов поиска пути', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.savefig(save_filename, dpi=300, bbox_inches='tight')
print(f"\nГрафик сохранён в файл: {save_filename}")
plt.show()
# ========== Основная программа ==========
def main():
print('\n' + '=' * 60)
print('ПОИСК ВЫХОДА ИЗ ЛАБИРИНТА')
print('Сравнение алгоритмов BFS, DFS и A*')
print('=' * 60)
# Находим все txt файлы в текущей директории
maze_files = [f for f in os.listdir('.') if f.endswith('.txt') and f.startswith('maze_')]
if not maze_files:
print("\nНет файлов с лабиринтами (maze_*.txt)")
return
print(f"\nНайдено лабиринтов: {len(maze_files)}")
for f in maze_files:
print(f" - {f}")
strategies = [
('BFS', BFS_Pathfinder()),
('DFS', DFS_Pathfinder()),
('AStar', AStar_Pathfinder())
]
all_results = []
builder = TextLabyrinthBuilder()
for maze_file in sorted(maze_files):
maze_name = maze_file.replace('.txt', '').replace('maze_', '').replace('_', ' ').title()
print(f"\nОбработка лабиринта: {maze_name}")
lab = builder.build(maze_file)
for sname, strat in strategies:
solver = LabyrinthSolver(lab)
solver.set_strategy(strat)
stats = solver.solve()
all_results.append({
'maze': maze_name,
'strategy': sname,
'time_ms': stats['time_ms'],
'visited_cells': stats['visited'],
'path_length': stats['length']
})
status = "Найден" if stats['length'] > 0 else "Не найден"
print(f" {sname}: {status} (время: {stats['time_ms']:.3f} мс, посещено: {stats['visited']}, длина: {stats['length']})")
# Сохранение в CSV
print('\n' + '=' * 60)
print('СОХРАНЕНИЕ РЕЗУЛЬТАТОВ В CSV')
print('=' * 60)
csv_filename = 'experiment_results.csv'
with open(csv_filename, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['лабиринт', 'стратегия', 'время_мс', 'посещено_клеток', 'длина_пути']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
for result in all_results:
writer.writerow({
'лабиринт': result['maze'],
'стратегия': result['strategy'],
'время_мс': f"{result['time_ms']:.3f}",
'посещено_клеток': result['visited_cells'],
'длина_пути': result['path_length']
})
print(f"\nРезультаты сохранены в файл: {csv_filename}")
# Вывод таблицы в консоль
print('\n' + '=' * 100)
print('ТАБЛИЦА РЕЗУЛЬТАТОВ')
print('=' * 100)
print(f"{'Лабиринт':<20} {'Стратегия':<10} {'Время (мс)':<12} {'Посещено':<12} {'Длина пути':<12}")
print('-' * 100)
for r in all_results:
print(f"{r['maze']:<20} {r['strategy']:<10} {r['time_ms']:<12.3f} {r['visited_cells']:<12.0f} {r['path_length']:<12.0f}")
print('=' * 100)
# Создание графика
print('\nСОЗДАНИЕ ГРАФИКА...')
create_charts(all_results, 'maze_performance.png')
# Статистика
print('\n' + '=' * 60)
print('СТАТИСТИКА')
print('=' * 60)
for strategy in ['BFS', 'DFS', 'AStar']:
strategy_results = [r for r in all_results if r['strategy'] == strategy and r['path_length'] > 0]
if strategy_results:
avg_time = sum(r['time_ms'] for r in strategy_results) / len(strategy_results)
avg_visited = sum(r['visited_cells'] for r in strategy_results) / len(strategy_results)
avg_length = sum(r['path_length'] for r in strategy_results) / len(strategy_results)
print(f'\n{strategy}:')
print(f' Среднее время: {avg_time:.3f} мс')
print(f' Среднее посещено: {avg_visited:.0f} клеток')
print(f' Средняя длина пути: {avg_length:.1f}')
print('\n' + '=' * 60)
print('ГОТОВО')
print(f'Результаты сохранены в:')
print(f' - {csv_filename} (таблица)')
print(f' - maze_performance.png (график)')
print('=' * 60)
if __name__ == '__main__':
main()