David Klement
2 years ago
3 changed files with 105 additions and 0 deletions
@ -0,0 +1,103 @@ |
|||
#!/usr/bin/env python3 |
|||
import argparse |
|||
import json |
|||
import numpy as np |
|||
|
|||
|
|||
parser = argparse.ArgumentParser() |
|||
parser.add_argument("--seed", default=1, type=int, help="Random seed.") |
|||
parser.add_argument("--threshold", default=0.4, type=float, help="Higher threshold = less hills.") |
|||
parser.add_argument("--safezone", default=3.0, type=float, help="Size of hill-free area around home bases.") |
|||
parser.add_argument("--size", default=(30, 30), type=int, nargs=2, help="Width and height for team.") |
|||
parser.add_argument("--res", default=(10, 10), type=int, nargs=2, help="Higher res = more smaller hills. Must divide size.") |
|||
|
|||
|
|||
def main(): |
|||
np.random.seed(args.seed) |
|||
# base noise profile |
|||
noise = generate_perlin_noise_2d( |
|||
args.size, |
|||
args.res, |
|||
tileable=(True, True), |
|||
) |
|||
|
|||
# make it less likely for hills to be close to home bases |
|||
home_distance_tile = np.fromfunction(distance_decay, args.size, dtype=np.float32) |
|||
home_distance = np.sum([ |
|||
home_distance_tile, |
|||
np.flip(home_distance_tile, axis=0), |
|||
np.flip(home_distance_tile, axis=1), |
|||
np.flip(home_distance_tile, axis=(0,1)), |
|||
], axis=0) |
|||
|
|||
hills = np.where(noise - home_distance > args.threshold, "x", ".") |
|||
rows = ["".join(row) for row in hills] |
|||
config = { |
|||
"width_per_team": args.size[0], |
|||
"height_per_team": args.size[1], |
|||
"hills": rows |
|||
} |
|||
print(json.dumps(config, indent=4)) |
|||
|
|||
|
|||
def distance_decay(x, y): |
|||
return np.exp(-np.sqrt(x*x + y*y) / args.safezone) |
|||
|
|||
|
|||
# Source: |
|||
# https://github.com/pvigier/perlin-numpy/blob/master/perlin_numpy/perlin2d.py |
|||
|
|||
def interpolant(t): |
|||
return t*t*t*(t*(t*6 - 15) + 10) |
|||
|
|||
|
|||
def generate_perlin_noise_2d( |
|||
shape, res, tileable=(False, False), interpolant=interpolant |
|||
): |
|||
"""Generate a 2D numpy array of perlin noise. |
|||
Args: |
|||
shape: The shape of the generated array (tuple of two ints). |
|||
This must be a multple of res. |
|||
res: The number of periods of noise to generate along each |
|||
axis (tuple of two ints). Note shape must be a multiple of |
|||
res. |
|||
tileable: If the noise should be tileable along each axis |
|||
(tuple of two bools). Defaults to (False, False). |
|||
interpolant: The interpolation function, defaults to |
|||
t*t*t*(t*(t*6 - 15) + 10). |
|||
Returns: |
|||
A numpy array of shape shape with the generated noise. |
|||
Raises: |
|||
ValueError: If shape is not a multiple of res. |
|||
""" |
|||
delta = (res[0] / shape[0], res[1] / shape[1]) |
|||
d = (shape[0] // res[0], shape[1] // res[1]) |
|||
grid = np.mgrid[0:res[0]:delta[0], 0:res[1]:delta[1]]\ |
|||
.transpose(1, 2, 0) % 1 |
|||
# Gradients |
|||
angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1) |
|||
gradients = np.dstack((np.cos(angles), np.sin(angles))) |
|||
if tileable[0]: |
|||
gradients[-1, :] = gradients[0, :] |
|||
if tileable[1]: |
|||
gradients[:, -1] = gradients[:, 0] |
|||
gradients = gradients.repeat(d[0], 0).repeat(d[1], 1) |
|||
g00 = gradients[:-d[0], :-d[1]] |
|||
g10 = gradients[d[0]:, :-d[1]] |
|||
g01 = gradients[:-d[0], d[1]:] |
|||
g11 = gradients[d[0]:, d[1]:] |
|||
# Ramps |
|||
n00 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1])) * g00, 2) |
|||
n10 = np.sum(np.dstack((grid[:, :, 0]-1, grid[:, :, 1])) * g10, 2) |
|||
n01 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1]-1)) * g01, 2) |
|||
n11 = np.sum(np.dstack((grid[:, :, 0]-1, grid[:, :, 1]-1)) * g11, 2) |
|||
# Interpolation |
|||
t = interpolant(grid) |
|||
n0 = n00*(1-t[:, :, 0]) + t[:, :, 0]*n10 |
|||
n1 = n01*(1-t[:, :, 0]) + t[:, :, 0]*n11 |
|||
return np.sqrt(2)*((1-t[:, :, 1])*n0 + t[:, :, 1]*n1) |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
args = parser.parse_args([] if "__file__" not in globals() else None) |
|||
main() |
Loading…
Reference in new issue