Skip to content

SelectionOdds

Namespace Nfty.Core.Stats in assembly Nfty.Core.

public static class SelectionOdds

How likely one asset's exact selection was, computed from the CookBook that produced it.

Remarks

The Set browser could already multiply its rarity table together, and that product is an estimate. It assumes the layers roll independently, which is true of a plain book and false of every book that uses incompatibility rules or optional layers — a rule makes a combination impossible and hands its share to the survivors, so the product understates every combination that survives. This is the figure without the assumption.

It is rejection sampling, so the normaliser is the whole point. The generator rolls a recipe, rolls each of its layers, and throws the entire attempt away if the recipe's rules reject it — recipe included, which is why the denominator sums over the book rather than over one recipe. So the chance of a selection is its own unconditional mass divided by the mass of everything legal:

         w_r/W  x  P(layers)
P  =  ------------------------------------------------
       SUM over recipes of  w/W  x  P(it rolls legal)

With no rules anywhere the denominator is exactly 1 and this reduces to the product the browser was already showing — which is the right relationship between the two: the estimate is not a different model, it is this one with its correction dropped.

The denominator is the only expensive part, and giving up on it costs a DIRECTION, not the answer. Each recipe's legal mass needs a walk of its combinations, budgeted exactly as UniqueSpace budgets its own. Every legal mass is at most 1, so the denominator is at most 1, so dropping it can only make the figure too SMALL. An un-walkable book therefore reports the numerator as SpaceCertainty.AtLeast — a real bound — and never the upside-down one SpaceCertainty exists to have caught.

What it is a chance OF: one roll of this CookBook. Two things deliberately do not enter it. Unique-DNA rejection is a property of a COLLECTION being filled, not of a roll, and distorts nothing until a run approaches the book's whole space. And generate --recipe pins a run to one recipe without recording that anywhere in the Set, so a pinned run's assets are scored against the book's own weights; a recipe the book has SHELVED is the one case that is detectable, and it reports nothing rather than a figure the book contradicts.

Methods

Describe(SelectionChance, bool)

public static string Describe(SelectionChance chance, bool asOdds);

Renders a chance the way every figure in this project is rendered: invariant, and saying which direction it is bounded in when it is bounded at all.

Parameters

  • chance (SelectionChance) - The figure.
  • asOdds (bool) - True for "1 in N", false for a percentage.

Returns. The figure, or an empty string when there is nothing to say.

Remarks

Worded HERE rather than in a front-end, for the reason SpaceText already records: the CLI and the GUI printing one number two ways is a defect nothing fails on. Note the direction INVERTS between the two units — a floor on the probability is a ceiling on the odds — which is exactly the kind of flip two hand-written copies get wrong in opposite ways.

Of(PeekedCookBook, SetItem, long)

public static SelectionChance Of(PeekedCookBook book, SetItem item, long enumerationBudget = 1000000);

Prepares and prices in one step. Prefer SelectionOdds.Prepare() plus BookOdds.Of() when pricing more than one asset.

Parameters

  • book (PeekedCookBook) - The book the Set was cooked from.
  • item (SetItem) - The asset, as its metadata records it.
  • enumerationBudget (long) - How many combinations may be walked per recipe.

Returns. Its chance.

Of(PeekedCookBook, string, IReadOnlyDictionary<string, string>, IReadOnlyCollection<string>, long)

public static SelectionChance Of(PeekedCookBook book, string recipeId, IReadOnlyDictionary<string, string> traits, IReadOnlyCollection<string> absentLayers, long enumerationBudget = 1000000);

Prepares and prices in one step. Prefer SelectionOdds.Prepare() plus BookOdds.Of() when pricing more than one asset.

Parameters

  • book (PeekedCookBook) - The book to score against.
  • recipeId (string) - The recipe's id.
  • traits (IReadOnlyDictionary<string, string>) - Variant NAME per layer NAME, for the layers that are present.
  • absentLayers (IReadOnlyCollection<string>) - The layer names this selection leaves out entirely.
  • enumerationBudget (long) - How many combinations may be walked per recipe.

Returns. Its chance.

Prepare(PeekedCookBook, long)

public static BookOdds Prepare(PeekedCookBook book, long enumerationBudget = 1000000);

Resolves everything about a book that does not depend on which asset is being priced — the per-layer outcome probabilities and, above all, the denominator.

Parameters

  • book (PeekedCookBook) - The book to price against. Never throws on a broken one; it simply cannot price anything.
  • enumerationBudget (long) - How many combinations may be walked per recipe; see UniqueSpace.DefaultEnumerationBudget.

Returns. The prepared book.

Exceptions

  • ArgumentOutOfRangeException - The budget is zero or negative.

Remarks

The same split WeightedRoller.Prepare() makes, for the same reason: the denominator is a fixed property of the book and the only part of this that can cost real time, so a caller pricing a grid of five hundred assets must not pay for it five hundred times.