2026-rff_mp/shahovaa/zadanie 2/scripts/generate_mazes.py
2026-05-19 22:39:51 +03:00

127 lines
3.3 KiB
Python

from __future__ import annotations
import random
from collections import deque
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MAZE_DIR = ROOT / "data" / "mazes"
def main() -> None:
generate_all()
def generate_all() -> None:
MAZE_DIR.mkdir(parents=True, exist_ok=True)
_write("small.txt", _small_maze())
_write("medium.txt", _perfect_maze(50, 50, seed=2026))
_write("large.txt", _perfect_maze(100, 100, seed=2027))
_write("empty.txt", _empty_maze(50, 50))
_write("no_exit.txt", _no_path_maze(30, 30))
def _write(filename: str, rows: list[str]) -> None:
(MAZE_DIR / filename).write_text("\n".join(rows) + "\n", encoding="utf-8")
def _small_maze() -> list[str]:
return [
"##########",
"#S #E#",
"# #### # #",
"# # # #",
"# # #### #",
"# # #",
"# ###### #",
"# #",
"######## #",
"##########",
]
def _empty_maze(width: int, height: int) -> list[str]:
grid = _bordered_grid(width, height, fill=" ")
grid[1][1] = "S"
grid[height - 2][width - 2] = "E"
return _to_rows(grid)
def _no_path_maze(width: int, height: int) -> list[str]:
grid = [["#" for _ in range(width)] for _ in range(height)]
grid[1][1] = "S"
grid[height - 2][width - 2] = "E"
return _to_rows(grid)
def _perfect_maze(width: int, height: int, seed: int) -> list[str]:
if width < 5 or height < 5:
raise ValueError("Maze must be at least 5x5")
randomizer = random.Random(seed)
grid = [["#" for _ in range(width)] for _ in range(height)]
start = (1, 1)
stack = [start]
grid[start[1]][start[0]] = " "
while stack:
x, y = stack[-1]
candidates = []
for dx, dy in ((0, -2), (2, 0), (0, 2), (-2, 0)):
nx, ny = x + dx, y + dy
if 1 <= nx < width - 1 and 1 <= ny < height - 1 and grid[ny][nx] == "#":
candidates.append((nx, ny, dx, dy))
if not candidates:
stack.pop()
continue
nx, ny, dx, dy = randomizer.choice(candidates)
grid[y + dy // 2][x + dx // 2] = " "
grid[ny][nx] = " "
stack.append((nx, ny))
exit_x, exit_y = _farthest_open_cell(grid, start)
grid[start[1]][start[0]] = "S"
grid[exit_y][exit_x] = "E"
return _to_rows(grid)
def _farthest_open_cell(grid: list[list[str]], start: tuple[int, int]) -> tuple[int, int]:
queue = deque([start])
distances = {start: 0}
farthest = start
while queue:
x, y = queue.popleft()
if distances[(x, y)] > distances[farthest]:
farthest = (x, y)
for dx, dy in ((0, -1), (1, 0), (0, 1), (-1, 0)):
nx, ny = x + dx, y + dy
if (nx, ny) not in distances and grid[ny][nx] != "#":
distances[(nx, ny)] = distances[(x, y)] + 1
queue.append((nx, ny))
return farthest
def _bordered_grid(width: int, height: int, fill: str) -> list[list[str]]:
grid = [[fill for _ in range(width)] for _ in range(height)]
for x in range(width):
grid[0][x] = "#"
grid[height - 1][x] = "#"
for y in range(height):
grid[y][0] = "#"
grid[y][width - 1] = "#"
return grid
def _to_rows(grid: list[list[str]]) -> list[str]:
return ["".join(row) for row in grid]
if __name__ == "__main__":
main()