Skip to content

Commit

Permalink
Add C# to random number generation page
Browse files Browse the repository at this point in the history
  • Loading branch information
skyace65 committed Jan 17, 2021
1 parent 0e881bf commit e7f0de0
Showing 1 changed file with 239 additions and 13 deletions.
252 changes: 239 additions & 13 deletions tutorials/math/random_number_generation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,53 @@ In global scope, you can find a :ref:`randomize()
once when your project starts to initialize the random seed.** Calling it
multiple times is unnecessary and may impact performance negatively.

Putting it in your main scene script's ``_ready()`` method is a good choice::
Putting it in your main scene script's ``_ready()`` method is a good choice:

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

func _ready():
randomize()

.. code-tab:: csharp

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


You can also set a fixed random seed instead using :ref:`seed()
<class_@GDScript_method_seed>`. Doing so will give you *deterministic* results
across runs::
across runs:

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

func _ready():
seed(12345)
# To use a string as a seed, you can hash it to a number.
seed("Hello world".hash())

.. code-tab:: csharp

// To use a string as a seed, you can hash it to a number.
GD.Seed((ulong)"Hello world".Hash());

When using the RandomNumberGenerator class, you should call ``randomize()`` on
the instance since it has its own seed::
the instance since it has its own seed:

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

var rng = RandomNumberGenerator.new()
rng.randomize()

.. code-tab:: csharp

var rng = new RandomNumberGenerator();
rng.Randomize();

Getting a random number
-----------------------

Expand All @@ -71,14 +98,25 @@ random numbers in Godot.
The function :ref:`randi() <class_@GDScript_method_randi>` returns a random
number between 0 and 2^32-1. Since the maximum value is huge, you most likely
want to use the modulo operator (``%``) to bound the result between 0 and the
denominator::
denominator:

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

# Prints a random integer between 0 and 49.
print(randi() % 50)

# Prints a random integer between 10 and 60.
print(randi() % 51 + 10)

.. code-tab:: csharp

// Prints a random integer between 0 and 49.
GD.Print(GD.Randi() % 50);

// Prints a random integer between 10 and 60
GD.Print(GD.Randi() % 51 + 10);

:ref:`randf() <class_@GDScript_method_randf>` returns a random floating-point
number between 0 and 1. This is useful to implement a
:ref:`doc_random_number_generation_weighted_random_probability` system, among
Expand All @@ -89,33 +127,64 @@ floating-point number between 0 and 1. Unlike :ref:`randf()
<class_@GDScript_method_randf>` which follows an uniform distribution, the
returned number follows a `normal distribution
<https://en.wikipedia.org/wiki/Normal_distribution>`__. This means the returned
value is more likely to be around 0.5 compared to the extreme bounds (0 and 1)::
value is more likely to be around 0.5 compared to the extreme bounds (0 and 1):

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

# Prints a normally distributed floating-point number between 0.0 and 1.0.
var rng = RandomNumberGenerator.new()
rng.randomize()
print(rng.randfn())

.. code-tab:: csharp

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

:ref:`rand_range() <class_@GDScript_method_rand_range>` takes two arguments
``from`` and ``to``, and returns a random floating-point number between ``from``
and ``to``::
and ``to``:

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

# Prints a random floating-point number between -4 and 6.5.
print(rand_range(-4, 6.5))

.. code-tab:: csharp

// Prints a random floating-point number between -4 and 6.5.
GD.Print(GD.RandRange(-4, 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``::
and ``to``, and returns a random integer between ``from`` and ``to``:

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

# Prints a random floating-point number between -10 and 10.
var rng = RandomNumberGenerator.new()
rng.randomize()
print(rng.randi_range(-10, 10))

.. code-tab:: csharp

// Prints a random floating-point number between -10 and 10.
var rng = new RandomNumberGenerator();
rng.Randomize();
GD.Print(rng.RandiRange(-10, 10));

Get a random array element
--------------------------

We can use random integer generation to get a random element from an array::
We can use random integer generation to get a random element from an array:

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

var fruits = ["apple", "orange", "pear", "banana"]

Expand All @@ -135,8 +204,34 @@ We can use random integer generation to get a random element from an array::
# We may get the same fruit multiple times in a row.
return random_fruit

.. code-tab:: csharp

string[] fruits = {"apple", "orange", "pear", "banana"};

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

for (int i = 0; i < 100; i++)
{
// Pick 100 fruits randomly.
GD.Print(get_fruit());
}
}

public string get_fruit()
{
string random_fruit = fruits[GD.Randi() % fruits.Length];
// Returns "apple", "orange", "pear", or "banana" every time the code runs.
// We may get the same fruit multiple times in a row.
return random_fruit;
}

To prevent the same fruit from being picked more than once in a row, we can add
more logic to this method::
more logic to this method:

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

var fruits = ["apple", "orange", "pear", "banana"]
var last_fruit = ""
Expand Down Expand Up @@ -166,6 +261,38 @@ more logic to this method::
# The function will never return the same fruit more than once in a row.
return random_fruit

.. code-tab:: csharp

string[] fruits = {"apple", "orange", "pear", "banana"};
string last_fruit = "";

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

for (int i = 0; i < 100; i++)
{
// Pick 100 fruits randomly.
GD.Print(get_fruit());
}
}

public string get_fruit()
{
string random_fruit = fruits[GD.Randi() % fruits.Length];
while (random_fruit == last_fruit)
{
// The last fruit was picked, try again until we get a different fruit.
random_fruit = fruits[GD.Randi() % fruits.Length];
}

last_fruit = random_fruit;

// Returns "apple", "orange", "pear", or "banana" every time the code runs
// The function will never return the same fruit more than once in a row.
return random_fruit;
}

This approach can be useful to make random number generation feel less
repetitive. Still, it doesn't prevent results from "ping-ponging" between a
limited set of values. To prevent this, use the :ref:`shuffle bag
Expand All @@ -174,7 +301,10 @@ limited set of values. To prevent this, use the :ref:`shuffle bag
Get a random dictionary value
-----------------------------

We can apply similar logic from arrays to dictionaries as well::
We can apply similar logic from arrays to dictionaries as well:

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

var metals = {
"copper": {"quantity": 50, "price": 50},
Expand All @@ -196,6 +326,41 @@ We can apply similar logic from arrays to dictionaries as well::
# The same metal may be selected multiple times in succession.
return random_metal

.. code-tab:: csharp

struct MetalInfo
{
public MetalInfo(int quantity, int price)
{
this.quantity = quantity;
this.price = price;
}
public int quantity;
public int price;
}

enum MetalType
{
copper,
silver,
gold,
}
Godot.Collections.Dictionary<MetalType, MetalInfo> metals = new Godot.Collections.Dictionary<MetalType, MetalInfo>
{
{ MetalType.copper, new MetalInfo(50, 50) },
{ MetalType.silver, new MetalInfo(20, 150) },
{ MetalType.gold, new MetalInfo(3, 500) }
};

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


.. _doc_random_number_generation_weighted_random_probability:

Expand All @@ -204,7 +369,10 @@ Weighted random probability

The :ref:`randf() <class_@GDScript_method_randf>` method returns a
floating-point number between 0.0 and 1.0. We can use this to create a
"weighted" probability where different outcomes have different likelihoods::
"weighted" probability where different outcomes have different likelihoods:

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

func _ready():
randomize()
Expand All @@ -226,6 +394,38 @@ floating-point number between 0.0 and 1.0. We can use this to create a
# 5% chance of being returned.
return "Rare"

.. code-tab:: csharp

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

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

public string get_item_rarity()
{
float random_float = GD.Randf();

if (random_float < 0.8)
{
// 80% chance of being returned
return "common";
}
if (random_float > 0.95)
{
// 15% change of being returned
return "Uncommon";
}
else
{
// 5% chance of being returned
return "rare";
}

.. _doc_random_number_generation_shuffle_bags:

"Better" randomness using shuffle bags
Expand All @@ -238,7 +438,10 @@ 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`.
Expand Down Expand Up @@ -266,6 +469,8 @@ ends up empty. When that happens, you reinitialize it to its default value::
# Prints "apple", "orange", "pear", or "banana" every time the code runs.
return random_fruit

.. code-tab:: csharp

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 @@ -281,7 +486,10 @@ time, or anything else.
To achieve this, you can use random *noise* functions. Noise functions are
especially popular in procedural generation to generate realistic-looking
terrain. Godot provides :ref:`class_opensimplexnoise` for this, which supports
1D, 2D, 3D, and 4D noise. Here's an example with 1D noise::
1D, 2D, 3D, and 4D noise. Here's an example with 1D noise:

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

var noise = OpenSimplexNoise.new()

Expand All @@ -297,3 +505,21 @@ terrain. Godot provides :ref:`class_opensimplexnoise` for this, which supports
# Prints a slowly-changing series of floating-point numbers
# between -1.0 and 1.0.
print(noise.get_noise_1d(i))

.. code-tab:: csharp

public override void _Ready()
{
var noise = new OpenSimplexNoise();
GD.Randomize();
// Configure the OpenSimplexNoise instance.
noise.Seed = (int)GD.Randi();
noise.Octaves = 4;
noise.Period = (float)20.0;
noise.Persistence = (float)0.8;

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

0 comments on commit e7f0de0

Please sign in to comment.