-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgrid.py
More file actions
144 lines (103 loc) · 3.8 KB
/
grid.py
File metadata and controls
144 lines (103 loc) · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import os
import random
from datetime import datetime
from collections import deque
EMPTY = "."
OBSTACLE = "#"
START = "S"
GOAL = "G"
SAMPLE = "P"
VISITED_SPECIAL = "X"
def in_bounds(rows, cols, r, c):
return 0 <= r < rows and 0 <= c < cols
def get_neighbors(rows, cols, r, c):
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
neighbors = []
for dr, dc in directions:
nr, nc = r + dr, c + dc
if in_bounds(rows, cols, nr, nc):
neighbors.append((nr, nc))
return neighbors
def reachable_from(grid, start):
rows = len(grid)
cols = len(grid[0])
queue = deque([start])
visited = {start}
while queue:
current = queue.popleft()
for nr, nc in get_neighbors(rows, cols, current[0], current[1]):
if (nr, nc) not in visited and grid[nr][nc] != OBSTACLE:
visited.add((nr, nc))
queue.append((nr, nc))
return visited
def basic_reachable(grid, start, goal):
return goal in reachable_from(grid, start)
def samples_safely_reachable(grid, start, goal, sample_positions):
reachable_from_start = reachable_from(grid, start)
reachable_from_goal = reachable_from(grid, goal)
if goal not in reachable_from_start:
return False
for sample in sample_positions:
if sample not in reachable_from_start:
return False
if sample not in reachable_from_goal:
return False
return True
def generate_random_positions(rows, cols):
start = (random.randint(0, rows - 1), random.randint(0, cols - 1))
goal = start
while goal == start:
goal = (random.randint(0, rows - 1), random.randint(0, cols - 1))
return start, goal
def generate_solvable_grid(rows, cols, obstacle_count, sample_count=0):
while True:
grid = [[EMPTY for _ in range(cols)] for _ in range(rows)]
start, goal = generate_random_positions(rows, cols)
grid[start[0]][start[1]] = START
grid[goal[0]][goal[1]] = GOAL
available_cells = [
(r, c)
for r in range(rows)
for c in range(cols)
if (r, c) != start and (r, c) != goal
]
max_obstacles = len(available_cells)
if obstacle_count > max_obstacles:
raise ValueError(f"Too many obstacles. Maximum is {max_obstacles}.")
obstacle_positions = random.sample(available_cells, obstacle_count)
for r, c in obstacle_positions:
grid[r][c] = OBSTACLE
remaining_cells = [
(r, c)
for r in range(rows)
for c in range(cols)
if grid[r][c] == EMPTY
]
if sample_count > len(remaining_cells):
raise ValueError(
f"Too many samples. Maximum after obstacle placement is {len(remaining_cells)}."
)
sample_positions = random.sample(remaining_cells, sample_count)
for r, c in sample_positions:
grid[r][c] = SAMPLE
if sample_count == 0:
if basic_reachable(grid, start, goal):
return grid, start, goal
else:
if samples_safely_reachable(grid, start, goal, sample_positions):
return grid, start, goal
def generate_filename(rows, cols, obstacles, samples=0, algo="bfs"):
os.makedirs("maps", exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
return f"maps/map_{rows}x{cols}_obs{obstacles}_samp{samples}_{algo}_{timestamp}.txt"
def save_grid(grid, filename):
os.makedirs("maps", exist_ok=True)
with open(filename, "w", encoding="utf-8") as file:
rows = len(grid)
cols = len(grid[0])
file.write(f"{rows} {cols}\n")
for row in grid:
file.write(" ".join(row) + "\n")
def print_grid(grid):
for row in grid:
print(" ".join(row))