## Random Number Generation in Warframe

2016

##### Disclaimer

The information included in this post is for educational purposes only. Any material on this webpage may not be reproduced, retransmitted, or redisplayed other than for personal or educational use.

##### Introduction

Random number generation is a process which generates a sequence of numbers or symbols that cannot be reasonably predicted better than by a random chance. Warframe uses Donald Knuth’s variation of a linear congruential generator (LCG) as a base for its random number generation algorithm and scales from rarity weights assigned by Digital Extremes that influence the drop chances of items in `DropTables` and `MissionDecks`. The recursive aspect of the algorithm only applies to missions in which players receive rewards over time, such as Defense and Survival. Other mission types simply use the process explained below.

##### Rarity Weights

Warframe uses four rarity weights in its random number generation algorithm. These weights are uniform across `DropTables` and `MissionDecks`, except for any table that uses `FixedWeights` (weights of fixed values that may deviate from the standard).

Rarity Weight Percentage
COMMON 0.755 75.50%
UNCOMMON 0.22 22.00%
RARE 0.02 2.00%
LEGENDARY 0.005 0.50%

##### Formulae

The independent drop chance of an item can be computed through the following equation, with `Rarity` being COMMON, UNCOMMON, RARE, or LEGENDARY:

Rarity Drop Chance Per Item = Base Rarity Drop Chance / Number of Rarity Items

Exercise 1. This exercise demonstrates the formulae with a test case of 8 COMMON, 6 UNCOMMON, 4 RARE, and 2 LEGENDARY items.

Rarity Weight Count Quotient Drop Chance Per Item
COMMON 0.755 8 0.094375 9.44%
UNCOMMON 0.22 6 0.03666666666 3.67%
RARE 0.02 4 0.005 0.50%
LEGENDARY 0.005 2 0.0025 0.25%

##### Normalization

Normalization refers to the division of available values in order that rarity weights of all items within a table fall between zero and one and amount to one. Normalization occurs when at least one of the rarity weights is not present within a `DropTable` or `MissionDeck`.

Exercise 1. This exercise demonstrates the normalization procedure with a test case of 1 COMMON, 1 UNCOMMON, 1 RARE, and 0 LEGENDARY items. The value of any rarity weight that does not exist within a table is zero.

Rarity Weight Normalization Procedure Count Quotient Drop Chance Per Item
COMMON 0.755 0.755 / (0.755 + 0.22 + 0.02) 1 0.75879396984 75.88%
UNCOMMON 0.22 0.22 / (0.755 + 0.22 + 0.02) 1 0.22110552763 22.11%
RARE 0.02 0.02 / (0.755 + 0.22 + 0.02) 1 0.02010050251 2.01%
LEGENDARY 0 0 / (0.755 + 0.22 + 0.02) 0 0 0.00%

##### Attenuation

Attenuation is a variable exclusive to `DropTables`. The boolean variable `OverrideLevelAdjustedBiasAtten` determines whether attenuation is present within a `DropTable` or not. Attenuation is defined as "the reduction of the force, effect, or value of something." As the value of attenuation increases, the drop chance of the item it impacts should decrease. However, due to the insignificance of its set value (0.5) and the fact that it impacts `DropTables` globally rather than individually across items, it is impossible to determine if it has a noticeable effect at all.

##### Bias

Bias is a variable exclusive to `DropTables`. Bias is applied to individual items within `DropTables`. Bias is defined as "a feature of a statistical technique or of its results whereby the expected value of the results differs from the true underlying quantitative parameter being estimated." The intended purpose of bias is to unequally weigh items within `DropTables` (through a positive (+) or negative (−) value change), even if said items have identical rarity weights. In Warframe, as the value of bias increases, the drop chance of an item decreases. Additionally, because bias scales from the rarity weight that the item it is impacting is assigned to, items with rarity weights of a higher value will be reduced more drastically, depending on the amount of bias which is present. This is evident from the following tables:

Specter Mod Drop Chance Bias Count Observed
Feyarch Specter Shotgun Amp 45.83% 0.05 22 52.38%
Feyarch Specter Empowered Blades 4.17% 0 3 7.14%
Feyarch Specter Final Harbinger 45.83% 0.1 14 33.33%
Feyarch Specter High Noon 4.17% 0 3 7.14%

Specter Mod Drop Chance Bias Count Observed
Knave Specter Pistol Amp 45.83% 0.05 60 53.57%
Knave Specter Growing Power 4.17% 0 4 3.57%
Knave Specter Blind Justice 45.83% 0.1 43 38.39%
Knave Specter Crimson Dervish 4.17% 0 5 4.46%

Specter Mod Drop Chance Bias Count Observed
Orphid Specter Stand United 30.56% 0.05 29 27.62%
Orphid Specter Brief Respite 30.56% 0 51 48.57%
Orphid Specter Atlantis Vulcan 30.56% 0.1 17 16.19%
Orphid Specter Crossing Snakes 8.33% 0 8 7.62%

##### Reward Seeds

`rewardSeed` is a variable exclusive to `MissionDecks`, and its function is to determine the `missionReward` players receive at the end of a mission. `rewardSeeds` are given to the host, and members of the host’s group receive the `sessionId` in order to participate in the same session. You will only receive a `rewardSeed` when your client needs to distribute it to players in a group (as the host). This means that you will receive a `rewardSeed` if you begin a Public, Friends Only, or Invite Only session. But if you begin a Solo session, then you will not be given a `rewardSeed`. Despite the `SRand` variable (the seeding for the random number generator which generates a pseudo-random integer between 0 and RAND_MAX) being different for each player, each player will always receive the same `missionReward` as the host because of their identical `sessionIds`.

##### Sample Code
###### Random Seed Generator

This generates text simulations of an integer seeded by an SRand variable based upon time. The following is my implementation in C:

``````#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main ()
{
int n = 0;
printf ("NULL SRand:\t %d\n", rand ());
srand (time (0));
for (n = 0; n <= 5; n++)
{
printf ("SRand seeded with:\t %d\n", rand ());
}
return 0;
}``````
###### Weighted Random Number Generator

This generates text simulations of weighted random number generation based upon one million instances. Initial values are set such that it emulates the Warframe random number generation algorithm. The following is my implementation in PHP:

``````<?php
function WarframeRNG() {
\$Uncommon = 0.22 * 1000;
\$Rare = 0.02 * 1000;
\$Legendary = 0.005 * 1000;
\$RarityClass = array();
for (\$i = 0; \$i <= 1000; \$i++) {
if (\$i <= \$Legendary) {
\$RarityClass[\$i] = "LEGENDARY";
} else if (\$i <= \$Legendary + \$Rare) {
\$RarityClass[\$i] = "RARE";
} else if (\$i <= \$Legendary + \$Rare + \$Uncommon) {
\$RarityClass[\$i] = "UNCOMMON";
} else {
\$RarityClass[\$i] = "COMMON";
}
}
\$Rarity = mt_rand(1, 1000);
return (\$RarityClass);
}
\$RarityArray = WarframeRNG();
\$i = 0;
\$CommonDrop = 0;
\$UncommonDrop = 0;
\$RareDrop = 0;
\$LegendaryDrop = 0;
\$times_to_run = 1000000;
\$array = array();
while (\$i++ < \$times_to_run) {
\$RarityClass = \$RarityArray[mt_rand(1, 1000)];
if (\$RarityClass == "COMMON") {
\$CommonDrop = \$CommonDrop + 1;
} else if (\$RarityClass == "UNCOMMON") {
\$UncommonDrop = \$UncommonDrop + 1;
} else if (\$RarityClass == "RARE") {
\$RareDrop = \$RareDrop + 1;
} else if (\$RarityClass == "LEGENDARY") {
\$LegendaryDrop = \$LegendaryDrop + 1;
}
}
echo "COMMON: " . \$CommonDrop . ", ";
echo "UNCOMMON: " . \$UncommonDrop . ", ";
echo "RARE: " . \$RareDrop . ", ";
echo "LEGENDARY: " . \$LegendaryDrop;
?>``````