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

Add C# code to random_number_generation and fix shuffle bag example. #8072

Merged
merged 10 commits into from
Nov 20, 2024
111 changes: 78 additions & 33 deletions tutorials/math/random_number_generation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,12 @@ varying by the deviation (1.0 by default):
.. code-tab:: gdscript GDScript

# Prints a random floating-point number from a normal distribution with a mean 0.0 and deviation 1.0.
var random = RandomNumberGenerator.new()
random.randomize()
print(random.randfn())
print(randfn(0.0, 1.0))

.. code-tab:: csharp

// Prints a normally distributed floating-point number between 0.0 and 1.0.
var random = new RandomNumberGenerator();
random.Randomize();
GD.Print(random.Randfn());
GD.Print(GD.Randfn(0.0, 1.0));

:ref:`randf_range() <class_@GlobalScope_method_randf_range>` takes two arguments
``from`` and ``to``, and returns a random floating-point number between ``from``
Expand All @@ -152,6 +148,11 @@ and ``to``:
# Prints a random floating-point number between -4 and 6.5.
print(randf_range(-4, 6.5))

.. code-tab:: csharp

// Prints a random floating-point number between -4 and 6.5.
GD.Print(GD.RandRange(-4.0, 6.5));

:ref:`RandomNumberGenerator.randi_range()
<class_RandomNumberGenerator_method_randi_range>` takes two arguments ``from``
and ``to``, and returns a random integer between ``from`` and ``to``:
Expand All @@ -160,15 +161,12 @@ and ``to``, and returns a random integer between ``from`` and ``to``:
.. code-tab:: gdscript GDScript

# Prints a random integer between -10 and 10.
var random = RandomNumberGenerator.new()
random.randomize()
print(random.randi_range(-10, 10))
print(randi_range(-10, 10))

.. code-tab:: csharp

// Prints a random integer number between -10 and 10.
random.Randomize();
GD.Print(random.RandiRange(-10, 10));
GD.Print(GD.RandRange(-10, 10));

Get a random array element
--------------------------
Expand All @@ -181,8 +179,6 @@ We can use random integer generation to get a random element from an array:
var _fruits = ["apple", "orange", "pear", "banana"]

func _ready():
randomize()

for i in range(100):
# Pick 100 fruits randomly.
print(get_fruit())
Expand All @@ -200,8 +196,6 @@ We can use random integer generation to get a random element from an array:

public override void _Ready()
{
GD.Randomize();

for (int i = 0; i < 100; i++)
{
// Pick 100 fruits randomly.
Expand All @@ -228,8 +222,6 @@ more logic to this method:


func _ready():
randomize()

# Pick 100 fruits randomly.
for i in range(100):
print(get_fruit())
Expand Down Expand Up @@ -257,8 +249,6 @@ more logic to this method:

public override void _Ready()
{
GD.Randomize();

for (int i = 0; i < 100; i++)
{
// Pick 100 fruits randomly.
Expand Down Expand Up @@ -295,26 +285,49 @@ We can apply similar logic from arrays to dictionaries as well:
.. tabs::
.. code-tab:: gdscript GDScript

var metals = {
var _metals = {
"copper": {"quantity": 50, "price": 50},
"silver": {"quantity": 20, "price": 150},
"gold": {"quantity": 3, "price": 500},
}


func _ready():
randomize()

for i in range(20):
print(get_metal())


func get_metal():
var random_metal = metals.values()[randi() % metals.size()]
var random_metal = _metals.values()[randi() % metals.size()]
# Returns a random metal value dictionary every time the code runs.
# The same metal may be selected multiple times in succession.
return random_metal

.. code-tab:: csharp

private Godot.Collections.Dictionary<string, Godot.Collections.Dictionary<string, int>> _metals = new()
{
{"copper", new Godot.Collections.Dictionary{{"quantity", 50}, {"price", 50}}},
{"silver", new Godot.Collections.Dictionary{{"quantity", 20}, {"price", 150}}},
{"gold", new Godot.Collections.Dictionary{{"quantity", 3}, {"price", 500}}}
};

public override void _Ready()
{
for (int i = 0; i < 20; i++)
{
GD.Print(GetMetal());
}
}

public Godot.Collections.Dictionary GetMetal()
{
var (_, randomMetal) = _metals.ElementAt(GD.Randi() % metals.Count);
// Returns a random metal value dictionary every time the code runs.
// The same metal may be selected multiple times in succession.
return randomMetal;
}

.. _doc_random_number_generation_weighted_random_probability:

Weighted random probability
Expand All @@ -328,8 +341,6 @@ floating-point number between 0.0 and 1.0. We can use this to create a
.. code-tab:: gdscript GDScript

func _ready():
randomize()

for i in range(100):
print(get_item_rarity())

Expand All @@ -351,8 +362,6 @@ floating-point number between 0.0 and 1.0. We can use this to create a

public override void _Ready()
{
GD.Randomize();

for (int i = 0; i < 100; i++)
{
GD.Print(GetItemRarity());
Expand Down Expand Up @@ -392,15 +401,17 @@ could get the same fruit three or more times in a row.

You can accomplish this using the *shuffle bag* pattern. It works by removing an
element from the array after choosing it. After multiple selections, the array
ends up empty. When that happens, you reinitialize it to its default value::
ends up empty. When that happens, you reinitialize it to its default value:

.. tabs::
.. code-tab:: gdscript GDScript

var _fruits = ["apple", "orange", "pear", "banana"]
# A copy of the fruits array so we can restore the original value into `fruits`.
var _fruits_full = []


func _ready():
randomize()
_fruits_full = _fruits.duplicate()
_fruits.shuffle()

Expand All @@ -409,17 +420,53 @@ ends up empty. When that happens, you reinitialize it to its default value::


func get_fruit():
if _fruits.empty():
if _fruits.is_empty():
# Fill the fruits array again and shuffle it.
_fruits = _fruits_full.duplicate()
_fruits.shuffle()

# Get a random fruit, since we shuffled the array,
# and remove it from the `_fruits` array.
var random_fruit = _fruits.pop_front()
# Prints "apple", "orange", "pear", or "banana" every time the code runs.
# Returns "apple", "orange", "pear", or "banana" every time the code runs, removing it from the array.
# When all fruit are removed, it refills the array.
return random_fruit

.. code-tab:: csharp

private Godot.Collections.Array<string> _fruits = new() { "apple", "orange", "pear", "banana" };
// A copy of the fruits array so we can restore the original value into `fruits`.
private Godot.Collections.Array<string> _fruitsFull;

public override void _Ready()
{
_fruitsFull = _fruits.Duplicate();
_fruits.Shuffle();

for (int i = 0; i < 100; i++)
{
GD.Print(GetFruit());
}
}

public string GetFruit()
{
if(_fruits.Count == 0)
{
// Fill the fruits array again and shuffle it.
_fruits = _fruitsFull.Duplicate();
_fruits.Shuffle();
}

// Get a random fruit, since we shuffled the array,
string randomFruit = _fruits[0];
// and remove it from the `_fruits` array.
_fruits.RemoveAt(0);
// Returns "apple", "orange", "pear", or "banana" every time the code runs, removing it from the array.
// When all fruit are removed, it refills the array.
return randomFruit;
}

When running the above code, there is a chance to get the same fruit twice in a
row. Once we picked a fruit, it will no longer be a possible return value unless
the array is now empty. When the array is empty, we reset it back to its default
Expand All @@ -443,7 +490,6 @@ terrain. Godot provides :ref:`class_fastnoiselite` for this, which supports
var _noise = FastNoiseLite.new()

func _ready():
randomize()
# Configure the FastNoiseLite instance.
_noise.noise_type = FastNoiseLite.NoiseType.TYPE_SIMPLEX_SMOOTH
_noise.seed = randi()
Expand All @@ -461,7 +507,6 @@ terrain. Godot provides :ref:`class_fastnoiselite` for this, which supports

public override void _Ready()
{
GD.Randomize();
// Configure the FastNoiseLite instance.
_noise.NoiseType = NoiseTypeEnum.SimplexSmooth;
_noise.Seed = (int)GD.Randi();
Expand Down