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

adding Random.choices() #178

Merged
merged 1 commit into from
Feb 19, 2022
Merged

adding Random.choices() #178

merged 1 commit into from
Feb 19, 2022

Conversation

rusty-tendrils
Copy link
Contributor

One of the features approved in #7

@Xrayez Xrayez added enhancement Improvement of existing features feature 💡 New feature proposal topic:core labels Feb 19, 2022
@Xrayez Xrayez added this to the 1.3-gd3 milestone Feb 19, 2022
@@ -126,6 +227,7 @@ void Random::_bind_methods() {

ClassDB::bind_method(D_METHOD("range", "from", "to"), &Random::range);
ClassDB::bind_method(D_METHOD("choice", "from"), &Random::choice);
ClassDB::bind_method(D_METHOD("choices", "from", "count", "weights"), &Random::choices, DEFVAL(1), DEFVAL(Variant()), DEFVAL(false));
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing bind argument:

Suggested change
ClassDB::bind_method(D_METHOD("choices", "from", "count", "weights"), &Random::choices, DEFVAL(1), DEFVAL(Variant()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("choices", "from", "count", "weights", "cumulative"), &Random::choices, DEFVAL(1), DEFVAL(Variant()), DEFVAL(false));

@@ -25,6 +25,7 @@ class Random : public RandomNumberGenerator {

Variant range(const Variant &p_from, const Variant &p_to);
Variant choice(const Variant &p_sequence);
Variant choices(const Variant &p_sequence, int count, const Array &p_weights, bool is_cumulative);
Copy link
Contributor

@Xrayez Xrayez Feb 19, 2022

Choose a reason for hiding this comment

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

Prepend p_ for parameters (Godot naming convention).

Also, given that the code could be used in C++ as well, it's better to provide default arguments in the method signature as well.

The following suggestion applies both ideas:

Suggested change
Variant choices(const Variant &p_sequence, int count, const Array &p_weights, bool is_cumulative);
Variant choices(const Variant &p_sequence, int p_count = 1, const Array &p_weights = Variant(), bool p_cumulative = false);

Don't forget to do the same to cpp file.

Comment on lines 100 to 103
Array cumulative_weights;
Array weights;
Array indices;
Array weighted_choices;
Copy link
Contributor

Choose a reason for hiding this comment

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

If weights/indices can only be integers, I think you could use PoolIntArray instead (both here and as input). If you pass Array, Godot will do the conversion to PoolIntArray automatically. I think this will actually simplify validation down the road.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How do I convert an Array to PoolIntArray ? I would need it for getting dict.values() .
It says no suitable user-defined conversion from 'Array' to 'PoolIntArray' exists

Copy link
Contributor

@Xrayez Xrayez Feb 19, 2022

Choose a reason for hiding this comment

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

Hmm yeah, looks like Godot doesn't provide such conversion. I'd just initialize in a loop.

Variant Random::choices(const Variant &p_from, int count, const PoolIntArray &p_weights, bool is_cumulative) {
	int sum = 0;
	PoolIntArray cumulative_weights;
	PoolIntArray weights;
	PoolIntArray indices;
	PoolIntArray weighted_choices;

	if((p_from.get_type() == Variant::DICTIONARY) && p_weights.empty()){
		Dictionary dict = p_from;
		Array w = dict.values();
		for (int i = 0; i < w.size(); ++i) {
			weights.push_back(w[i]);
		}
	} else {
		weights = p_weights;
	}

There will be other missing functions like back(), you'd have to access the last element manually.

weighted_choices.push_back(str.substr((randi() % str.size()), 1));
}
} else {
ERR_FAIL_COND_V_MSG(str.size() != weights.size(), Variant(), "Size of weights does not match.");
Copy link
Contributor

@Xrayez Xrayez Feb 19, 2022

Choose a reason for hiding this comment

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

Note that size() and length() are different in String. size() includes null character, while length() doesn't. Not sure whether this logic matters here though. But if it does, you'd likely need to use str.length() instead.

@@ -95,6 +95,112 @@ Variant Random::choice(const Variant &p_from) {
return Variant();
}

Variant Random::choices(const Variant &p_from, int p_count, const PoolIntArray &p_weights, bool p_is_cumulative) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Variant Random::choices(const Variant &p_from, int p_count, const PoolIntArray &p_weights, bool p_is_cumulative) {
Array Random::choices(const Variant &p_from, int p_count, const PoolIntArray &p_weights, bool p_is_cumulative) {

Comment on lines 123 to 128
if(int(weights[i]) < 0) {
ERR_FAIL_V_MSG(Array(), "Weights must be positive integers.");
} else {
sum += (int)weights[i];
cumulative_weights.push_back(sum);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think casting is no longer necessary.

Suggested change
if(int(weights[i]) < 0) {
ERR_FAIL_V_MSG(Array(), "Weights must be positive integers.");
} else {
sum += (int)weights[i];
cumulative_weights.push_back(sum);
}
if(weights[i] < 0) {
ERR_FAIL_V_MSG(Array(), "Weights must be positive integers.");
} else {
sum += weights[i];
cumulative_weights.push_back(sum);
}

while (left < right)
{
int mid = (left + right) / 2;
if(int(cumulative_weights[mid]) < random_number) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above.

Suggested change
if(int(cumulative_weights[mid]) < random_number) {
if(cumulative_weights[mid] < random_number) {

@rusty-tendrils
Copy link
Contributor Author

Is returning an Array (instead of a String) a good idea when the input is a String ?
I cannot imagine what it could be used for in the context of games.

@Xrayez
Copy link
Contributor

Xrayez commented Feb 19, 2022

Well, I think String may not be used as often and it's probably specific, but it's a sequence type so better to have support for it. See "Completeness" development philosophy in Goost.

Therefore, having the method return an Array is better in terms of auto-completion and documentation for common cases. Besides, this is what Python does as well it seems:

image

@Xrayez
Copy link
Contributor

Xrayez commented Feb 19, 2022

Thanks and congrats for your first contribution!

@Xrayez Xrayez merged commit 4cfacd3 into goostengine:gd3 Feb 19, 2022
@rusty-tendrils
Copy link
Contributor Author

Thanks, I learnt a lot.

@rusty-tendrils rusty-tendrils deleted the random-choices-feature branch February 20, 2022 13:05
@filipworksdev
Copy link

Nice! I like the cummulative bool approach. I'm still here just migrated all my data to a brand new account (with the same name) :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Improvement of existing features feature 💡 New feature proposal topic:core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants