📜 ⬆️ ⬇️

We relieve the player from irritation: the correct use of random numbers

image

If you happen to chat with an RPG fan, you will soon hear complaints about randomized results and loot, as well as how annoying they can be. Many gamers express their annoyance, and while some developers come up with innovative solutions, the majority still forces us to pass into enraging checks for perseverance.

But there is a better way. Using random numbers and generating them in a different way, we can create an exciting gameplay that creates an “ideal” level of complexity, without dislodging players. But before we get to this, let's look at the basics of random number generators (or RNG).

Random Number Generator and its Applications


Random numbers are found everywhere and are used to add variation to the software. In general, RNGs are usually used to model chaotic events, to demonstrate inconstancy, or to be an artificial limiter.

Most likely, you interact daily with random numbers or the results of their actions. They are used in scientific experiments, video games, animations, art, and in virtually every computer application. For example, the RNG is most likely implemented in simple animations on your phone.
')
Now that we have talked a little about the RNG, let's take a look at their implementation and learn how to use them to improve games.

Standard random number generator


In almost every programming language, among other functions, there is a standard RNG. His job is to return a random value in the range of two numbers. In different systems, standard RNGs can be implemented in dozens of different ways, but in general they have the same effect: they return a random number from the interval, each value in which can be selected with the same probability.

In games, generators are often used to simulate bone rolls. Ideally, they should be used only in situations where each of the results should occur an equal number of times.

If you want to experiment with rarity or different degrees of randomization, then the following method is more suitable for you.

Weighted random numbers and rarity slots


This type of RNG is the basis for any RPG with item rarity system. In particular, it applies when you need a randomized result, but some values ​​should drop out with less frequency than others. When studying probabilities, a bag of balls is often given as an example. With a weighted RNG, there may be three blue balls and one red in the bag. Since we only need one ball, we will get either red or blue, but more likely it will be blue.

Why may weighted randomization be important? Let's take the SimCity in-game events as an example. If each event were chosen in unweighted ways, then the probability of each event would be statistically the same. That is, with the same probability you would be offered to open a new casino or an earthquake would occur. By adding weights, we can make these events happen in proportionate probability, ensuring good gameplay.

Types and uses


Grouping the same items


In many books on computer science, this method is called "bag". The name speaks for itself - classes or objects are used to create a visual representation of the bag in the literal sense.

In fact, it works like this: there is a container in which objects can be placed, a function for placing an object in a “bag” and a function for randomly selecting an item from a “bag”. Returning to our example with the balls, we can say that our bag will contain blue, blue, blue and red balls.

Using this method of randomization, we can approximately set the frequency of occurrence of the results in order to average the gameplay for each player. If we simplify the results to the scale from “Very bad” to “Very good”, we will get a much more pleasant system compared to the one when a player can get an unnecessary sequence of unwanted results (for example, “Very bad” results 20 times in a row).

However, it is still possible to statistically get a series of bad results, just the probability of this has decreased. We will look at a method that goes a little further to reduce unwanted results.

Here is a brief example of what the bag class pseudocode might look like:

Class Bag { //    ,    Array itemsInBag; //      Constructor (Array startingItems) { itemsInBag = startingItems; } //   ,   (      ) Function addItem (Object item) { itemsInBag.push(item); } //       random,     Function getRandomItem () { return(itemsInBag[random(0,itemsInBag.length-1)]); } } 

Implementation of rarity slots


Rare slots are a standardization method for setting the frequency of dropping an object (usually used to simplify the process of creating a game's design and player rewards).

Instead of setting the frequency of each individual item in the game, we create a corresponding rarity - for example, the rarity "Normal" may represent the probability of a certain result 20 to X, and the rarity level "Rare" - the probability 1 to X.

This method does not greatly change the function of the bag, but it can be used to increase efficiency on the developer’s side, allowing you to quickly assign the statistical probability of an exponentially large number of objects.

In addition, splitting rarities into slots is useful for changing player perceptions. It allows you to quickly and without having to mess with numbers to understand how often an event should occur so that the player does not lose interest.

Here is a simple example of how we can add rarity slots to our bag:

 Class Bag { //    ,    Array itemsInBag; //   ,   Function addItem (Object item) { //     Int timesToAdd; //    //(       , //  ) Switch(item.rarity) { Case 'common': timesToAdd = 5; Case 'uncommon': timesToAdd = 3; Case 'rare': timesToAdd = 1; } //         While (timesToAdd >0) { itemsInBag.push(item); timesToAdd--; } } } 

Variable frequency random numbers


We talked about some of the most common ways of dealing with accidents in games, so let's move on to more complicated ones. The concept of using variable frequencies begins in the same way as the bag in the examples above: we have a specified number of results, and we know how often we want them to occur. The difference in implementation is that we want to change the probability of the results when they occur.

Why do we need this? Take, for example, collecting games. If we have ten possible outcomes for the item to be obtained, when nine are “normal” and one is “rare”, then the probabilities are very simple: 90% of the time a player will receive a normal item, and 10% of the time - a rare one. The problem arises when we take into account several puffs from the bag.

Let's look at our chances of getting a series of ordinary results:


That is, although the initial 9: 1 ratio seemed ideal to us, in fact, it corresponds only to average results, that is, 1 in 10 players will spend twice the desired amount on getting a rare item. Moreover, 4% of players will spend on receiving a rare item three times longer, and 1.5% of losers - four times more.

How do variable frequencies solve this problem?


The solution is to implement an interval of randomness in our objects. To do this, we set the maximum and minimum rarity of each object (or rarity slots, if you want to combine this method with the previous example). For example, let's give our ordinary object a minimum rarity value of 1, and a maximum value of 9. A rare item will have a minimum and maximum value of 1.

Now, according to the scenario shown above, we will have ten items, nine of which are instances of the usual, and one - rare. At the first draw, there is a 90% chance of getting a regular item. At variable frequencies, after stretching a regular object, we reduce its rarity value by 1.

At the same time, in the next stretch, we will have a total of nine items, eight of which are ordinary, which gives a probability of 89% of the usual draw. After each result with the usual object, the randomness of this object falls, which increases the probability of drawing out the rare, until we are left with two objects in the bag, one ordinary and one rare.

Thus, instead of a 35% probability of drawing out 10 ordinary objects in a row, we will have only 5% probability left. The probability of boundary results, such as pulling out 20 ordinary objects in a row, is reduced to 0.5%, and then it becomes even smaller. This creates permanent results for the players, and protects us from boundary cases in which the player constantly draws a bad result.

Creating a class of variable frequencies


The simplest implementation of a variable frequency is to remove the item from the bag, and not just return it:

 Class Bag { //    ,    Array itemsInBag; //      Constructor (Array startingItems) { itemsInBag = startingItems; } //   ,   (      ) Function addItem (Object item) { itemsInBag.push(item); } Function getRandomItem () { //pick a random item from the bag Var currentItem = itemsInBag[random(0,itemsInBag.length-1)]; //    ,     If (instancesOf (currentItem, itemsInBag) > currentItem.minimumRarity) { itemsInBag.remove(currentItem); } return(currentItem); } } 

Although some problems appear in such a simple version (for example, the bag gradually enters a state of normal distribution), it displays minor changes to stabilize randomization results.

Idea development


We have described the basic idea of ​​variable frequencies, but in our own implementations we need to consider many more aspects:


Less boring random numbers


In many games, standard random number generation is still used to create difficulty. This creates a system in which half of the players deviates in one direction or the other from the expected results. If left unattended, there will be the possibility of boundary cases with too many repeated bad results.

By limiting the scatter intervals, we can provide a more holistic gameplay and allow more players to enjoy it.

Summarize


Random number generation is one of the pillars of good game design. To improve the gameplay, carefully check your statistics and implement the most appropriate types of generation.

Source: https://habr.com/ru/post/354132/


All Articles