Skip to content

Commit

Permalink
update Rand choices and add missing info
Browse files Browse the repository at this point in the history
  • Loading branch information
filipworksdev committed Feb 16, 2022
1 parent 4dd4a05 commit 33d22b4
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 20 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
- New `Rand` singleton that allows generating completely random values from anywhere. `Rand` auto randomizes every time the engine starts and since it extends `RandomNumberGenerator` you can also call `randomize()` or `set_seed()` manually.
* `shuffle(Array)` shuffles an Array
* `choice(Variant)` picks a random value from an Array or Dictionary or random character from a string
* `choices(Array, count, weights)` picks count number of random values from Array based on weights
* `color()` generates a completely random color
* `decision(double)` helps generates a random decisions based on a probability from `0.0` to `1.0` (0% to 100%)
* `roll(count,side)` simulates a random dice roll using count and side and returns an Dictionary with sum and rolls.
* `roll_notation(dice_notation`) similar to a roll ecept it uses dice notation such as `2d6`, `2x(3d6!U)`. Als returns a Dictionary but contains additional information.
* `i(from, to)` same as `rand_range` but shorter and slightly faster.
* `f(from, to)` same as `randf_range` but shorter.
* `color()` generates a completely random color
* `uuid_v4()` generated a random UUID hex bytes 8-4-4-4-12 using version 4 of the spec(also known a GUID).
- Added autotile auto-transforms pr found [here](https://github.com/godotengine/godot/pull/39046) to Goblin. The proposal is [here](https://github.com/godotengine/godot-proposals/issues/893). The idea here is to allow specific transforms on autotiles so that when looking up a specific bitmask the autotile is esentially transformed dynamically, based on allowed transformations. Allows for less manual tile work in some situations and smaller texture file. The drawback is the tiles resulting from transforms are repetitive.
- Added `eval("expression")` function in `@GDScript` which parses a string expression and outputs the result or null if couldn't parse. It does not take inputs like Expression but can be added since it actually uses Expression class in the backend. This is a common function in many interpeted languages.
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ Goblin will not provide any Mono builds since the goal is to keep the engine lea
## Community and contributing

There is no community as of yet but PRs welcome. There are a number of features that I am still looking to add:
- Make the engine leander and faster optimizing where necessary
- Add custom GDScript functionality
- Optimize engine size and speed
- More efficient implementations of custom code
- Custom GDScript functionality
- Porting to more platforms
- Implementing features that are beyond my understanding

## Documentation and demos

Expand Down
15 changes: 15 additions & 0 deletions modules/goblin/doc_classes/Rand.xml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@
<description>
Returns a random element from a container or indexable sequence, such as [Array], [Dictionary], [String]. If container is empty, prints an error and returns [code]null[/code].
</description>
</method>
<method name="choices">
<return type="Variant" />
<argument index="0" name="from" type="Variant" />
<argument index="1" name="count" type="int" default="1" />
<argument index="2" name="weights" type="Array" default="null" />
<description>
Returns random elements from an [Array] based on count and weights. This is somewhat similar to [method choice] but it allows you to specify weights for each element. Weights must be an [Array] of positive integers and of the same size as the original [Array]. If no weights are specified, all elements are equally likely.
</description>
</method>
<method name="color">
<return type="Color" />
<description>
Returns a random [Color].
</description>
</method>
<method name="shuffle">
<return type="void" />
Expand Down
46 changes: 29 additions & 17 deletions modules/goblin/rand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,31 +63,43 @@ Variant Rand::choices(const Variant &p_from, int count, const Array &p_weights)
case Variant::POOL_VECTOR3_ARRAY:
case Variant::POOL_COLOR_ARRAY:
case Variant::ARRAY: {
ERR_FAIL_COND_V_MSG(count < 1, Array(), "Count must be positive");

Array arr = p_from;
ERR_FAIL_COND_V_MSG(arr.empty(), Array(), "Array is empty.");
ERR_FAIL_COND_V_MSG(arr.size() != p_weights.size(), Array(), "Array and weights unequal size.");

int weights_sum = 0;
for (int i = 0; i < p_weights.size(); i++) {
if (p_weights.get(i).get_type() == Variant::INT) {
weights_sum += (int)p_weights.get(i);
} else {
ERR_FAIL_V_MSG(Array(), "Weights are not integers.");
}

if (p_weights.size() > 0) {
ERR_FAIL_COND_V_MSG(arr.size() != p_weights.size(), Array(), "Array and weights unequal size.");
}

Array choices = Array();
while(choices.size() < count) {
float remaining_distance = randf() * weights_sum;
if (p_weights.empty()) { //no weights array then everything is weighted randomly
for (int j = 0; j < count; j++) {
choices.append(choice(p_from));
}
} else { //with weights
//calculate weights sum
int weights_sum = 0;
for (int i = 0; i < p_weights.size(); i++) {
remaining_distance -= (int)p_weights.get(i);
if (remaining_distance < 0) {
choices.append(p_from.get(i));
break;
if (p_weights.get(i).get_type() == Variant::INT && (int)p_weights.get(i) >= 0) {
weights_sum += (int)p_weights.get(i);
} else {
ERR_FAIL_V_MSG(Array(), "Weights must be positive integers.");
}
}
}

//loop through based on weights and add until you reach count
while(choices.size() < count) {
float remaining_distance = randf() * weights_sum;

This comment has been minimized.

Copy link
@rusty-tendrils

rusty-tendrils Feb 17, 2022

Can remaining_distance be int ?

for (int i = 0; i < p_weights.size(); i++) {
remaining_distance -= (int)p_weights.get(i);
if (remaining_distance < 0) {
choices.append(arr.get(i));
break;
}
}
}
}
return choices;
} break;
default: {
Expand Down Expand Up @@ -302,7 +314,7 @@ void Rand::_bind_methods() {
ClassDB::bind_method(D_METHOD("f", "from", "to"), &Rand::f);

ClassDB::bind_method(D_METHOD("choice", "from"), &Rand::choice);
ClassDB::bind_method(D_METHOD("choices", "from", "count", "weights"), &Rand::choices);
ClassDB::bind_method(D_METHOD("choices", "from", "count", "weights"), &Rand::choices, DEFVAL(1), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("shuffle", "array"), &Rand::shuffle);
ClassDB::bind_method(D_METHOD("decision", "probability"), &Rand::decision);
ClassDB::bind_method(D_METHOD("roll", "count", "faces"), &Rand::roll);
Expand Down

0 comments on commit 33d22b4

Please sign in to comment.