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
Markdown
# ๐ฏ 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
```