[2] maze #260

Merged
kit8nino merged 7 commits from SobolevNS/2026-rff_mp:02 into develop 2026-05-30 12:02:02 +00:00
2 changed files with 165 additions and 0 deletions
Showing only changes of commit 313fe75c36 - Show all commits

View File

@ -0,0 +1,146 @@
"""
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()

View File

@ -0,0 +1,19 @@
"""generate_weighted_choice.py - создаёт лабиринт, где Dijkstra/A* реально
обходят 'болото' и находят более дешёвый путь, чем BFS."""
W, H = 21, 13
grid = [[' '] * W for _ in range(H)]
# периметр
for x in range(W):
grid[0][x] = '#'; grid[H-1][x] = '#'
for y in range(H):
grid[y][0] = '#'; grid[y][W-1] = '#'
# центральное болото 5х5 (вес 3)
for y in range(4, 9):
for x in range(8, 13):
grid[y][x] = '~'
# старт слева в центре, выход справа в центре
grid[H//2][1] = 'S'
grid[H//2][W-2] = 'E'
with open('mazes/weighted_choice.txt','w') as f:
f.write('\n'.join(''.join(row) for row in grid) + '\n')
print(open('mazes/weighted_choice.txt').read())