0

I'm learning the basics of Pygame and I'm slowly trying to get a mini RPG game built bit by bit. I've done a very basic implementation of getting tiles to display from coordinates and the camera location, however it's just block colours at the moment, so I wanted to vary the colour a bit.

I tried something to the effect of this code below, which works, but it's very slow:

new_rand = random.Random()

#x_range and y_range only draw blocks that are within the bounds of the screen
for x in x_range:
    for y in y_range:
        #get the colour from a dictionary

        new_rand.seed((x, y))
        colour = [min(255, max(0, c + new_rand.uniform(-10, 10))) for c in colour]

        #draw block

I'm positive there should be a better way to do this, just could do with a pointer in the right direction.

Also, I subtract the camera location from (x, y) to get where the blocks should be on screen. Would there be a faster way to do this? That by itself when very zoomed out is not particularly fast.

Peter
  • 2,153
  • 2
  • 20
  • 34
  • Why do you need to have each (x,y) pair to have their own random seed? Why can't you use the same seed for all x and y? – Daenyth Nov 18 '15 at 18:43
  • It seems like you are misunderstanding seeds. A PRNG only needs to be seeded **once** at the beginning of program execution. For non-cryptographic applications, it's usually just seeded with the current system time, or something like that. – Linuxios Nov 18 '15 at 18:43
  • @Linuxios: If the goal is the _same_ random result (per the question title), then reseeding would be needed to get the same result each time. – ShadowRanger Nov 18 '15 at 18:48
  • Well, how would I get it to return the same number for `(x, y)` without having to set a seed, bearing in mind the camera can move and give a different range of values? :P – Peter Nov 18 '15 at 18:48
  • @ShadowRanger: You could still just set the seed once before the loop to get a consistent sequence of pseudorandom numbers. – Linuxios Nov 18 '15 at 18:48
  • @Peter: Ah. I get it now. Frankly, I'd just generate an array of the random color deltas ahead of time. Unless your map is truly huge, it's not really going to be much memory anyway. – Linuxios Nov 18 '15 at 18:50
  • Well as of this moment I was just testing if it worked at an x value of 10^100 (which it does, but it starts running a bit slower), I know I'm jumping in the deep end, but I'm planning on a procedural map that'll go on forever :P With the array, do you mean like a precalculated small grid that'll repeat itself? I'll give that a shot in a short while. – Peter Nov 18 '15 at 18:57
  • Somebody is going to have to explain to me what a "consistent random result" is, or how you can get the "same random result" each time. Clearly I'm using a different definition of random than someone. – Two-Bit Alchemist Nov 18 '15 at 18:58
  • @Two-BitAlchemist If I understood correctly he wants a random colour for each (x,y) pair but at the same time needs that each time the random colour is generated returns the same colour for each (x, y) pair – Mr. E Nov 18 '15 at 19:07
  • Basically, the relative location of points change, so I can't just calculate a grid of random values, because when you move the camera, the random values will move with the camera. Given point (72, -5) for example, I'd like it to get the same result from random so that it's brightness will stay constant whenever the camera moves. – Peter Nov 18 '15 at 19:07
  • 1
    @Mr.E Then the random colors are generated _once_, and mapped to the (x, y) pairs. The point here is somewhat pedantic and conceptual, but at the same time crucial: **random** functions should not (except rarely and by accident) produce the same output twice. If you want something to be "randomly generated the same way twice", you have fundamentally misunderstood randomness. – Two-Bit Alchemist Nov 18 '15 at 19:11
  • 2
    Maybe you could use a simple hash function that returns values between -10 and 10? Something like x * y % 20 - 10 ? – Chris Lawlor Nov 18 '15 at 19:20
  • @ChrisLawlor is right, OP shouldn't be using a random generator for something that must be deterministic – Mr. E Nov 18 '15 at 19:30
  • @Two-BitAlchemist Totally agree with you, requirement is contradictory – Mr. E Nov 18 '15 at 19:31
  • Ah, it looks like the idea from Chris may work, that calculation in particular wouldn't really work (anything with 0 in it would result in -10), but I'll have a play around and see if I can get something working. – Peter Nov 18 '15 at 19:36

1 Answers1

1

Fleshing out my comment from above - if you're looking for a consistent value for any given x, y pair, what you need is a hash function. From Wikipedia:

A hash function is any function that can be used to map data of arbitrary size to data of fixed size.

In your case, where you're essentially looking for an offset as a small integer value, something like this should do the trick:

def simplehash(x, y):
    return (x * y) % 20 - 10

This probably won't give you a very uniform distribution of values, but it should be sufficient for your needs. You could always tweak it a bit for aesthetics. Maybe add one to x and y before multiplying to avoid a glut of zeroes, that sort of thing.

As a side note, you might be able to improve performance a bit with some caching:

from functools import lru_cache

@lru_cache(maxsize=100)  # definitely tweak maxsize as needed
def simplecache(y, x):
   ...

lru_cache is pretty new, I think it was introduced in Python 3.3 but there are backports available for Python 2 if needed.

Chris Lawlor
  • 41,304
  • 11
  • 45
  • 67
  • Thanks, nice idea with the `%20 - 10`, I'm trying to get an even distribution with using the `id` of each number, it's a bit harder than I thought but I'm getting there aha. – Peter Nov 18 '15 at 19:49
  • So, slight problem. This is how your formula looks (zoomed out) - http://i.imgur.com/lcj8OYC.png, and this is how the cantor pairing function looks - http://i.imgur.com/xr7fcLy.png. Turns out it's actually quite difficult to do :P – Peter Nov 18 '15 at 20:20
  • Alright, got it reasonably working. By using the inbuilt python hash on strings (I did `x=hash(str(x) + str(y / 2)); y=hash(str(y * x))`), it provides good enough values for the cantor pairing function, which I didn't link to before but I saw it here - http://stackoverflow.com/questions/919612/mapping-two-integers-to-one-in-a-unique-and-deterministic-way – Peter Nov 18 '15 at 20:39