Skip to content
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

random -> numpy.random #146

Merged
merged 6 commits into from
Oct 30, 2015
Merged

random -> numpy.random #146

merged 6 commits into from
Oct 30, 2015

Conversation

tcld
Copy link
Contributor

@tcld tcld commented Oct 23, 2015

Here the replacement of all uses of the random-module with respective numpy-methods.
Since this is kind of a break with save-compatibility and...everything, I guess, I also took the freedom to include some small changes so they aren't necessary in the future - I will add comments to the code to point them out and explain them.

Notes:

  1. This is based on Make use of Numpy-Arrays #138 .
  2. This does not pass the ancient-map tests (since that one uses RNG to determine its outcome)

@@ -9,14 +9,13 @@ def setUp(self):
pass

def test_random_point(self):
for seed in [0, 1, 27, 29, 1939, 1982, 2015]:
random.seed(seed)
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

@tcld
Copy link
Contributor Author

tcld commented Oct 26, 2015

I might change some small things about this before uploading the new blessed ancient maps. Once that is done the tests should be fine again.

@tcld tcld mentioned this pull request Oct 26, 2015
@tcld
Copy link
Contributor Author

tcld commented Oct 26, 2015

It would be nice if somebody could look over the code; I think I touched all that is necessary, but this will need some polish, I presume.

@@ -8,16 +8,6 @@ class TestBasicMapOperations(unittest.TestCase):
def setUp(self):
pass

def test_random_point(self):
Copy link
Contributor Author

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.

@ftomassetti
Copy link
Member

It looks good to me


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
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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).

@tcld
Copy link
Contributor Author

tcld commented Oct 26, 2015

After all this I ended up here:
ancientmap_48956
ancientmap_48956

It might not be immediately visible but everything except the mountains is the same (see the middle island). Before the changes to the RNG there were a lot more (small) differences. Does anybody have an idea why the mountains differ?

PS: I am referring to differences between Python 2 and 3.

@ftomassetti
Copy link
Member

the mountain generation is quite complicate. We have a mask to avoid overlapping mountains and a system to determine the size of each single mountain which is based on the elevation in the area. I am not sure if it is using the world seed.

@ftomassetti
Copy link
Member

it seems we are not using any seed for the mountains BUT we are using it for the other masks. Basically we assign each pixel to some masks, ~one for biome, and then we draw that pixel accordingly. If any mask change it could affect how mountains are drawn

@tcld
Copy link
Contributor Author

tcld commented Oct 26, 2015

I assume you are talking about this:

def _draw_a_mountain(pixels, 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)
    # left edge
    for mody in range(-h, h + 1):
        bottomness = (float(mody + h) / 2.0) / w
        leftborder = int(bottomness * w)
        darkarea = int(bottomness * w / 2)
        lightarea = int(bottomness * w / 2)
        for itx in range(darkarea, leftborder + 1):
            pixels[x - itx, y + mody] = gradient(itx, darkarea, leftborder,
                                                 (0, 0, 0), (64, 64, 64))
        for itx in range(-darkarea, lightarea + 1):
            pixels[x + itx, y + mody] = gradient(itx, -darkarea, lightarea,
                                                 (64, 64, 64), (128, 128, 128))
        for itx in range(lightarea, leftborder):
            pixels[x + itx, y + mody] = (181, 166, 127, 255)  # land_color
    # right edge
    for mody in range(-h, h + 1):
        bottomness = (float(mody + h) / 2.0) / w
        modx = int(bottomness * w)
        pixels[x + modx, y + mody] = mcr

Yes, this is complicated.^^ I worked through it a couple of days ago, but there is no directly visible use of something random (and the RNG in draw_ancientmap() is already replaced with a numpy one).

@tcld
Copy link
Contributor Author

tcld commented Oct 26, 2015

I will take another look at it tomorrow. We are very close to the goal already.^^

@tcld
Copy link
Contributor Author

tcld commented Oct 26, 2015

I did one more thing:
diff
So the mountains are clearly different; but I think the background-colors might be slightly shifted, too. (Maybe I just misunderstood how ImageMagick's compare works).
Anyway, it's a nice comparison.

@tcld
Copy link
Contributor Author

tcld commented Oct 28, 2015

I think I found the cause for the remaining problems:

def _mask(world, predicate, factor):
    _mask = [[False for x in range(factor * world.width)] for y in
             range(factor * world.height)]
    for y in range(factor * world.height):
        for x in range(factor * world.width):
            xf = int(x / factor)
            yf = int(y / factor)
            if predicate((xf, yf)):
                v = len(
                    world.tiles_around((xf, yf), radius=1,
                                       predicate=predicate))
                if v > 5:
                    _mask[y][x] = v
    return _mask

Apparently this map only stores integers in Python 2 (???) but floats in Python 3. It was going to be replaced by numpy anyway, I hope that will fix things.

EDIT: Maybe Python 2 does not automatically typecast at this position?

_mask[y][x] = v / 4   # v is an integer

EDIT2: That last assumption seems to be right. I will just add a typecast, assuming that was the intended behaviour. If not, we can change that later.

Python 2.7.6 (default, Jun 22 2015, 17:58:13) 
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 5/4
1
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 5/4
1.25

@tcld
Copy link
Contributor Author

tcld commented Oct 28, 2015

I could use some help with the pyflakes- and manifest-tests since I don't think my changes make them fail.

Mindwerks/worldengine-data#5 will take care of the problems with ancient maps.

Other than the failing tests, this code is ready and could be reviewed, if anybody has the time.

@@ -142,7 +140,7 @@ def _find_mountains_mask(world, 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
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 int(v/4).

@ftomassetti
Copy link
Member

Restarting tests after the changes to worldengine data

@tcld
Copy link
Contributor Author

tcld commented Oct 28, 2015

I consider this passed.^^ It would be nice if this could be the next merge so I can cross this off my list and move on to finishing another branch (instead of having to come back here and rebase/fix things).

import random


def random_point(width, height):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it could be nice to have methods such as random_point or perhaps random_land_point but we could add it later if needed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was only used once. Removing it allowed for another numpy-optimization; so for now this function would have been dead code (and since it is fairly simple anyway, rewriting it would be easy).

EDIT: I am talking about this optimization:
https://github.com/Mindwerks/worldengine/pull/146/files#diff-c341fcae944ab9ce9e38ea34a0165badR337

It is a very important one in my opinion.

@ftomassetti
Copy link
Member

Looks good to me. I would just leave a possibility to @psi29a to comment on it before merging but it is agreed that this one should be merged soon.

A lot of great work on this one, thanks a lot!

@tcld
Copy link
Contributor Author

tcld commented Oct 28, 2015

I hope I took proper care of your comments.

@ftomassetti
Copy link
Member

I am satisfied, as I said I would just leave @psi29a the time to comment, if he wishes to do so. If not this is good to go for me.

@ftomassetti
Copy link
Member

Let's start merging stuff

ftomassetti added a commit that referenced this pull request Oct 30, 2015
@ftomassetti ftomassetti merged commit 56e8dba into Mindwerks:master Oct 30, 2015
@tcld tcld deleted the rng_refine branch October 30, 2015 07:18
@psi29a
Copy link
Member

psi29a commented Oct 30, 2015

No need to wait on me... I'm in the middle of moving so I'm MIA for a bit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants