102 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			102 lines
		
	
	
	
		
			3.6 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/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
 | |
|     distance_decay_tiling = np.fromfunction(distance_decay, args.size, dtype=np.float32)
 | |
| 
 | |
|     hills = np.where(noise - distance_decay_tiling > 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):
 | |
|     # calculate distance from home base
 | |
|     dx = x - args.size[0] // 2
 | |
|     dy = y - args.size[1] // 2
 | |
|     distance = np.sqrt(dx*dx + dy*dy)
 | |
|     # use exponential decay
 | |
|     return np.exp(-distance / 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()
 |