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()
|