UNPKG

tiny-essentials

Version:

Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.

675 lines (434 loc) โ€ข 19.1 kB
# ๐ŸŽฏ TinyAdvancedRaffle **TinyAdvancedRaffle** is an advanced, event-driven raffle system designed for complex probability-based selections. It goes beyond simple random draws by offering **weighted probabilities**, **normalization modes**, **pity mechanics**, **temporary modifiers**, and **seedable randomness** for reproducible outcomes. With **import/export in JSON format**, **group-based filtering**, and **exclusion lists**, itโ€™s ideal for games, loot systems, prize distributions, and any scenario requiring **balanced yet configurable randomness**. โœจ **Key Features** * ๐Ÿ“Š **Weighted items** โ€” Assign different probabilities to each item. * ๐Ÿงฎ **Normalization modes** โ€” Choose between *relative* and *softmax*. * ๐ŸŽฏ **Pity system** โ€” Guarantee fairness over multiple draws. * ๐Ÿ”„ **Temporary modifiers** โ€” Adjust weights dynamically for limited draws. * ๐Ÿ—‚๏ธ **Group filtering** โ€” Include or exclude items by groups. * ๐Ÿ” **Item exclusions** โ€” Prevent specific items from being drawn. * ๐Ÿ“ฆ **Import/export in JSON** โ€” Save and restore raffle states easily. * โณ **Seedable RNG** โ€” Ensure deterministic and repeatable results. * ๐Ÿ“ก **Event-driven** โ€” Hook into lifecycle events for customization. --- ## ๐ŸŽฏ Type Definitions & Core Concepts This section defines the **core data structures**, **callbacks**, and **types** used by the `TinyAdvancedRaffle` system. --- ### ๐Ÿ“Š Normalization Modes ```ts type Normalization = 'relative' | 'softmax'; ``` Defines the available normalization strategies for **probability weight calculations**: * **`relative`** โ€“ Direct proportion based on item weights. * **`softmax`** โ€“ Applies a softmax transformation for a probability distribution with exponential scaling. --- ### ๐ŸŽฒ RNG Generator ```ts type RngGenerator = () => number; ``` A callback that generates a pseudo-random number between `0` (inclusive) and `1` (exclusive). --- ### โณ Temporary Weight Modifier ```ts type TempModifier = { fn: WeightsCallback; uses: number; }; ``` Represents a **temporary weight modifier** that is applied for a **limited number of draws**. * **`fn`** โ€“ Function that modifies the item weights. * **`uses`** โ€“ Number of draws before the modifier is removed. --- ### ๐Ÿ“ฆ Item Data Template ```ts type ItemDataTemplate<TGroups extends Set<string> | string[]> = { id: string; label: string; baseWeight: number; groups: TGroups; locked: boolean; meta: ItemMetadata; }; ``` Represents the **core data structure** for an item in the raffle system. | Property | Type | Description | | ------------ | --------------------------- | ----------------------------------------- | | `id` | `string` | Unique identifier for the item. | | `label` | `string` | Human-readable name for the item. | | `baseWeight` | `number` | Base probability weight before modifiers. | | `groups` | `Set<string>` or `string[]` | Groups the item belongs to. | | `locked` | `boolean` | Whether the item is excluded from draws. | | `meta` | `ItemMetadata` | Arbitrary metadata. | --- ### ๐Ÿ’พ Exported Raffle State ```ts type ExportedJson = { items: ItemDataGetter[]; pity: [string, Pity][]; exclusions: string[]; normalization: Normalization; seed: number | null; }; ``` Represents the **serialized state** of the raffle system for export or persistence. --- ### ๐Ÿท Specialized Item Types * **`ItemData`** โ€“ Item where `groups` is a `Set<string>`. * **`ItemDataGetter`** โ€“ Item where `groups` is a `string[]`. --- ### ๐Ÿ—ƒ Metadata Container ```ts type ItemMetadata = Record<string | number | symbol, any>; ``` Arbitrary key-value container for **extra item information**. --- ### ๐Ÿ›  Weight Calculation Context ```ts type ComputeEffectiveWeightsContext = { metadata?: ItemMetadata; previousDraws?: DrawOne[]; }; ``` Provides **context** to weight modification functions during a draw. --- ### โš– Weight Map ```ts type Weights = Map<string, number>; ``` Maps **item IDs** to their computed **effective weight**. --- ### ๐ŸŽฏ Pity System ```ts type Pity = { threshold: number; increment: number; cap: number; counter: number; currentAdd: number; }; ``` Defines **pity mechanics** for guaranteeing item selection after repeated failures. --- ### ๐Ÿงฎ Weight Modifier Callback ```ts type WeightsCallback = ( weights: Weights, context: ComputeEffectiveWeightsContext ) => Weights | null; ``` A function that modifies or overrides computed weights **before** a draw. --- ### ๐ŸŽŸ Draw Result ```ts type DrawOne = { id: string; label: string; meta: ItemMetadata; prob: number; }; ``` Represents the **result** of a single draw. --- ### ๐Ÿ“ก Event Handler ```ts type handler = (payload: any, event: any) => void; ``` Generic event handler for message or signal reception. --- ## ๐ŸŽฐ Core Properties, Getters & Setters This section covers the **private fields**, **public getters/setters**, and **constructor** of the `TinyAdvancedRaffle` class. --- ### ๐Ÿ”’ Private Fields * **`#normalization`**: *(Normalization)* โ€” Defines the strategy for scaling probabilities before a draw. * Options: `'relative'` or `'softmax'`. * **`#seed`**: *(number | null)* โ€” RNG seed for deterministic results. If `null`, draws are fully random. * **`#globalModifiers`**: *(WeightsCallback\[])* โ€” Persistent weight-modifying functions applied to **every** draw. * **`#temporaryModifiers`**: *(TempModifier\[])* โ€” Temporary weight modifiers that expire after a set number of draws. * **`#conditionalRules`**: *(WeightsCallback\[])* โ€” Dynamic rules adjusting weights based on the raffle state. * **`#pitySystems`**: *(Map\<string, Pity>)* โ€” "Pity" mechanisms to increase chances after repeated failures. * **`#exclusions`**: *(Set\<string>)* โ€” Item IDs that cannot be drawn. * **`#groups`**: *(Map\<string, Set\<string>>)* โ€” Named groups containing sets of item IDs. * **`#rng`**: *(RngGenerator)* โ€” The random number generator used in draws. * **`#items`**: *(Map\<string, ItemData>)* โ€” All raffle items and their associated data. * **`#freq`**: *(Map\<string, number>)* โ€” Tracks how many times each item has been drawn. --- ### ๐Ÿ“ค Public Getters | Getter | Returns | Description | | -------------------- | -------------------------------- | ------------------------------------------------ | | `freq` | `Record<string, number>` | Draw frequency as a plain object. | | `size` | `number` | Total number of registered items. | | `normalization` | `Normalization` | Current probability scaling method. | | `seed` | `number \| null` | RNG seed (if any). | | `globalModifiers` | `WeightsCallback[]` | Copy of persistent modifiers. | | `temporaryModifiers` | `TempModifier[]` | Copy of temporary modifiers with usage counts. | | `conditionalRules` | `WeightsCallback[]` | Copy of conditional rules. | | `pitySystems` | `Record<string, Pity>` | Copy of all pity configurations. | | `exclusions` | `string[]` | List of excluded item IDs. | | `groups` | `Record<string, string[]>` | Copy of group definitions. | | `rng` | `RngGenerator` | RNG function used for draws. | | `items` | `Record<string, ItemDataGetter>` | Copy of all item definitions (groups as arrays). | --- ### ๐Ÿ“ Public Setters Each setter **validates input types** and throws `TypeError` for invalid data. * **`normalization`** โ€” Must be a non-empty string: `'relative'` or `'softmax'`. * **`seed`** โ€” Must be a finite number or `null`; reseeds RNG when changed. * **`globalModifiers`** โ€” Must be an array of functions. * **`temporaryModifiers`** โ€” Must be an array of `{ fn: function, uses: positive integer }`. * **`conditionalRules`** โ€” Must be an array of functions. * **`pitySystems`** โ€” Must be a `Map<string, Pity>` with all numeric fields. * **`exclusions`** โ€” Must be a `Set<string>`. * **`groups`** โ€” Must be a `Map<string, Set<string>>` where all values are strings. * **`rng`** โ€” Must be a function returning a number in `[0, 1)`. * **`items`** โ€” Must be a `Map<string, ItemData>`; **clears old list before setting**. --- ### ๐Ÿ— Constructor This section sets up all **core data structures** and provides strict **input validation** to keep the raffle systemโ€™s state consistent. ๐ŸŽฏ **Parameters**: * `opts.rng` *(RngGenerator | null)* โ€” Custom RNG function. Defaults to `Math.random` if no seed is provided. * `opts.seed` *(number | null)* โ€” RNG seed for reproducible results. * `opts.normalization` *(Normalization)* โ€” Probability scaling mode (`'relative'` by default). --- ## ๐ŸŽฏ Item Management & Rules ### ๐Ÿ“ฆ Item Management #### `hasItem(itemId)` Check if an item exists in the system. * **Parameters:** * `itemId` *(string)* โ€” The ID of the item to check. * **Returns:** `boolean` โ€” `true` if the item exists, otherwise `false`. * **Throws:** `TypeError` if `itemId` is not a string. --- #### `addItem(id, opts = {})` Add or update an item in the raffle. * **Parameters:** * `id` *(string)* โ€” Unique item identifier. * `opts` *(object, optional)* โ€” Item configuration: * `weight` *(number, default=1)* โ€” Base relative weight (โ‰ฅ 0). * `label` *(string)* โ€” Human-readable label. * `meta` *(object)* โ€” Arbitrary metadata. * `groups` *(string\[] | Set<string>)* โ€” Groups this item belongs to. * **Returns:** `ItemData` โ€” The created or updated item. * **Throws:** `TypeError` for invalid types. --- #### `removeItem(id)` Remove an item from the system. * **Parameters:** * `id` *(string)* โ€” Item ID to remove. * **Returns:** `boolean` โ€” `true` if removed, `false` if it didnโ€™t exist. * **Throws:** `TypeError` if `id` is not a string. --- #### `setBaseWeight(id, weight)` Update the base weight of an existing item. * **Parameters:** * `id` *(string)* โ€” Item ID. * `weight` *(number)* โ€” New base weight (โ‰ฅ 0). * **Throws:** * `Error` if the item is not found. * `TypeError` for invalid parameters. --- #### `getItem(id)` Retrieve an item by ID. * **Parameters:** * `id` *(string)* โ€” Item ID. * **Returns:** `ItemData | null` โ€” Item data or `null` if not found. * **Throws:** `TypeError` if `id` is not a string. --- #### `listItems()` List all items in the system. * **Returns:** `ItemData[]` โ€” Array of cloned item objects. --- #### `clearList()` Remove all items, frequencies, and pity systems. --- ### ๐Ÿ›  Modifiers & Rules #### Global Modifiers Modifiers applied **persistently** to all draws. * **`hasGlobalModifier(fn)`** โ€” Check if a global modifier exists. * **`addGlobalModifier(fn)`** โ€” Add a persistent modifier. * **`removeGlobalModifier(fn)`** โ€” Remove a specific global modifier. --- #### Temporary Modifiers Modifiers applied only for a **limited number of draws**. * **`hasTemporaryModifier(fn)`** โ€” Check if it exists. * **`addTemporaryModifier(fn, uses = 1)`** โ€” Add a modifier for `uses` draws. * **`removeTemporaryModifier(fn)`** โ€” Remove it. --- #### Conditional Rules Rules applied dynamically during each draw. * **`hasConditionalRule(ruleFn)`** โ€” Check if it exists. * **`addConditionalRule(ruleFn)`** โ€” Add a conditional rule. * **`removeConditionalRule(ruleFn)`** โ€” Remove a conditional rule. --- ### ๐Ÿ€ Pity System The **pity system** increases the chance of an item appearing if it hasnโ€™t been drawn after a certain number of tries. * **`hasPity(itemId)`** โ€” Check if pity exists for an item. * **`configurePity(itemId, cfg)`** โ€” Configure pity with: * `threshold` *(number)* โ€” Number of failed draws before increasing weight. * `increment` *(number)* โ€” Weight added each draw after threshold. * `cap` *(number, default=Infinity)* โ€” Max extra weight. * **`resetPity(itemId)`** โ€” Reset pity counters. * **`clearPities()`** โ€” Remove all pity configurations. --- ### ๐Ÿšซ Exclusions & Groups #### Exclusions * **`hasExclusion(itemId)`** โ€” Check if excluded. * **`excludeItem(itemId)`** โ€” Exclude from raffle. * **`includeItem(itemId)`** โ€” Re-include in raffle. #### Groups * **`_ensureGroup(name)`** *(private)* โ€” Create or get a group. * **`hasInGroup(itemId, groupName)`** โ€” Check group membership. * **`addToGroup(itemId, groupName)`** โ€” Add item to a group. * **`removeFromGroup(itemId, groupName)`** โ€” Remove item from a group. --- ## ๐ŸŽฒ Draw Core ### ๐Ÿ”„ Frequency Management #### `clearFreqs()` Clear the draw frequency count for **all** items. Effectively resets the internal frequency tracking map to an empty state. --- #### `resetFreq(itemId)` Reset the draw frequency for a **specific item**. * **Parameters:** * `itemId` *(string)* โ€” Unique identifier of the item. * **Throws:** * `TypeError` if `itemId` is not a string. --- ### โš–๏ธ Weight Computation #### `computeEffectiveWeights(context = {})` Compute the **effective weights** of all items after applying: 1. Base weights 2. Global modifiers 3. Temporary modifiers 4. Conditional rules 5. Pity system adjustments 6. Exclusions 7. Removal of zero or negative weights * **Parameters:** * `context` *(object, optional)* โ€” Includes: * `previousDraws` *(array)* โ€” List of past draws. * `metadata` *(object)* โ€” Arbitrary metadata for rules. * **Returns:** `Map<string, number>` โ€” Map of item ID โ†’ effective weight. * **Throws:** * `TypeError` for invalid `context` format or property types. --- #### `_weightsToDistribution(weights)` Convert a **weights map** into a normalized **probability distribution array**. * **Parameters:** * `weights` *(Map\<string, number>)* โ€” Item IDs and their weights. * **Returns:** An array of objects: ```ts { id: string, // item ID weight: number, // weight value p: number, // normalized probability cumulative: number // cumulative probability for sampling } ``` * **Throws:** * `TypeError` if `weights` is not a Map. * `TypeError` for invalid key or value types. * **Notes:** * Supports **`softmax`** or **relative normalization**. --- ### ๐ŸŽฏ Drawing Items #### `drawOne(opts = {})` Draw **one item** from the raffle considering all configurations, rules, and pity effects. * **Parameters:** * `opts` *(object, optional)*: * `previousDraws` *(array)* โ€” Draw history for context. * `metadata` *(object)* โ€” Metadata for conditional rules. * **Returns:** An object: ```ts { id: string, // item ID label: string, // display label meta: object, // cloned metadata prob: number // probability at time of draw } ``` Or `null` if no items are available. * **Throws:** `TypeError` for invalid option types. * **Effects:** * Updates **pity counters**. * Consumes **temporary modifiers**. * Increments **frequency counts**. * Emits a **`draw`** event. --- #### `_consumeTemporaryModifiers()` *(private)* Decrements usage counts of **temporary modifiers** and removes them when their uses reach zero. --- #### `drawMany(count = 1, opts = {})` Draw multiple items in one operation. * **Parameters:** * `count` *(integer > 0)* โ€” Number of items to draw. * `opts` *(object, optional)*: * `metadata` *(object)* โ€” Metadata for rules. * `withReplacement` *(boolean, default = true)* โ€” Allow duplicates. * `ensureUnique` *(boolean, default = false)* โ€” Attempt to ensure unique results. * `previousDraws` *(array)* โ€” History for context. * **Returns:** `DrawOne[]` โ€” List of drawn items. * **Throws:** * `TypeError` for invalid `count` or option types. * **Behavior Notes:** * **`withReplacement = false` & `ensureUnique = true`** โ†’ Items are temporarily excluded during the draw session. * **`withReplacement = false`** (without `ensureUnique`) โ†’ Items are excluded only for the session, then re-included. --- ### ๐Ÿ“ฆ Save & Load (JSON) #### ๐Ÿ“ค `exportToJson()` Exports the current configuration into a **JSON-serializable object**, capturing: * **Items** (ID, label, base weight, metadata, groups) * **Pity systems** * **Exclusions** * **Normalization mode** * **Seed value** This method is ideal for saving and restoring the raffle configuration between sessions. **Returns** * `ExportedJson` โ€” An object containing the complete configuration. --- #### ๐Ÿ“ฅ `loadFromJson(data)` Loads configuration data previously generated by [`exportToJson()`](#-exporttojson). **Behavior** * Clears all existing items and state. * Validates all input fields for type safety and constraints. * Reconstructs items, pity systems, exclusions, and normalization settings. * Restores the seed value if provided. **Parameters** * `data` (`ExportedJson`) โ€” The configuration to load. **Throws** * `TypeError` if `data` is missing required properties or contains invalid values. --- ### ๐Ÿชž `clone()` Creates a **deep clone** of the current raffle instance. **Features** * Fully duplicates all internal **Maps**, **Sets**, and **Arrays** to prevent shared references. * Functions (e.g., callbacks) are **copied by reference** since they are immutable. * The clone is entirely **independent** from the original instance. **Returns** * `TinyAdvancedRaffle` โ€” A new cloned instance with identical state. --- ### ๐ŸŽฒ RNG: Seedable (mulberry32) #### ๐Ÿ”ข `_makeSeededRng(seed)` Creates a **deterministic pseudo-random number generator (PRNG)** using the **mulberry32** algorithm. **Parameters** * `seed` (`number`) โ€” An integer used to seed the PRNG. **Returns** * `RngGenerator` โ€” A function that returns a pseudo-random number in the range `[0, 1)`. **Throws** * `TypeError` if `seed` is not a finite number. --- ## ๐Ÿ—‘๏ธ `destroy()` Completely **wipes** all internal data, breaks all references, and renders the raffle instance permanently unusable. After calling this method, **any further calls** to its functions will throw an error. **Use Cases** * Freeing memory in long-running applications. * Preventing accidental reuse after the raffle is no longer needed. * Security-sensitive scenarios where sensitive item data must be fully erased. **Example** ```js const raffle = new TinyAdvancedRaffle(); raffle.destroy(); raffle.clone(); // โŒ Throws an error: instance has been destroyed ```