22 - python & all web labs
This commit is contained in:
@@ -1,65 +1,239 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import heapq
|
||||
import os
|
||||
import random
|
||||
|
||||
|
||||
def create_file():
|
||||
n = random.randint(5, 10)
|
||||
maze = []
|
||||
for _ in range(n):
|
||||
row = "".join(["M" if random.random() < 0.3 else " " for _ in range(n)])
|
||||
maze.append(row)
|
||||
with open("maze.txt", "w") as f:
|
||||
for row in maze:
|
||||
f.write(row + "\n")
|
||||
return n
|
||||
WALL = "M"
|
||||
|
||||
|
||||
def read_maze():
|
||||
with open("maze.txt") as f:
|
||||
maze = [list(line.rstrip("\n")) for line in f]
|
||||
return maze
|
||||
def _maze_path() -> str:
|
||||
return os.path.join(os.path.dirname(__file__), "maze.txt")
|
||||
|
||||
|
||||
def solve(maze, x, y, xc, yc, visited, length, best):
|
||||
n = len(maze)
|
||||
if x < 0 or y < 0 or x >= n or y >= n:
|
||||
return
|
||||
if maze[x][y] == "M" or visited[x][y]:
|
||||
return
|
||||
if length >= best[0]:
|
||||
return
|
||||
if x == xc and y == yc:
|
||||
best[0] = min(best[0], length)
|
||||
return
|
||||
visited[x][y] = True
|
||||
solve(maze, x + 1, y, xc, yc, visited, length + 1, best)
|
||||
solve(maze, x - 1, y, xc, yc, visited, length + 1, best)
|
||||
solve(maze, x, y + 1, xc, yc, visited, length + 1, best)
|
||||
solve(maze, x, y - 1, xc, yc, visited, length + 1, best)
|
||||
visited[x][y] = False
|
||||
def _read_int(
|
||||
prompt: str, *, min_value: int | None = None, max_value: int | None = None
|
||||
) -> int:
|
||||
while True:
|
||||
try:
|
||||
s = input(prompt).strip()
|
||||
except EOFError:
|
||||
raise
|
||||
try:
|
||||
v = int(s)
|
||||
except ValueError:
|
||||
print("Ошибка: нужно целое число.")
|
||||
continue
|
||||
if min_value is not None and v < min_value:
|
||||
print(f"Ошибка: число должно быть не меньше {min_value}.")
|
||||
continue
|
||||
if max_value is not None and v > max_value:
|
||||
print(f"Ошибка: число должно быть не больше {max_value}.")
|
||||
continue
|
||||
return v
|
||||
|
||||
|
||||
while True:
|
||||
print("\n1 - создать лабиринт\n2 - решить\n0 - выход")
|
||||
try:
|
||||
c = input("Выбор: ")
|
||||
if c == "1":
|
||||
n = create_file()
|
||||
print("Файл создан. Размер:", n)
|
||||
elif c == "2":
|
||||
maze = read_maze()
|
||||
n = len(maze)
|
||||
x = int(input("Старт X: "))
|
||||
y = int(input("Старт Y: "))
|
||||
xc = int(input("Сыр X: "))
|
||||
yc = int(input("Сыр Y: "))
|
||||
visited = [[False] * n for _ in range(n)]
|
||||
best = [10**9]
|
||||
solve(maze, x, y, xc, yc, visited, 0, best)
|
||||
if best[0] == 10**9:
|
||||
print("Путь не найден")
|
||||
def _normalize_grid(rows: list[str], n: int) -> list[str]:
|
||||
if len(rows) != n:
|
||||
raise ValueError(f"Ожидалось {n} строк, получено {len(rows)}.")
|
||||
grid = [(r[:n] + (" " * max(0, n - len(r)))) for r in rows]
|
||||
return grid
|
||||
|
||||
|
||||
def read_maze_from_file(n: int) -> list[str]:
|
||||
path = _maze_path()
|
||||
if not os.path.exists(path):
|
||||
raise FileNotFoundError("Файл maze.txt не найден.")
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
rows = [ln.rstrip("\n") for ln in f.readlines()]
|
||||
rows = rows[:n]
|
||||
return _normalize_grid(rows, n)
|
||||
|
||||
|
||||
def write_maze_to_file(grid: list[str]) -> None:
|
||||
path = _maze_path()
|
||||
with open(path, "w", encoding="utf-8") as f:
|
||||
for r in grid:
|
||||
f.write(r + "\n")
|
||||
|
||||
|
||||
def print_maze_with_coords(grid: list[str]) -> None:
|
||||
n = len(grid)
|
||||
|
||||
# Заголовок столбцов
|
||||
print(" ", end="")
|
||||
for j in range(n):
|
||||
print(f"{j + 1:2}", end=" ")
|
||||
print()
|
||||
|
||||
# Лабиринт с номерами строк
|
||||
for i, row in enumerate(grid):
|
||||
print(f"{i + 1:2} ", end="")
|
||||
for cell in row:
|
||||
print(f" {cell if cell != ' ' else '.'}", end=" ")
|
||||
print()
|
||||
|
||||
|
||||
def _is_exit(grid: list[str], i: int, j: int) -> bool:
|
||||
n = len(grid)
|
||||
return (i == 0 or j == 0 or i == n - 1 or j == n - 1) and grid[i][j] != WALL
|
||||
|
||||
|
||||
def a_star_shortest_path(grid: list[str], start: tuple[int, int]) -> str:
|
||||
n = len(grid)
|
||||
sx, sy = start
|
||||
if not (0 <= sx < n and 0 <= sy < n):
|
||||
raise ValueError("Стартовые координаты вне лабиринта.")
|
||||
if grid[sx][sy] == WALL:
|
||||
raise ValueError("Стартовая клетка является стеной.")
|
||||
|
||||
exits = [(i, j) for i in range(n) for j in range(n) if _is_exit(grid, i, j)]
|
||||
if not exits:
|
||||
raise ValueError("В лабиринте нет выходов.")
|
||||
if _is_exit(grid, sx, sy):
|
||||
return ""
|
||||
|
||||
def h(i: int, j: int) -> int:
|
||||
return min(abs(i - ei) + abs(j - ej) for ei, ej in exits)
|
||||
|
||||
inf = 10**9
|
||||
gscore = [[inf] * n for _ in range(n)]
|
||||
gscore[sx][sy] = 0
|
||||
|
||||
prev: list[list[tuple[int, int] | None]] = [[None] * n for _ in range(n)]
|
||||
prev_move: list[list[str | None]] = [[None] * n for _ in range(n)]
|
||||
|
||||
heap: list[tuple[int, int, int, int]] = [(h(sx, sy), 0, sx, sy)]
|
||||
closed = [[False] * n for _ in range(n)]
|
||||
dirs = ((0, -1, "L"), (0, 1, "R"), (-1, 0, "U"), (1, 0, "D"))
|
||||
|
||||
while heap:
|
||||
_, g, i, j = heapq.heappop(heap)
|
||||
if closed[i][j]:
|
||||
continue
|
||||
closed[i][j] = True
|
||||
|
||||
if _is_exit(grid, i, j):
|
||||
path: list[str] = []
|
||||
ci, cj = i, j
|
||||
while (ci, cj) != (sx, sy):
|
||||
mv = prev_move[ci][cj]
|
||||
p = prev[ci][cj]
|
||||
if mv is None or p is None:
|
||||
raise RuntimeError("Ошибка восстановления пути.")
|
||||
path.append(mv)
|
||||
ci, cj = p
|
||||
path.reverse()
|
||||
return "".join(path)
|
||||
|
||||
for di, dj, mv in dirs:
|
||||
ni, nj = i + di, j + dj
|
||||
if not (0 <= ni < n and 0 <= nj < n):
|
||||
continue
|
||||
if grid[ni][nj] == WALL:
|
||||
continue
|
||||
ng = g + 1
|
||||
if ng < gscore[ni][nj]:
|
||||
gscore[ni][nj] = ng
|
||||
prev[ni][nj] = (i, j)
|
||||
prev_move[ni][nj] = mv
|
||||
heapq.heappush(heap, (ng + h(ni, nj), ng, ni, nj))
|
||||
|
||||
raise ValueError("Пути к выходу нет.")
|
||||
|
||||
|
||||
def _random_grid(n: int, wall_percent: int) -> list[str]:
|
||||
p = wall_percent / 100.0
|
||||
rows: list[str] = []
|
||||
for i in range(n):
|
||||
row_chars: list[str] = []
|
||||
for j in range(n):
|
||||
if random.random() < p:
|
||||
row_chars.append(WALL)
|
||||
else:
|
||||
print("Кратчайший путь:", best[0])
|
||||
elif c == "0":
|
||||
break
|
||||
except:
|
||||
print("Ошибка ввода")
|
||||
row_chars.append(" ")
|
||||
rows.append("".join(row_chars))
|
||||
return rows
|
||||
|
||||
|
||||
def _random_passage_cell(grid: list[str]) -> tuple[int, int]:
|
||||
n = len(grid)
|
||||
cells = [(i, j) for i in range(n) for j in range(n) if grid[i][j] != WALL]
|
||||
if not cells:
|
||||
raise ValueError("В лабиринте нет проходов.")
|
||||
return random.choice(cells)
|
||||
|
||||
|
||||
def generate_maze_file_interactive() -> None:
|
||||
n = _read_int("Введите N (1..15): ", min_value=1, max_value=15)
|
||||
wall_percent = _read_int(
|
||||
"Введите процент стен M (0..90): ", min_value=0, max_value=90
|
||||
)
|
||||
tries = 0
|
||||
while True:
|
||||
tries += 1
|
||||
grid = _random_grid(n, wall_percent)
|
||||
for j in range(n):
|
||||
if random.random() < 0.25:
|
||||
grid[0] = grid[0][:j] + " " + grid[0][j + 1 :]
|
||||
start = _random_passage_cell(grid)
|
||||
try:
|
||||
_ = a_star_shortest_path(grid, start)
|
||||
write_maze_to_file(grid)
|
||||
print(
|
||||
f"Файл maze.txt создан. N={n}. Старт: X={start[0] + 1} Y={start[1] + 1}."
|
||||
)
|
||||
print_maze_with_coords(grid)
|
||||
return
|
||||
except Exception:
|
||||
if tries >= 500:
|
||||
raise ValueError("Не удалось сгенерировать лабиринт.")
|
||||
|
||||
|
||||
def print_maze_file_interactive() -> None:
|
||||
n = _read_int("Введите N (1..15): ", min_value=1, max_value=15)
|
||||
grid = read_maze_from_file(n)
|
||||
print("maze.txt:")
|
||||
print_maze_with_coords(grid)
|
||||
|
||||
|
||||
def solve_from_file_interactive() -> None:
|
||||
n = _read_int("Введите N (1..15): ", min_value=1, max_value=15)
|
||||
grid = read_maze_from_file(n)
|
||||
print_maze_with_coords(grid)
|
||||
x = _read_int("Введите строку (1..N): ", min_value=1, max_value=n)
|
||||
y = _read_int("Введите колонку (1..N): ", min_value=1, max_value=n)
|
||||
start = (x - 1, y - 1)
|
||||
path = a_star_shortest_path(grid, start)
|
||||
print("Путь:", path)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
while True:
|
||||
print()
|
||||
print("Меню:")
|
||||
print("1) Создать лабиринт")
|
||||
print("2) Показать лабиринт")
|
||||
print("3) Найти путь")
|
||||
print("0) Выход")
|
||||
try:
|
||||
choice = input("Выберите пункт: ").strip()
|
||||
except EOFError:
|
||||
return
|
||||
try:
|
||||
if choice == "0":
|
||||
return
|
||||
if choice == "1":
|
||||
generate_maze_file_interactive()
|
||||
elif choice == "2":
|
||||
print_maze_file_interactive()
|
||||
elif choice == "3":
|
||||
solve_from_file_interactive()
|
||||
else:
|
||||
print("Неизвестный пункт меню.")
|
||||
except Exception as e:
|
||||
print(f"Ошибка: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user