-
-
Notifications
You must be signed in to change notification settings - Fork 97
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
Add a method to get a random element with weighted probability (similar to random.choices()
in Python)
#3948
Comments
To add another usecase: I needed something like this in my procedural city generator. I ended up implementing it in gdnative rust because a pure gdscript implementation turned out to be a performance bottleneck in my case. At least when implementing this in a similar way to the cpython implementation (using the cumulative weights internally) this will iterate the whole array at least once. For huge arrays this becomes quite slow in GDScript. I would love to see this added to core, it would have saved me quite some hassle in multiple of my projects. For reference the python implementation: https://github.com/python/cpython/blob/main/Lib/random.py#L477
(This does assume that the cumulative weights list is sorted, which we can if we only allow normal weights to be passed in to the function. Otherwise a check for that would probably be needed) |
If this enhancement will not be used often, can it be worked around with a few lines of script? Here's the simplest form of a weighted random choice algorithm as defined in this web page, returning the chosen index or 0 if the
|
random.choices()
in Python)
@Mickeon As I wrote before, approaches based on commulative weights like the one you posted will require at least one full iteration of the array plus however many elements randomly need to be iterated, this is true for the first ones in the article you linked too. That's basically the same as the bisect method from python as the second for loop basically bisects the weights based on the random value and returns the item with the corresponding index. I see not much difference, except that this lacks the parameters for items and k, which is what makes it so useful for games. But that would be easy to add to your code as well. As it's basically the same as the python one it has the same performance penalty that I ran into in my city generator. Granted, that was on 3.x, so maybe with GDScript 2 arrays perform better. I would need to profile this to be certain though. I still think this would be good to have in core regardless of performance as it's something very useful for a whole range of games, even if this can be done in GDScript. It won't add much engine bloat but it will add a versatile functionality. So in the question "If this enhancement will not be used often, can it be worked around with a few lines of script?" I think the first part can be answered with "it probably will be used often". I can think of many usecases:
|
My reply wasn't in disapproval of the proposal, it was to suggest an additional alternative. In fact, I would argue that some simple way to calculate weighted probability should probably be core. A method may fit well right inside the RandomNumberGenerator class. To be honest, not all games need the algorithm, but those that do would use it frequently. |
I think the For parameters, I'd switch the order to:
because GDScript does not support keyword arguments.
Feel free to do so at Goost. We have But note that there's a related 4 months old proposal #3454 which hasn't been considered for approval, this is why I invite you to contribute to Goost first and hopefully both #3454 and this proposal will eventually land in Godot. If not, that's also ok. You can look at I added this proposal to goostengine/goost#7.
Indeed, that's actually the development philosophy of Goost: completeness. |
When I wrote that reply, I was going to mention that Goost has a weighted random choice method, but it actually does not! That did catch me off-guard. I would say that a weighted probability method is even more needed than pure randomization. Perhaps, it could be optimised in such a way that if no weights are passed in the method, pure random choice is used, anyway. |
Pure The optimization makes sense regardless. But yes, if Godot chooses to implement such a method, it could provide both functionalities as a single method to prevent bloat (this is not an issue in Goost as long as API is clear and well organized). |
I think for now I would just push for a weighted random choice in godot core as a random unweighted pick really is easy to do in GDScript without performance penality like in the weighted choice. So I can understand if a unweighted pick is not done in core (which is where goost is useful for people that want to have a huge number of stuff available). For now I think focussing this proposal on weighted choice is best. |
I agree that weighted pick would be useful in core. It has plenty of uses and there's pretty much only single best way to implement it. Right now I use my custom implementation, in 3 different projects. Sure, it could be an addon, but C++ version would be more performant. It mostly matters in procedural generation, where the method can be called hundreds or thousands of times. As for the implementation, single Dictionary would be better than using 2 arrays. Also, the weights could be integers, for more precision and speed. They can be any number anyways. |
I implemented this in my custom build based on Mickeon's code adapted to C++ and only for Arrays. I don't think Dictionary needs this as you're only selecting arrays of keys really. See here goblinengine/goblin@4dd4a05 and here for an updated version that is closer to Python goblinengine/goblin@33d22b4 Is naively implemented with multiple for loops but it seems to be quite fast about 7-20 usec per call. |
Is this being worked on by anyone? If not, I would like to give it a shot. It seems like it is common agreement that it owuld be useful. |
There is no PR open and it's unlikely someone works on it right now. |
I'm not working on it. Even though I said "I would be up for implementing it.". Please proceed if that was the hold up. |
Describe the project you are working on
Loot generation with weighted probabilities.
{rare: 1, common: 3, empty: 6}
, six slots for loot.Describe the problem or limitation you are having in your project
Using a random float for each roll does not feel like an elegant solution.
Describe the feature / enhancement and how it helps to overcome the problem or limitation
Generates a list of loot which can be used directly.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
['empty', 'empty', 'rare', 'rare', 'common', 'empty']
If this enhancement will not be used often, can it be worked around with a few lines of script?
It can be worked around by computing floats for each item given considering its weight and rolling a random float with randf() multiples times. Weighted Probability in docs
Is there a reason why this should be core and not an add-on in the asset library?
Loot generation of this type is commonly seen in games.
The text was updated successfully, but these errors were encountered: