Description

Rarity Levels

rarity.h defines nine rarity levels, as follows:

Very Common
Common
Unusual
Very Unusual
Rare
Very Rare
Exotic
Very Exotic
Special

The last level, Special, is not a true rarity level. It is used for things
that should never be selected in any random pool, but nonetheless need a
rarity associated with them. Some materials, for example, set this rarity
so that they will never show up randomly.

The other levels indicate progressively increasing rarity. The relationships
between the levels are as follows:

Common is half as likely as Very Common
Unusual is half as likely as Common
Very Unusual is two-fifths as likely as Unusual
Rare is half as likely as Very Unusual
Very Rare is half as likely as Rare
Exotic is one-fifth as likely as Very Rare
Very Exotic is half as likely as Exotic

If you had a pool of at least one thing from each rarity level, with no
likelihood adjustments, the percentage chance of getting the thing from
each rarity level would be:

Very Common 51.74%
Common 25.87%
Unusual 12.93%
Very Unusual 5.17%
Rare 2.58%
Very Rare 1.29%
Exotic 0.25%
Very Exotic 0.12%

When working with pools that do not include all the rarity levels, the
base probabilities shift, according to the relative likelihood of each
level. For instance, if you had only Very Common and Common things in
your pool, you would get a Very Common thing 2/3 of the time and a Common
thing the other 1/3. If you had only items of Unusual rarity and above,
the percentages would be:

Takes the same arguments as rarity_select() and returns the final mapping
of elements to likelihoods. "Likelihoods" are integer proportions, after
the fashion used by random_element(); the mapping returned by this function
is suitable to be passed directly to random_element().

If the third argument is true, then rarity_distribution() will be allowed
to select randomly from among the rarity levels in its distribution and
return the array of items from that level, rather than returning a full
distribution. This can only be done when no adjustment values are being
provided.

mapping Rarity_Analyze_Distribution(mapping distribution)

Takes an argument of a mapping containing values mapped to likelihoods
(integer proportions), as returned by rarity_distribution() or macros like
Selected_Materials_Distribution(), or as generally used with the function
random_element(). The return value is a mapping of the argument's keys
values mapped to float percentage chances of the value being selected by
random_element(). Useful for getting an idea of the actual percentages
you're winding up with from a likelihood distribution.

mixed Rarity_Distribution_Relative(mapping select, mapping adjust)

Similar to rarity_distribution(), but rather than returning a mapping with
integer proportions defining absolute weights, returns a mapping with
float proportions defining relative weights. The smallest proportion is
arbitrarily assigned the value of 1.0, and the other proportions have
values indicating their magnitude relative to that proportion. This is
mainly useful in cases where a rarity distribution is being used in
conjunction with functions like distribute_proportionally(), and the
values in play are large enough to potentially cause overflow errors if
integers are used.

mixed Rarity_Select_Simple(mapping select, mapping adjust)

Similar to Rarity_Select(), but does a simple distribution as described
below.

mixed Rarity_Distribution_Simple(mapping select, mapping adjust)

Similar to Rarity_Distribution(), but does a simple distribution as
described below.

How Rarity Becomes Likelihood

When rarity_distribution() turns a mapping of values to rarities into a
mapping of values to likelihoods, it normally does so in a way that gives
results that may be counterintuitive until you understand what it's doing.

The first thing to understand is that, before adjustments are taken into
account, the chance of a value of a given rarity being selected is held
constant. This means that no matter how many Common values you have in
your mapping, you have the same chance of getting a Common item. You can
think of this almost as if the mechanism were selecting a rarity level
first, and then randomly selecting a value from within that rarity level
only.

One thing this means is that, when looking at final percentages (as when
using analyze_distribution()), you can actually wind up with individual
values of Exotic rarity being more likely than individual values of Rare
rarity, because there are relatively few Exotic values and relatively
many Rare values. The overall likelihood of the Rare category winds up
being split enough ways that the likelihood of individual values in the
category falls below the likelihood of individual Exotic values.

This becomes more complex when likelihood adjustments are present, as our
next section explores.

The exception to this is when using the "simple" distribution functions.
In these cases, the rarity values are simply used as weights, with no
attempt to hold the probability of rarity levels constant with respect to
each other. This is most appropriate when you are dealing with a small
set of items in your rarity distribution, which would cause somewhat
distorted behavior when the probability manipulations described above are
used.

Likelihood Adjustments

The optional second argument to rarity_select() and rarity_distribution()
allows you to tune the likelihood of individual values in your rarity
map. The adjustment argument is a map of values to float multipliers for
the value's likelihood, as shown above in the documentation for the
rarity_select() function. If a value does not appear in the map, its
likelihood is not adjusted.

The manipulations performed by the adjustment map violate the principle
of relative likelihood of rarity levels being held constant. If you have
one Common value and one Unusual value, and adjust the Unusual value's
likelihood by 2.0, you wind up with the two values having the same
likelihood. This is intentional, and done so that adjustments remain
meaningful across rarity levels.

For a more involved example, look at the randomize_ring_craft() function
in /std/app/rings/ordinary.c. That shows a way you might use an adjustment
map set up according to situational factors to alter a basic rarity map.