feat(map-generator): mountain-agent for generating mountains

This commit is contained in:
Mahdi Dibaiee 2019-04-14 12:19:54 +04:30
parent 3cd25bb458
commit 8d4010b5dc

View File

@ -1,22 +1,35 @@
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from matplotlib import colors, cm
import scipy.interpolate as interpolate import scipy.interpolate as interpolate
from scipy import ndimage
import math import math
WIDTH = 900 WIDTH = 900
HEIGHT = 450 HEIGHT = 450
RATIO = WIDTH / HEIGHT RATIO = WIDTH / HEIGHT
MAX_ELEVATION = 100 MOUNTAIN_SEA_DISTANCE = 50
MOUNTAIN_SEA_THRESHOLD = 2
MOUNTAIN_JAGGEDNESS = 1
CONTINENT_MAX_TRIALS = 1e4
MOUNTAIN_RATIO = 0.3
SEA_COLOR = np.array((53, 179, 220, 255)) / 255
SHARPNESS = 0.7
WATER_LEVEL = 0
MAX_ELEVATION = 30
GROUND_NOISE = 15
WATER_PROPORTION = 0.6
GROUND_PROPORTION = 1 - WATER_PROPORTION
DIRECTIONS = [(-1, -1), (-1, 0), (-1, 1), (1, 1), (1, 0), (1, -1), (0, -1), (0, 1)]
def s(x): def s(x):
# return x # return x
return -2 * x**3 + 3 * x**2 return -2 * x**3 + 3 * x**2
WATER_LEVEL = 0
WATER_PROPORTION = 0.4
GROUND_PROPORTION = 1 - WATER_PROPORTION
# class Point(object): # class Point(object):
# def __init__(self, x, y, z): # def __init__(self, x, y, z):
# self.x = x # self.x = x
@ -32,6 +45,11 @@ GROUND_PROPORTION = 1 - WATER_PROPORTION
def is_ground(value): def is_ground(value):
return value > WATER_LEVEL return value > WATER_LEVEL
def in_range(p, m, size):
x, y = p
mx, my = m
return ((x - mx)**2 + (y - my)**2) < size
def max_recursion(fn, max_recursion=0): def max_recursion(fn, max_recursion=0):
def f(*args, recursion=0, **kwargs): def f(*args, recursion=0, **kwargs):
if recursion > max_recursion: if recursion > max_recursion:
@ -39,35 +57,134 @@ def max_recursion(fn, max_recursion=0):
return fn(*args, **kwargs) return fn(*args, **kwargs)
def bound_check(ground, point):
x, y = point
w, h = ground.shape
x = max(min(x, w - 1), 0)
y = max(min(y, h - 1), 0)
return (x, y)
def continent_agent(ground, position, size): def continent_agent(ground, position, size):
if size <= 0: return if size <= 0: return
x, y = position x, y = position
w, h = ground.shape w, h = ground.shape
trials = 0
while True: while True:
if size <= 0: break if size <= 0 or trials > CONTINENT_MAX_TRIALS: break
dx = np.random.randint(2) or -1 dx = np.random.randint(2) or -1
dy = np.random.randint(2) or -1 dy = np.random.randint(2) or -1
x = max(min(x + dx, w - 1), 0) r = np.random.randint(3)
y = max(min(y + dy, h - 1), 0) new_point = bound_check(ground, (x + dx, y + dy))
if r == 0:
x = new_point[0]
elif r == 1:
y = new_point[1]
else:
x, y = new_point
if not is_ground(ground[x, y]):
if not is_ground(ground[x, y]) and in_range((x, y), position, size):
trials = 0
size -= 1 size -= 1
ground[x, y] = 1 ground[x, y] = np.random.randint(1, GROUND_NOISE)
else:
trials += 1
def neighbours(ground, position, radius):
x, y = position
return ground[x-radius:x+radius+1, y-radius:y+radius+1]
def away_from_sea(ground, position, radius=MOUNTAIN_SEA_DISTANCE):
ns = neighbours(ground, position, radius).flatten()
sea = len([1 for x in ns if not is_ground(x)])
return sea < MOUNTAIN_SEA_THRESHOLD
MOUNTAIN_AREA_ELEVATION = 0.4
MOUNTAIN_AREA_ELEVATION_N = 5
MOUNTAIN_AREA_ELEVATION_AREA = 10
def random_elevate_agent(ground, position, height, size=MOUNTAIN_AREA_ELEVATION_N):
position = position + np.random.random_integers(-MOUNTAIN_AREA_ELEVATION_AREA, MOUNTAIN_AREA_ELEVATION_AREA, size=2)
for i in range(size):
d = DIRECTIONS[np.random.randint(len(DIRECTIONS))]
change = height * MOUNTAIN_AREA_ELEVATION + np.random.randint(MOUNTAIN_JAGGEDNESS + 1)
new_index = bound_check(ground, position + np.array(d))
if is_ground(ground[new_index]):
ground[new_index] += change
def mountain_agent(ground, position):
if not away_from_sea(ground, position):
return
x, y = position
height = np.random.randint(MAX_ELEVATION)
ground[x, y] = height
last_height = height
for i in range(1, height):
for d in DIRECTIONS:
change = np.random.randint(MOUNTAIN_JAGGEDNESS + 1)
distance = np.array(d)*i
new_index = bound_check(ground, position + distance)
if is_ground(ground[new_index]):
ground[new_index] = last_height - change
last_height = last_height - MOUNTAIN_JAGGEDNESS
random_elevate_agent(ground, position, height)
# takes an initial position and a list of (direction, probability) tuples to walk on
# def split_agent(ground, position, directions):
def constant_filter(a):
if a[0] > (1 - SHARPNESS):
return max(1, a[0])
return 0
def generate_map(width, height, continents=4): def generate_map(width, height, continents=4):
ground = np.zeros((width, height)) ground = np.zeros((width, height))
ground_size = width * height * GROUND_PROPORTION
# position = (int(width / 2), int(height / 2))
# ground_size = width * height * GROUND_PROPORTION
# continent_agent(ground, position, size=ground_size)
for continent in range(continents): for continent in range(continents):
position = (np.random.randint(0, width), np.random.randint(0, height)) position = (np.random.randint(0, width), np.random.randint(0, height))
size = np.random.randint(width * height * GROUND_PROPORTION / continents) print(position)
print(size) continent_agent(ground, position, size=ground_size)
continent_agent(ground, position, size=size)
plt.imshow(ground.T, cmap='hot') ground = ndimage.gaussian_filter(ground, sigma=(1 - SHARPNESS) * 20)
for i in range(int(ground_size * MOUNTAIN_RATIO / MAX_ELEVATION**2)):
position = (np.random.randint(0, width), np.random.randint(0, height))
mountain_agent(ground, position)
norm = colors.Normalize(vmin=1)
greys = cm.get_cmap('Greys')
greys.set_under(color=SEA_COLOR)
ground = ndimage.gaussian_filter(ground, sigma=4)
ground = ndimage.generic_filter(ground, constant_filter, size=1)
print(np.min(ground), np.max(ground), MAX_ELEVATION)
print(np.unique(ground))
plt.imshow(ground.T, cmap=greys, norm=norm)
plt.show() plt.show()