From 8d4010b5dc5686d54599dcab19a3e9129c440b5f Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Sun, 14 Apr 2019 12:19:54 +0430 Subject: [PATCH] feat(map-generator): mountain-agent for generating mountains --- map-generator/index.py | 145 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 131 insertions(+), 14 deletions(-) diff --git a/map-generator/index.py b/map-generator/index.py index 49610ca..1340020 100644 --- a/map-generator/index.py +++ b/map-generator/index.py @@ -1,22 +1,35 @@ import numpy as np import matplotlib.pyplot as plt +from matplotlib import colors, cm import scipy.interpolate as interpolate +from scipy import ndimage import math WIDTH = 900 HEIGHT = 450 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): # return x return -2 * x**3 + 3 * x**2 -WATER_LEVEL = 0 -WATER_PROPORTION = 0.4 -GROUND_PROPORTION = 1 - WATER_PROPORTION - # class Point(object): # def __init__(self, x, y, z): # self.x = x @@ -32,6 +45,11 @@ GROUND_PROPORTION = 1 - WATER_PROPORTION def is_ground(value): 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 f(*args, recursion=0, **kwargs): if recursion > max_recursion: @@ -39,35 +57,134 @@ def max_recursion(fn, max_recursion=0): 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): if size <= 0: return x, y = position w, h = ground.shape + trials = 0 + while True: - if size <= 0: break + if size <= 0 or trials > CONTINENT_MAX_TRIALS: break dx = np.random.randint(2) or -1 dy = np.random.randint(2) or -1 - x = max(min(x + dx, w - 1), 0) - y = max(min(y + dy, h - 1), 0) + r = np.random.randint(3) + 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 - 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): 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): position = (np.random.randint(0, width), np.random.randint(0, height)) - size = np.random.randint(width * height * GROUND_PROPORTION / continents) - print(size) - continent_agent(ground, position, size=size) + print(position) + continent_agent(ground, position, size=ground_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()