forked from UNN/2026-rff_mp
202 lines
6.5 KiB
Python
202 lines
6.5 KiB
Python
|
|
from builders import TextFileMazeBuilder
|
|||
|
|
from strategies import BFSStrategy, DFSStrategy, AStarStrategy
|
|||
|
|
from solver import MazeSolver
|
|||
|
|
from observers import ConsoleView
|
|||
|
|
from experiments import run_experiments, save_to_csv
|
|||
|
|
import random
|
|||
|
|
|
|||
|
|
WALL = '■'
|
|||
|
|
PASSAGE = ' '
|
|||
|
|
START = 'S'
|
|||
|
|
EXIT = 'E'
|
|||
|
|
|
|||
|
|
|
|||
|
|
def generate_maze_prim(size: int, filename: str, label: str):
|
|||
|
|
random.seed(hash(filename) % 10000)
|
|||
|
|
|
|||
|
|
grid = [[WALL for _ in range(size)] for _ in range(size)]
|
|||
|
|
|
|||
|
|
start_x, start_y = 1, 1
|
|||
|
|
grid[start_y][start_x] = PASSAGE
|
|||
|
|
|
|||
|
|
walls = []
|
|||
|
|
|
|||
|
|
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
|
|||
|
|
wall_x = start_x + dx
|
|||
|
|
wall_y = start_y + dy
|
|||
|
|
cell_x = start_x + 2*dx
|
|||
|
|
cell_y = start_y + 2*dy
|
|||
|
|
|
|||
|
|
if 0 <= cell_x < size and 0 <= cell_y < size:
|
|||
|
|
if grid[wall_y][wall_x] == WALL:
|
|||
|
|
walls.append((wall_x, wall_y, start_x, start_y, cell_x, cell_y))
|
|||
|
|
|
|||
|
|
while walls:
|
|||
|
|
idx = random.randint(0, len(walls) - 1)
|
|||
|
|
wall_x, wall_y, from_x, from_y, to_x, to_y = walls.pop(idx)
|
|||
|
|
|
|||
|
|
if grid[to_y][to_x] == WALL:
|
|||
|
|
grid[wall_y][wall_x] = PASSAGE
|
|||
|
|
grid[to_y][to_x] = PASSAGE
|
|||
|
|
|
|||
|
|
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
|
|||
|
|
new_wall_x = to_x + dx
|
|||
|
|
new_wall_y = to_y + dy
|
|||
|
|
new_cell_x = to_x + 2*dx
|
|||
|
|
new_cell_y = to_y + 2*dy
|
|||
|
|
|
|||
|
|
if 0 <= new_cell_x < size and 0 <= new_cell_y < size:
|
|||
|
|
if grid[new_wall_y][new_wall_x] == WALL:
|
|||
|
|
walls.append((new_wall_x, new_wall_y, to_x, to_y, new_cell_x, new_cell_y))
|
|||
|
|
|
|||
|
|
grid[1][1] = START
|
|||
|
|
grid[size-2][size-2] = EXIT
|
|||
|
|
|
|||
|
|
with open(filename, 'w', encoding='utf-8') as f:
|
|||
|
|
for row in grid:
|
|||
|
|
f.write(''.join(row) + '\n')
|
|||
|
|
|
|||
|
|
print(f"✓ {label}: {filename}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def generate_small_maze(filename='small_maze.txt'):
|
|||
|
|
maze = [
|
|||
|
|
"■■■■■■■■■■",
|
|||
|
|
"■S ■",
|
|||
|
|
"■ ■■■■ ■ ■",
|
|||
|
|
"■ ■ ■ ■ ■",
|
|||
|
|
"■ ■ ■■ ■ ■",
|
|||
|
|
"■ ■ ■ ■",
|
|||
|
|
"■ ■■■■■■ ■",
|
|||
|
|
"■ ■",
|
|||
|
|
"■ ■■■■■■■■",
|
|||
|
|
"■ E■",
|
|||
|
|
"■■■■■■■■■■"
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
with open(filename, 'w', encoding='utf-8') as f:
|
|||
|
|
f.write('\n'.join(maze))
|
|||
|
|
print(f"Маленький лабиринт 10x10: {filename}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def generate_no_exit_maze(filename='no_exit_maze.txt'):
|
|||
|
|
maze = [
|
|||
|
|
"■■■■■■■■■■",
|
|||
|
|
"■S ■",
|
|||
|
|
"■ ■■■■ ■ ■",
|
|||
|
|
"■ ■ ■ ■ ■",
|
|||
|
|
"■ ■ ■■ ■ ■",
|
|||
|
|
"■ ■ ■ ■",
|
|||
|
|
"■ ■■■■■■ ■",
|
|||
|
|
"■ ■",
|
|||
|
|
"■ ■■■■■■■■",
|
|||
|
|
"■ ■■■■■■",
|
|||
|
|
"■■■■■E■■■■"
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
with open(filename, 'w', encoding='utf-8') as f:
|
|||
|
|
f.write('\n'.join(maze))
|
|||
|
|
print(f"Лабиринт без выхода 10x10: {filename}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def generate_empty_maze(filename='empty_maze.txt'):
|
|||
|
|
size = 10
|
|||
|
|
maze = []
|
|||
|
|
maze.append(WALL * size)
|
|||
|
|
|
|||
|
|
for i in range(size - 2):
|
|||
|
|
if i == 0:
|
|||
|
|
row = WALL + START + PASSAGE * (size - 3) + WALL
|
|||
|
|
elif i == size - 3:
|
|||
|
|
row = WALL + PASSAGE * (size - 3) + EXIT + WALL
|
|||
|
|
else:
|
|||
|
|
row = WALL + PASSAGE * (size - 2) + WALL
|
|||
|
|
maze.append(row)
|
|||
|
|
|
|||
|
|
maze.append(WALL * size)
|
|||
|
|
|
|||
|
|
with open(filename, 'w', encoding='utf-8') as f:
|
|||
|
|
f.write('\n'.join(maze))
|
|||
|
|
print(f"Пустой лабиринт 10x10: {filename}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def main():
|
|||
|
|
print("=" * 60)
|
|||
|
|
print("ГЕНЕРАЦИЯ ЛАБИРИНТОВ")
|
|||
|
|
print("=" * 60)
|
|||
|
|
|
|||
|
|
generate_small_maze('small_maze.txt')
|
|||
|
|
generate_maze_prim(50, 'medium_maze.txt', 'Средний лабиринт 50x50')
|
|||
|
|
generate_maze_prim(100, 'large_maze.txt', 'Большой лабиринт 100x100')
|
|||
|
|
generate_no_exit_maze('no_exit_maze.txt')
|
|||
|
|
generate_empty_maze('empty_maze.txt')
|
|||
|
|
|
|||
|
|
print("\n" + "=" * 60)
|
|||
|
|
print("ДЕМОНСТРАЦИЯ НА МАЛЕНЬКОМ ЛАБИРИНТЕ")
|
|||
|
|
print("=" * 60)
|
|||
|
|
|
|||
|
|
builder = TextFileMazeBuilder()
|
|||
|
|
maze = builder.buildFromFile('small_maze.txt')
|
|||
|
|
|
|||
|
|
print(f"Размер: {maze.width}x{maze.height}")
|
|||
|
|
print(f"Старт: ({maze.start.x}, {maze.start.y})")
|
|||
|
|
print(f"Выход: ({maze.exit.x}, {maze.exit.y})")
|
|||
|
|
|
|||
|
|
view = ConsoleView()
|
|||
|
|
solver = MazeSolver(maze)
|
|||
|
|
solver.attach(view)
|
|||
|
|
|
|||
|
|
strategies = {
|
|||
|
|
"BFS (поиск в ширину)": BFSStrategy(),
|
|||
|
|
"DFS (поиск в глубину)": DFSStrategy(),
|
|||
|
|
"A*": AStarStrategy()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for name, strat in strategies.items():
|
|||
|
|
print(f"\n{'─' * 40}")
|
|||
|
|
print(f"Стратегия: {name}")
|
|||
|
|
|
|||
|
|
solver.setStrategy(strat)
|
|||
|
|
path, stats = solver.solve()
|
|||
|
|
|
|||
|
|
if path:
|
|||
|
|
print(f"Путь найден! Длина: {len(path)} шагов")
|
|||
|
|
print(f" Время: {stats.time_ms:.3f} мс | Посещено: {stats.visited_count}")
|
|||
|
|
view.render(maze, path=path)
|
|||
|
|
else:
|
|||
|
|
print("Путь не найден!")
|
|||
|
|
|
|||
|
|
# Эксперименты
|
|||
|
|
print("\n" + "=" * 60)
|
|||
|
|
print("ЭКСПЕРИМЕНТЫ НА ВСЕХ ЛАБИРИНТАХ")
|
|||
|
|
print("=" * 60)
|
|||
|
|
|
|||
|
|
test_mazes = {}
|
|||
|
|
maze_files = [
|
|||
|
|
("Маленький (10x10)", "small_maze.txt"),
|
|||
|
|
("Средний (50x50)", "medium_maze.txt"),
|
|||
|
|
("Большой (100x100)", "large_maze.txt"),
|
|||
|
|
("Без выхода (10x10)", "no_exit_maze.txt"),
|
|||
|
|
("Пустой (10x10)", "empty_maze.txt")
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
for name, filename in maze_files:
|
|||
|
|
try:
|
|||
|
|
test_mazes[name] = builder.buildFromFile(filename)
|
|||
|
|
m = test_mazes[name]
|
|||
|
|
print(f"{name} загружен ({m.width}x{m.height})")
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"Ошибка {name}: {e}")
|
|||
|
|
|
|||
|
|
if test_mazes:
|
|||
|
|
print(f"\nЗапуск тестов (по 3 прогона)...")
|
|||
|
|
results = run_experiments(test_mazes, strategies, runs=3)
|
|||
|
|
save_to_csv(results)
|
|||
|
|
|
|||
|
|
print("ГОТОВО! Графики: python visualize_results.py")
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|