Man rarity
From LSWiki
Current revision
Contents |
Files
/lib/rarity.h /mod/global/rarity.c
Description
This document describes the lib's rarity mechanics. These are mainly used in applications of the selection parser (see 'man selection'), but can be employed by other code as well.
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:
Unusual 57.80% Very Unusual 23.12% Rare 11.56% Very Rare 5.78% Exotic 1.15% Very Exotic 0.57%
If you had only things of Rare rarity and above:
Rare 60.60% Very Rare 30.30% Exotic 6.06% Very Exotic 3.03%
Rarity Processing
Rarity processing is handled by /daemon/rarity, which publishes various functions as services. Always use the macros for these functions, as shown below. These functions are:
mixed Rarity_Select(mapping select, mapping adjust)
Returns a randomly selected element of the mapping provided as the first argument, which should look something like this:
([ some_value : Rarity_Common, some_other_value : Rarity_Unusual, yet_another_value : Rarity_Unusual, ])
The optional second argument (supply 0 if none) is a mapping that defines adjustments to the final likelihoods of individual elements, as described later on. It might look like this:
([ some_value : 1.5, some_other_value : 0.1, ])
mixed Rarity_Distribution(mapping select, mapping adjust, status allow_array)
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.
mixed Rarity_Distribution_Simple_Relative(mapping select, mapping adjust)
Similar to Rarity_Distribution_Relative(), 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.
[ Example ]
A quick example of a way you might use a rarity-based selection:
void configure() { string adj = Rarity_Select(([ "happy" : Rarity_Common, "sad" : Rarity_Uncommon, "angry" : Rarity_Rare, ]), 0); alter_identity(Identity_Adjectives, ({ adj }), True); }
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.
See Also
random_element(sefun), selection(mechanisms), selection_core(mechanisms)