""" generate_mazes.py - генерирует тестовые лабиринты в mazes/. Состав: small_10x10.txt - маленький с простым путём medium_50x50.txt - средний с тупиками (DFS-генератор) large_100x100.txt - большой запутанный (DFS-генератор) empty_30x30.txt - без стен внутри (только периметр) nopath_15x15.txt - без пути от S до E (выход замурован) weighted_30x30.txt - со взвешенными клетками (асфальт/песок/болото) """ import os import random random.seed(2024) MAZES_DIR = "mazes" os.makedirs(MAZES_DIR, exist_ok=True) def write(name, lines): path = os.path.join(MAZES_DIR, name) with open(path, "w", encoding="utf-8") as f: f.write("\n".join(lines) + "\n") print("written:", path, f"({len(lines)} строк, ширина {len(lines[0])})") def make_small(): """Маленький лабиринт 10x10, ручной.""" raw = [ "##########", "#S #", "# ###### #", "# # #", "###### # #", "# # # #", "# ## # # #", "# # # #", "# ##### E", "##########", ] write("small_10x10.txt", raw) def _carve_perfect_maze(w, h, rng): """Генератор «идеального» лабиринта DFS (recursive backtracker), итеративный - чтобы не упасть в RecursionError на больших размерах.""" grid = [['#'] * w for _ in range(h)] grid[1][1] = ' ' stack = [(1, 1)] while stack: x, y = stack[-1] dirs = [(0, -2), (0, 2), (-2, 0), (2, 0)] rng.shuffle(dirs) carved = False for dx, dy in dirs: nx, ny = x + dx, y + dy if 0 < nx < w - 1 and 0 < ny < h - 1 and grid[ny][nx] == '#': grid[y + dy // 2][x + dx // 2] = ' ' grid[ny][nx] = ' ' stack.append((nx, ny)) carved = True break if not carved: stack.pop() return grid def make_with_generator(name, w, h): """Создаёт перфектный лабиринт и расставляет S/E в противоположных углах.""" rng = random.Random(hash(name) & 0xFFFF) grid = _carve_perfect_maze(w, h, rng) grid[1][1] = 'S' grid[h - 2][w - 2] = 'E' lines = ["".join(row) for row in grid] write(name, lines) def make_empty(name, w, h): """Пустая комната - только периметр.""" lines = [] for y in range(h): if y == 0 or y == h - 1: lines.append('#' * w) else: lines.append('#' + ' ' * (w - 2) + '#') # старт в левом верхнем углу, выход в правом нижнем row = list(lines[1]); row[1] = 'S'; lines[1] = "".join(row) row = list(lines[h - 2]); row[w - 2] = 'E'; lines[h - 2] = "".join(row) write(name, lines) def make_nopath(name, w=15, h=15): """Лабиринт, в котором выход замурован - пути нет.""" lines = ['#' * w] for y in range(1, h - 1): lines.append('#' + ' ' * (w - 2) + '#') lines.append('#' * w) # S слева сверху row = list(lines[1]); row[1] = 'S'; lines[1] = "".join(row) # E в правой нижней клетке, но обнесён стенами с двух сторон # делаем «коробочку» 3x3 вокруг E с одним зазором, который мы тут же закроем ex, ey = w - 2, h - 2 # сначала откроем коробочку из стен 1 клетка по периметру вокруг E # построим коробочку: на (ex-1, ey-1)..(ex+1, ey+1) поставим '#' кроме E for yy in range(ey - 1, ey + 2): for xx in range(ex - 1, ex + 2): if 0 <= xx < w and 0 <= yy < h and not (xx == ex and yy == ey): row = list(lines[yy]); row[xx] = '#'; lines[yy] = "".join(row) row = list(lines[ey]); row[ex] = 'E'; lines[ey] = "".join(row) write(name, lines) def make_weighted(name, w=30, h=30): """Перфектный лабиринт + случайные взвешенные клетки на проходимых местах.""" rng = random.Random(7) grid = _carve_perfect_maze(w | 1, h | 1, rng) # Перекрасим часть проходов в '.', ',' и '~' for y, row in enumerate(grid): for x, ch in enumerate(row): if ch == ' ': r = rng.random() if r < 0.65: grid[y][x] = ' ' # асфальт (1) elif r < 0.90: grid[y][x] = ',' # песок (2) else: grid[y][x] = '~' # болото (3) grid[1][1] = 'S' grid[len(grid) - 2][len(grid[0]) - 2] = 'E' lines = ["".join(row) for row in grid] write(name, lines) def main(): make_small() make_with_generator("medium_51x51.txt", 51, 51) make_with_generator("large_101x101.txt", 101, 101) make_empty("empty_30x30.txt", 30, 30) make_nopath("nopath_15x15.txt", 15, 15) make_weighted("weighted_31x31.txt", 31, 31) if __name__ == "__main__": main()