Iterate over a list in pseudo-random order without saving the shuffled list

In the game , we use a method called color picker to select units.

This means that each visible device is assigned a unique color.

Here is an example of a scene made to select a color:

enter image description here

Since some users may have 16-bit displays, these colors may be in the range of 0..64K.

However, if we give the units incremental colors, for example. unit0 is 0, unitN is N, then the colors are very difficult to debug people. Units are almost indistinguishable.

We want to give units unique and distinctive colors.

We are currently increasing the fixed pitch using a binary tree (C ++ map ) to store the colors used to check for conflicts. This is a low level hardware performance issue. Even if it was a hash table and avoiding the use of string , it is necessary to limit the temporary allocation of memory in game frames. Therefore, instead of optimizing the code in its current form, I really want to find out if there are ways to avoid the full story.

Is there a way to iterate over 0..64K numbers in large increments or randomly, so that most of the 64K possible values ​​are used and avoid using the history of colors already highlighted to avoid collisions?

(It is unlikely that there are more than 64K visible blocks on the screen that we do not need to process in this case!)

+8
c ++ random
source share
5 answers

My attempt:

  • Choose the total number next to your desired range (64007 is a good candidate here).
  • Find primitive roots modulo p of this number.
  • Choose a primitive mid-range root ( 43062 43067 is a good candidate).

     class Sequence { public: uint32_t get() const {return state;} void advance() { state = (state * k)%p;} void reset() { state = k; } private: uint32_t state = 43067; const uint32_t k = 43067; const uint32_t p = 64007; }; 

These are cyclically all numbers in the range [1, 64007] exactly once, in a pseudo-random manner.

+8
source share

Can you just make step_size equal to the total available colors divided by the common units and then use (unit_index * step_size) as the color for each block?

+1
source share

I really don't see the problem. As I wrote in the comments, only 128K is required to store the permutation [0..64K], and you do not need any allocation inside the main loop. Here's a stateful color repository in C ++ 11 (in earlier C ++ use vector or new[] ):

 class ColorPicker { std::array<uint16_t, 65536> colors; uint16_t idx; public: ColorPicker() : idx(0) { std::iota(colors.begin(), colors.end(), 0); std::random_shuffle(colors.begin(), colors.end()); } uint16_t next_color() { return colors[idx++]; } }; 

You need only one of these structures. Now, when you create a new block, call next_color in ColorPicker and save it on the device as an attribute.

This solution will cycle in color. If this is not desired, do random_shuffle every time the index wraps to zero.

+1
source share

It seems to me that it is important that the contrast between units that are close to each other is quite high, i.e. I will try to somehow take into account the proximity of units.

For example, you can take into account the coordinates of X / Y units, so that coordinates that are close to each other acquire colors with high contrast, low contrast is used only for sufficiently distant units.

The first shot may be to have a simple array a of 256 colors, so there is a lot of contrast between a[n] and a[n+1] . Then you can select the color of the units using their X and / or Y coordinate modulo 256 as an index in the array. This way you get colors that are reusable for units that are at least 256 pixels (or any other metric that you can use), but different colors for units that are very close to each other.

+1
source share

Use binary files first to preserve color states (1-used, 0-not used). 2 ^ 16 = 65536 (states). If we use 1 bit for one color, 65536/8 = 8192 bytes are needed.
The next question is how to manage these bytes. I suggest a tree structure: on these 8192 bytes one more (8192/8 =) 1024 bytes is required, each bit in these upper bytes represents one byte in 8192 bytes, if one of the lower bytes is ALL 1, its upper bit is 1.
This rule can expand up and down: 8192 β†’ 1024 β†’ 128 ... finally, up to 1 byte (not fully used).
To use this structure, you can generate a random number 0..7 many times, starting with the root byte, if its bit is 1, try again; if its 0, to the lower byte, repeat this action until the low byte is reached.
In addition, you can build this tree in one array: just like a heap in a heapsort. (with some empty units).

APPENDIX: int16 requires one color, once before the low byte, you get a three-bit binary number, add them from left to right on the color number: int16. (The root byte represents only 2 states and generates only a single-bit binary number, its form is 111111 ??.

0
source share

All Articles