-
Notifications
You must be signed in to change notification settings - Fork 132
random -> numpy.random #146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
48b43fb
11f3f5f
1e9861b
1f02115
2b07689
40e5176
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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))) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was very much unnecessary unless the RNG is expected to be of very dubious quality. I chose to simplify it. |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,7 +6,6 @@ | |
|
|
||
| import math | ||
| import numpy | ||
| import random | ||
| import sys | ||
| import time | ||
| from worldengine.common import get_verbose | ||
|
|
@@ -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 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure which behaviour was the intended one here. The ancient map output looks different, I think the mountains are spread a little thinner for the old behaviour |
||
| return _mask | ||
|
|
||
|
|
||
|
|
@@ -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) | ||
|
|
@@ -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: | ||
|
|
@@ -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: | ||
|
|
@@ -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() | ||
|
|
@@ -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) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like the use of this fixed number at all. Does somebody have a better idea? sys.maxint (or whatever it is called) is not available for Python 3; sys.maxsize returns a number that is not compatible with numpy (which takes a uint32). |
||
| seed_dict = { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice idea using a dict here |
||
| '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(): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is fine, execute is part of an interface, in the other implementations seed is used |
||
| world.irrigation = self._calculate(world) | ||
|
|
||
| @staticmethod | ||
|
|
||
| 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 | ||
|
|
||
|
|
||
|
|
@@ -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 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of permanently modifying the global RNG (e.g. numpy.random.randint()) the sub-steps now use their own RNG whenever easily possible (and they don't modify the seeds anymore due to that now happening in generation.py). This makes the main generation mostly independent of the order in which steps are done. In addition, should a GUI ever be able to let the user modify seeds for different steps of the generation, that seed would then actually be used instead of being multiplied by an arbitrary number first. |
||
| base = rng.randint(0, 4096) | ||
|
|
||
| perm = numpy.zeros((height, width), dtype=float) | ||
|
|
||
| octaves = 6 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could we return None or (None, None) here when there is no land?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed it. |
||
| return x, y | ||
|
|
||
| def is_land(self, pos): | ||
| return not self.ocean[pos[1], pos[0]]#faster than reversing pos or transposing ocean | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method random_point() isn't used anymore so I removed it (and this test). It was an almost trivial method anyway.