Skip to content

Commit

Permalink
Merge pull request #146 from tcld/rng_refine
Browse files Browse the repository at this point in the history
random -> numpy.random
  • Loading branch information
ftomassetti committed Oct 30, 2015
2 parents 29d187f + 40e5176 commit 56e8dba
Show file tree
Hide file tree
Showing 13 changed files with 55 additions and 58 deletions.
14 changes: 2 additions & 12 deletions tests/basic_map_operations_test.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import random
import numpy
import unittest
from worldengine.basic_map_operations import distance, index_of_nearest, random_point
from worldengine.basic_map_operations import distance, index_of_nearest


class TestBasicMapOperations(unittest.TestCase):

def setUp(self):
pass

def test_random_point(self):
for seed in [0, 1, 27, 29, 1939, 1982, 2015]:
random.seed(seed)
for n in range(10):
x, y = random_point(100, 200)
self.assertTrue(x >= 0, "x is within boundaries")
self.assertTrue(x < 100, "x is within boundaries")
self.assertTrue(y >= 0, "y is within boundaries")
self.assertTrue(y < 200, "y is within boundaries")

def test_distance(self):
self.assertAlmostEquals(22.360679774997898, distance((0, 0), (10, 20)))
self.assertAlmostEquals(22.360679774997898, distance((-1, -1), (9, 19)))
Expand Down
5 changes: 0 additions & 5 deletions worldengine/basic_map_operations.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import math
import random


def random_point(width, height):
return random.randrange(0, width), random.randrange(0, height)


def distance(pa, pb):
Expand Down
7 changes: 4 additions & 3 deletions worldengine/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import os
import numpy
import pickle
import random
import worldengine.generation as geo
from worldengine.common import array_to_matrix, set_verbose, print_verbose
from worldengine.draw import draw_ancientmap_on_file, draw_biome_on_file, draw_ocean_on_file, \
Expand Down Expand Up @@ -390,11 +389,13 @@ def main():
if not os.path.exists(args.FILE):
usage("The specified world file does not exist")

maxseed = 65535 # there is a hard limit somewhere so seeds outside the uint16 range are considered unsafe
if args.seed is not None:
seed = int(args.seed)
assert 0 <= seed <= maxseed, "Seed has to be in the range between 0 and %s, borders included." % maxseed
else:
seed = random.randint(0, 65535)#RNG initialization is done automatically
random.seed(seed)
seed = numpy.random.randint(0, maxseed) # first-time RNG initialization is done automatically
numpy.random.seed(seed)

if args.world_name:
world_name = args.world_name
Expand Down
16 changes: 7 additions & 9 deletions worldengine/drawing_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import math
import numpy
import random
import sys
import time
from worldengine.common import get_verbose
Expand Down Expand Up @@ -133,16 +132,15 @@ def is_inner_border(pos):


def _find_mountains_mask(world, factor):
_mask = [[False for x in range(factor * world.width)] for y in
range(factor * world.height)]
_mask = numpy.full((factor * world.height, factor * world.width), False, dtype=object)
for y in range(factor * world.height):
for x in range(factor * world.width):
if world.is_mountain((int(x / factor), int(y / factor))):
v = len(world.tiles_around((int(x / factor), int(y / factor)),
radius=3,
predicate=world.is_mountain))
if v > 32:
_mask[y][x] = v / 4
_mask[y, x] = v / 4.0 # force conversion to float, Python 2 will *not* do it automatically
return _mask


Expand Down Expand Up @@ -515,7 +513,7 @@ def _draw_savanna(pixels, x, y):


# TODO: complete and enable this one
def _dynamic_draw_a_mountain(pixels, x, y, w=3, h=3):
def _dynamic_draw_a_mountain(pixels, rng, x, y, w=3, h=3):
# mcl = (0, 0, 0, 255) # TODO: No longer used?
# mcll = (128, 128, 128, 255)
mcr = (75, 75, 75, 255)
Expand All @@ -530,7 +528,7 @@ def _dynamic_draw_a_mountain(pixels, x, y, w=3, h=3):
max_leftborder = int(bottomness * w * 1.33)
if not last_leftborder == None:
max_leftborder = min(max_leftborder, last_leftborder + 1)
leftborder = int(bottomness * w) + random.randint(-2, 2)/2
leftborder = int(bottomness * w) + rng.randint(-2, 2)/2
if leftborder < min_leftborder:
leftborder = min_leftborder
if leftborder > max_leftborder:
Expand All @@ -557,7 +555,7 @@ def _dynamic_draw_a_mountain(pixels, x, y, w=3, h=3):
max_modx = int(bottomness * w * 1.33)
if not last_modx == None:
max_modx = min(max_modx, last_modx + 1)
modx = int(bottomness * w) + random.randint(-2, 2)/2
modx = int(bottomness * w) + numpy.random.randint(-2, 2)/2
if modx < min_modx:
modx = min_modx
if modx > max_modx:
Expand Down Expand Up @@ -603,7 +601,7 @@ def draw_ancientmap(world, target, resize_factor=1,
sea_color=(212, 198, 169, 255),
draw_biome = True, draw_rivers = True, draw_mountains = True,
draw_outer_land_border = False, verbose=get_verbose()):
random.seed(world.seed * 11)
rng = numpy.random.RandomState(world.seed) # create our own random generator

if verbose:
start_time = time.time()
Expand Down Expand Up @@ -893,7 +891,7 @@ def _anti_alias_point(x, y):
if len(world.tiles_around_factor(resize_factor, (x, y),
radius=r,
predicate=on_border)) <= 2:
if random.random() <= .5:
if rng.random_sample() <= .5:
_draw_temperate_forest1(target, x, y, w=w, h=h)
else:
_draw_temperate_forest2(target, x, y, w=w, h=h)
Expand Down
32 changes: 23 additions & 9 deletions worldengine/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,31 +194,45 @@ def _around(x, y, width, height):
def generate_world(w, step):
if isinstance(step, str):
step = Step.get_by_name(step)
seed = w.seed

if not step.include_precipitations:
return w

# Prepare sufficient seeds for the different steps of the generation
rng = numpy.random.RandomState(w.seed) # create a fresh RNG in case the global RNG is compromised (i.e. has been queried an indefinite amount of times before generate_world() was called)
sub_seeds = rng.randint(0, 4294967295, size=100) # sys.maxsize didn't quite work
seed_dict = {
'PrecipitationSimulation': sub_seeds[ 0], # after 0.19.0 do not ever switch out the seeds here to maximize seed-compatibility
'ErosionSimulation': sub_seeds[ 1],
'WatermapSimulation': sub_seeds[ 2],
'IrrigationSimulation': sub_seeds[ 3],
'TemperatureSimulation': sub_seeds[ 4],
'HumiditySimulation': sub_seeds[ 5],
'PermeabilitySimulation': sub_seeds[ 6],
'BiomeSimulation': sub_seeds[ 7],
'': sub_seeds[99]
}

# Precipitation with thresholds
PrecipitationSimulation().execute(w, seed)
PrecipitationSimulation().execute(w, seed_dict['PrecipitationSimulation'])

if not step.include_erosion:
return w
ErosionSimulation().execute(w, seed)
ErosionSimulation().execute(w, seed_dict['ErosionSimulation']) # seed not currently used
if get_verbose():
print("...erosion calculated")

WatermapSimulation().execute(w, seed)
WatermapSimulation().execute(w, seed_dict['WatermapSimulation']) # seed not currently used

# FIXME: create setters
IrrigationSimulation().execute(w, seed)
TemperatureSimulation().execute(w, seed)
HumiditySimulation().execute(w, seed)
IrrigationSimulation().execute(w, seed_dict['IrrigationSimulation']) # seed not currently used
TemperatureSimulation().execute(w, seed_dict['TemperatureSimulation'])
HumiditySimulation().execute(w, seed_dict['HumiditySimulation']) # seed not currently used


PermeabilitySimulation().execute(w, seed)
PermeabilitySimulation().execute(w, seed_dict['PermeabilitySimulation'])

cm, biome_cm = BiomeSimulation().execute(w, seed)
cm, biome_cm = BiomeSimulation().execute(w, seed_dict['BiomeSimulation']) # seed not currently used
for cl in cm.keys():
count = cm[cl]
if get_verbose():
Expand Down
3 changes: 1 addition & 2 deletions worldengine/plates.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# extension which is not available when using this project from jython

import platec
import random
import time
import numpy

Expand Down Expand Up @@ -64,7 +63,7 @@ def world_gen(name, width, height, seed, num_plates=10, ocean_level=1.0,

if verbose:
start_time = time.time()
add_noise_to_elevation(world, random.randint(0, 4096))
add_noise_to_elevation(world, numpy.random.randint(0, 4096)) # uses the global RNG; this is the very first call to said RNG - should that change, this needs to be taken care of
if verbose:
elapsed_time = time.time() - start_time
print("...plates.world_gen: elevation noise added. Elapsed time " +
Expand Down
1 change: 1 addition & 0 deletions worldengine/simulations/erosion.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def _numpy_to_matrix(numpy_array):
This is used because currently we do not know how to serialize numpy
arrays :( with pickle. In the future we will use pytables/hdf5"""
# TODO: Is this still true? Pickle didn't seem to cause problems for me. /tcld

width = numpy_array.shape[0]
height = numpy_array.shape[1]
Expand Down
4 changes: 2 additions & 2 deletions worldengine/simulations/hydrology.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ def droplet(world, pos, q, _watermap):

_watermap_data = numpy.zeros((world.height, world.width), dtype=float)
for i in range(n):
x, y = world.random_land()
if True and world.precipitation['data'][y, x] > 0:
x, y = world.random_land() # will return None for x and y if no land exists
if x is not None and world.precipitation['data'][y, x] > 0:
droplet(world, (x, y), world.precipitation['data'][y, x],
_watermap_data)
_watermap = dict()
Expand Down
2 changes: 1 addition & 1 deletion worldengine/simulations/irrigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class IrrigationSimulation(object):
def is_applicable(world):
return world.has_watermap() and (not world.has_irrigation())

def execute(self, world, seed):#seed is currently not used
def execute(self, world, seed):
world.irrigation = self._calculate(world)

@staticmethod
Expand Down
6 changes: 3 additions & 3 deletions worldengine/simulations/permeability.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from worldengine.simulations.basic import find_threshold_f
from noise import snoise2
import random
import numpy


Expand All @@ -21,8 +20,9 @@ def execute(self, world, seed):

@staticmethod
def _calculate(seed, width, height):
random.seed(seed * 37)
base = random.randint(0, 4096)
rng = numpy.random.RandomState(seed) # create our own random generator
base = rng.randint(0, 4096)

perm = numpy.zeros((height, width), dtype=float)

octaves = 6
Expand Down
6 changes: 3 additions & 3 deletions worldengine/simulations/precipitation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import random
import time
import numpy
from noise import snoise2
Expand Down Expand Up @@ -32,9 +31,10 @@ def execute(self, world, seed):
@staticmethod
def _calculate(seed, width, height):
"""Precipitation is a value in [-1,1]"""
rng = numpy.random.RandomState(seed) # create our own random generator
base = rng.randint(0, 4096)

border = width / 4
random.seed(seed * 13)
base = random.randint(0, 4096)
precipitations = numpy.zeros((height, width), dtype=float)

octaves = 6
Expand Down
5 changes: 2 additions & 3 deletions worldengine/simulations/temperature.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from worldengine.simulations.basic import find_threshold_f
import random
import numpy


Expand Down Expand Up @@ -31,8 +30,8 @@ def _calculate(world, seed, elevation, mountain_level):
width = world.width
height = world.height

random.seed(seed * 7)
base = random.randint(0, 4096)
rng = numpy.random.RandomState(seed) # create our own random generator
base = rng.randint(0, 4096)
temp = numpy.zeros((height, width), dtype=float)

from noise import snoise2
Expand Down
12 changes: 6 additions & 6 deletions worldengine/world.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
WarmTemperateWetForest, TropicalDesert, TropicalDesertScrub, TropicalDryForest, \
TropicalMoistForest, TropicalRainForest, TropicalThornWoodland, TropicalWetForest, \
TropicalVeryDryForest, biome_index_to_name, biome_name_to_index
from worldengine.basic_map_operations import random_point
import worldengine.protobuf.World_pb2 as Protobuf
from worldengine.step import Step
from worldengine.common import _equal
Expand Down Expand Up @@ -332,11 +331,12 @@ def contains(self, pos):
#

def random_land(self):
x, y = random_point(self.width, self.height)
if self.ocean[y, x]:#TODO: this method should get a safer/quicker way of finding land!
return self.random_land()
else:
return x, y
if self.ocean.all():
return None, None # return invalid indices if there is no land at all
lands = numpy.invert(self.ocean)
lands = numpy.transpose(lands.nonzero()) # returns a list of tuples/indices with land positions
y, x = lands[numpy.random.randint(0, len(lands))] # uses global RNG
return x, y

def is_land(self, pos):
return not self.ocean[pos[1], pos[0]]#faster than reversing pos or transposing ocean
Expand Down

0 comments on commit 56e8dba

Please sign in to comment.