-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmap_generation.py
286 lines (218 loc) · 8.37 KB
/
map_generation.py
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
import itertools
import math
import random
from settings import config
def prepare_params(seed, length):
try:
seed = int(seed)
except (ValueError, TypeError):
seed = random.randint(1, 999999)
try:
length = int(length)
except (ValueError, TypeError):
length = random.randint(200, 2000)
return seed, length
def generate_section_params():
return (
random.randint(config.STEEPNESS_MIN, config.STEEPNESS_MAX),
random.randint(config.SECTION_LENGTH_MIN, config.SECTION_LENGTH_MAX),
random.randint(config.SPACE_MIN, config.SPACE_MAX)
)
def generate_map(seed, length, rows):
seed, length = prepare_params(seed, length)
random.seed(seed)
path_center = rows // 2
direction = 1
data = [[config.WALL] for i in range(rows)]
full_length = 0
while full_length < length:
steepness, section_length, space = generate_section_params()
direction, path_center, section_data = generate_section(
steepness, section_length, space, rows, direction, path_center
)
extend_map(data, section_data)
full_length += section_length
return data
def preetify_map(data):
# PREETY SPRITES
no_of_columns = len(data[0])
no_of_rows = len(data)
for column_idx in range(no_of_columns):
painted = [False for row_idx in range(no_of_rows)]
# Paint top section
for row_idx in range(no_of_rows):
if data[row_idx][column_idx]:
data[row_idx][column_idx] = config.TOP_WALL
painted[row_idx] = True
else:
if row_idx - 1 >= 0:
data[row_idx - 1][column_idx] = config.TOP_WALL_BOTTOM
break
# Paint bottom section
for row_idx in reversed(range(no_of_rows)):
if data[row_idx][column_idx]:
data[row_idx][column_idx] = config.BOTTOM_WALL
painted[row_idx] = True
else:
if row_idx + 1 < no_of_rows:
data[row_idx + 1][column_idx] = config.BOTTOM_WALL_TOP
# Put random small object
if random.randint(0, config.SMALL_OBJECT_CHANCE) == 0:
data[row_idx][column_idx] = random.choice(
config.SMALL_OBJECTS
)
painted[row_idx] = True
break
# Paint anything in the middle
for row_idx in range(no_of_rows):
if not data[row_idx][column_idx]:
# empty cell
continue
if painted[row_idx]:
# already marked as top or bottom
continue
data[row_idx][column_idx] = config.MIDDLE_WALL
FINISH_TUNNEL_LENGTH = 50
def add_finish_line(data):
no_of_columns = len(data[0])
last_col_idx = no_of_columns - 1
no_of_rows = len(data)
column = [config.EMPTY for _ in range(no_of_columns)]
# Build how a straight tunnel should look like
for row_idx in range(no_of_rows):
if data[row_idx][last_col_idx] in config.WALLS:
column[row_idx] = config.TOP_WALL
else:
column[row_idx - 1] = config.TOP_WALL_BOTTOM
break
for row_idx in reversed(range(no_of_rows)):
if data[row_idx][last_col_idx] in config.WALLS:
column[row_idx] = config.BOTTOM_WALL
else:
column[row_idx + 1] = config.BOTTOM_WALL_TOP
break
# Repeat the straight section column
for _ in range(FINISH_TUNNEL_LENGTH):
for row_idx in range(no_of_rows):
data[row_idx].append(column[row_idx])
# Insert exit column
exit_column = [cell if cell else config.FINISH for cell in column]
for row_idx in range(no_of_rows):
data[row_idx].append(exit_column[row_idx])
# Repeat straight tunnel a bit more
for _ in range(2):
for row_idx in range(no_of_rows):
data[row_idx].append(column[row_idx])
def extend_map(current_map, new_section):
for i, row in enumerate(current_map):
row.extend(new_section[i])
def generate_section(steepness, length, space, rows, direction, path_center):
data = [[] for i in range(rows)]
switched = None
direction *= -1
min_row = 1
max_row = rows - 1
for col in range(length):
half_space = space // 2
top_block = path_center - half_space
bottom_block = path_center + half_space
for row in range(rows):
if row in (0, max_row):
data[row].append(config.WALL)
elif row < top_block or row > bottom_block:
data[row].append(config.WALL)
else:
data[row].append(config.EMPTY)
current_center = math.floor(rows / steepness * col)
next_center = math.floor(rows / steepness * (col + 1))
path_center += (current_center - next_center) * direction
if bottom_block >= max_row and switched != 'up':
direction, switched = 1, 'up'
if top_block <= min_row and switched != 'down':
direction, switched = -1, 'down'
data = add_obstacles(data, length, space, rows)
return direction, path_center, data
def add_obstacles(data, length, space, rows):
if space < 9:
return data
elif space < 15:
obstacle = random.choice(config.NARROW_OBSTACLES_TYPES)
else:
obstacle = random.choice(
config.OBSTACLES_TYPES
)
if obstacle == 'point':
return add_point_obstacle(data, length, space, rows)
elif obstacle == 'stalactite':
return add_stalactite_obstacle(data, length, space, rows)
elif obstacle == 'junction':
return add_junction_obstacle(data, length, space, rows)
elif obstacle == 'deadend':
return add_deadend_obstacle(data, length, space, rows)
def add_point_obstacle(data, length, space, rows):
distance = random.randint(8, 14)
for col in range(distance, length, distance):
clear_path = get_clear_path_indices(data, rows, col)
possible_space = clear_path[3:-3]
if not possible_space:
continue
row = random.choice(possible_space)
if space <= 12:
data[row][col] = config.WALL
else:
for i, j in itertools.product([0, 1], [0, 1]):
try:
data[row + i][col + j] = config.WALL
except IndexError:
pass
return data
def add_stalactite_obstacle(data, length, space, rows):
distance = random.randint(12, 18)
for col in range(distance, length, distance):
clear_path = get_clear_path_indices(data, rows, col)
possible_space = clear_path[7:-7]
if not possible_space:
continue
row = random.choice(possible_space)
direction = random.randint(0, 1)
for k in clear_path:
if direction and k <= row:
data[k][col] = config.WALL
elif not direction and k >= row:
data[k][col] = config.WALL
return data
def add_junction_obstacle(data, length, space, rows):
distance = 4
for col in range(distance, length - distance):
clear_path = get_clear_path_indices(data, rows, col)
middle = clear_path[len(clear_path) // 2]
for x in clear_path:
if x == middle:
data[x][col] = config.WALL
return data
def add_deadend_obstacle(data, length, space, rows):
distance = 8
depth = random.randint(6, 9)
direction = random.randint(0, 1)
start = 0
if length - (distance + depth) <= distance:
start_distance = distance
else:
start_distance = random.randint(distance, length - (distance + depth))
for col in range(start_distance, length - distance):
clear_path = get_clear_path_indices(data, rows, col)
middle = clear_path[len(clear_path) // 2]
for x in clear_path:
if x == middle:
data[x][col] = config.WALL
if start == depth:
for k in clear_path:
if direction and k <= middle:
data[k][col] = config.WALL
elif not direction and k >= middle:
data[k][col] = config.WALL
break
start += 1
return data
def get_clear_path_indices(data, rows, col):
return [i for i in range(rows) if data[i][col] is config.EMPTY]