probability-picker 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,6 @@
1
- # Probability Picker
2
- A lightweight library for selecting random values based on weighted probabilities.
1
+ # probability-picker
2
+
3
+ A small, type-safe utility for random selection based on weighted probabilities. It handles simple maps, nested objects, and selection without replacement.
3
4
 
4
5
  ## Installation
5
6
 
@@ -7,20 +8,65 @@ A lightweight library for selecting random values based on weighted probabilitie
7
8
  npm i probability-picker
8
9
  ```
9
10
 
10
- ## Usage
11
+ ## Basic Usage
12
+
13
+ The `picker` function takes an object where keys are your options and values are their weights.
11
14
 
12
- The `probabilityPicker` function will return `a`, `b`, or `c`. The probability of each one is defined as a value in the object.
13
-
14
- ```javascript
15
- import picker from 'probability-picker';
15
+ ```typescript
16
+ import { picker } from 'probability-picker';
16
17
 
17
- const value = picker({
18
- a: 10,
19
- b: 70,
20
- c: 20,
21
- });
18
+ const drop = picker({
19
+ common: 80,
20
+ rare: 15,
21
+ legendary: 5
22
+ }).one();
22
23
  ```
23
24
 
24
- ## License
25
+ ## Advanced Features
26
+
27
+ ### Picking multiple items
28
+ Use `.take(n)` to get an array of results.
29
+
30
+ ```typescript
31
+ const items = picker({ grass: 95, clover: 5 }).take(5);
32
+ // returns something like ['grass', 'grass', 'clover', 'grass', 'grass']
33
+ ```
34
+
35
+ ### Luck Modifier
36
+ The `luck()` method adjusts probabilities using an exponential curve. A luck value > 1 increases the weight of all items (favoring rarer ones relatively), while < 1 does the opposite.
37
+
38
+ ```typescript
39
+ const loot = picker({ sword: 1, gold: 99 })
40
+ .luck(5)
41
+ .one();
42
+ ```
43
+
44
+ ### Nested Maps
45
+ If a value is another object, the picker will recursively select until it hits a leaf node (a string key).
25
46
 
26
- [MIT](LICENSE)
47
+ ```typescript
48
+ const map = {
49
+ weapons: {
50
+ sword: 10,
51
+ axe: 5
52
+ },
53
+ consumables: 85
54
+ };
55
+
56
+ const result = picker(map).one(); // returns 'sword', 'axe', or 'consumables'
57
+ ```
58
+
59
+ ### The "Bag" System
60
+ If you need to pick items without repeating them until the set is exhausted, use `bag`.
61
+
62
+ ```typescript
63
+ import { bag } from 'probability-picker';
64
+
65
+ const deck = bag({ Ace: 1, King: 1, Queen: 1 });
66
+
67
+ console.log(deck.next()); // 'King'
68
+ console.log(deck.next()); // 'Ace' (won't repeat until the deck is empty)
69
+ ```
70
+
71
+ ## License
72
+ MIT
package/dist/index.cjs CHANGED
@@ -1,59 +1,83 @@
1
1
  'use strict';
2
2
 
3
- // src/index.ts
4
- function filterNumbers(entries) {
5
- return entries.filter((e) => typeof e[1] === "number" && !Number.isNaN(e[1]));
6
- }
7
- function sortEntries(entries) {
8
- return entries.sort((a, b) => a[1] - b[1]);
9
- }
10
- function sumProbabilities(entries) {
11
- return entries.reduce((acc, e) => acc + e[1], 0);
12
- }
13
- function normalizeProbabilities(entries, total) {
14
- return entries.map(([k, v]) => [k, Math.round(v / total * 100)]);
15
- }
16
- function prepareEntries(entries) {
17
- const filtered = filterNumbers(entries);
18
- if (filtered.length === 0) return;
19
- const sorted = sortEntries(filtered);
20
- const sum = sumProbabilities(sorted);
21
- if (sum === 0) return;
22
- if (sum === 100) return sorted;
23
- return normalizeProbabilities(sorted, sum);
24
- }
25
- function secureRandom() {
26
- if (typeof globalThis.crypto === "undefined" || typeof globalThis.crypto.getRandomValues !== "function") {
27
- return Math.random();
28
- }
3
+ // src/utils.ts
4
+ var secureRandom = () => {
29
5
  const array = new Uint32Array(1);
30
6
  globalThis.crypto.getRandomValues(array);
31
7
  return array[0] / 4294967296;
32
- }
33
- function random(min, max) {
34
- return secureRandom() * (max - min) + min;
35
- }
36
- function chooseOne(entries) {
37
- const num = random(1, 100);
38
- let count = 0;
39
- for (const entry of entries) {
40
- count += entry[1];
41
- if (num <= count) return entry[0];
8
+ };
9
+ var normalize = (map, luck) => {
10
+ const valid = [];
11
+ for (const [key, value] of Object.entries(map)) {
12
+ const weight = typeof value === "number" ? value : 1;
13
+ if (!Number.isNaN(weight) && weight > 0) {
14
+ valid.push([key, Math.pow(weight, 1 / luck)]);
15
+ }
42
16
  }
43
- return entries[entries.length - 1][0];
17
+ return valid;
18
+ };
19
+ var select = (items) => {
20
+ const total = items.reduce((acc, [, v]) => acc + v, 0);
21
+ let target = secureRandom() * total;
22
+ for (const [k, v] of items) {
23
+ target -= v;
24
+ if (target <= 0) return k;
25
+ }
26
+ return items[items.length - 1][0];
27
+ };
28
+
29
+ // src/picker.ts
30
+ var ProbabilityEngine = class _ProbabilityEngine {
31
+ _luck = 1;
32
+ _map;
33
+ constructor(map) {
34
+ this._map = map;
35
+ }
36
+ luck(value) {
37
+ this._luck = value;
38
+ return this;
39
+ }
40
+ one() {
41
+ const entries = normalize(this._map, this._luck);
42
+ if (entries.length === 0) return;
43
+ const key = select(entries);
44
+ const value = this._map[key];
45
+ if (value && typeof value === "object" && !Array.isArray(value)) {
46
+ return new _ProbabilityEngine(value).luck(this._luck).one();
47
+ }
48
+ return key;
49
+ }
50
+ take(n) {
51
+ const results = [];
52
+ for (let i = 0; i < n; i++) {
53
+ const picked = this.one();
54
+ if (picked) results.push(picked);
55
+ }
56
+ return results;
57
+ }
58
+ };
59
+ function picker(map) {
60
+ if (!map || typeof map !== "object") {
61
+ return new ProbabilityEngine({});
62
+ }
63
+ return new ProbabilityEngine(map);
44
64
  }
45
- function probabilityPicker(map) {
46
- if (!(map instanceof Object)) return null;
47
- const entries = Object.entries(map);
48
- if (entries.length === 0) return void 0;
49
- const first = entries[0];
50
- if (first && entries.length === 1) return first[0];
51
- const prepared = prepareEntries(entries);
52
- if (!prepared) return void 0;
53
- return chooseOne(prepared);
65
+
66
+ // src/bag.ts
67
+ function bag(map) {
68
+ let remaining = { ...map };
69
+ return {
70
+ next() {
71
+ const keys = Object.keys(remaining);
72
+ if (keys.length === 0) remaining = { ...map };
73
+ const picked = picker(remaining).one();
74
+ if (picked) delete remaining[picked];
75
+ return picked;
76
+ }
77
+ };
54
78
  }
55
- var index_default = probabilityPicker;
56
79
 
57
- module.exports = index_default;
80
+ exports.bag = bag;
81
+ exports.picker = picker;
58
82
  //# sourceMappingURL=index.cjs.map
59
83
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";;;AAGA,SAAS,cAAc,OAAA,EAAkB;AACrC,EAAA,OAAO,OAAA,CAAQ,MAAA,CAAO,CAAA,CAAA,KAAK,OAAO,EAAE,CAAC,CAAA,KAAM,QAAA,IAAY,CAAC,MAAA,CAAO,KAAA,CAAM,CAAA,CAAE,CAAC,CAAC,CAAC,CAAA;AAC9E;AAEA,SAAS,YAAY,OAAA,EAAkB;AACnC,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,EAAE,CAAC,CAAA,GAAI,CAAA,CAAE,CAAC,CAAC,CAAA;AAC7C;AAEA,SAAS,iBAAiB,OAAA,EAAkB;AACxC,EAAA,OAAO,OAAA,CAAQ,OAAO,CAAC,GAAA,EAAK,MAAM,GAAA,GAAM,CAAA,CAAE,CAAC,CAAA,EAAG,CAAC,CAAA;AACnD;AAEA,SAAS,sBAAA,CAAuB,SAAkB,KAAA,EAAe;AAC7D,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAC,GAAG,CAAC,CAAA,KAAM,CAAC,CAAA,EAAG,KAAK,KAAA,CAAO,CAAA,GAAI,KAAA,GAAS,GAAG,CAAC,CAAU,CAAA;AAC9E;AAEA,SAAS,eAAe,OAAA,EAAkB;AACtC,EAAA,MAAM,QAAA,GAAW,cAAc,OAAO,CAAA;AACtC,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AAC3B,EAAA,MAAM,MAAA,GAAS,YAAY,QAAQ,CAAA;AACnC,EAAA,MAAM,GAAA,GAAM,iBAAiB,MAAM,CAAA;AACnC,EAAA,IAAI,QAAQ,CAAA,EAAG;AACf,EAAA,IAAI,GAAA,KAAQ,KAAK,OAAO,MAAA;AACxB,EAAA,OAAO,sBAAA,CAAuB,QAAQ,GAAG,CAAA;AAC7C;AAEA,SAAS,YAAA,GAAuB;AAC5B,EAAA,IAAI,OAAO,WAAW,MAAA,KAAW,WAAA,IAAe,OAAO,UAAA,CAAW,MAAA,CAAO,oBAAoB,UAAA,EAAY;AACrG,IAAA,OAAO,KAAK,MAAA,EAAO;AAAA,EACvB;AACA,EAAA,MAAM,KAAA,GAAQ,IAAI,WAAA,CAAY,CAAC,CAAA;AAC/B,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA,CAAM,CAAC,CAAA,GAAK,UAAA;AACvB;AAEA,SAAS,MAAA,CAAO,KAAa,GAAA,EAAa;AACtC,EAAA,OAAO,YAAA,EAAa,IAAK,GAAA,GAAM,GAAA,CAAA,GAAO,GAAA;AAC1C;AAEA,SAAS,UAAU,OAAA,EAAkB;AACjC,EAAA,MAAM,GAAA,GAAM,MAAA,CAAO,CAAA,EAAG,GAAG,CAAA;AACzB,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,MAAW,SAAS,OAAA,EAAS;AACzB,IAAA,KAAA,IAAS,MAAM,CAAC,CAAA;AAChB,IAAA,IAAI,GAAA,IAAO,KAAA,EAAO,OAAO,KAAA,CAAM,CAAC,CAAA;AAAA,EACpC;AACA,EAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,GAAS,CAAC,EAAG,CAAC,CAAA;AACzC;AAEA,SAAS,kBAAkB,GAAA,EAAgD;AACvE,EAAA,IAAI,EAAE,GAAA,YAAe,MAAA,CAAA,EAAS,OAAO,IAAA;AACrC,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA;AAClC,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,MAAA;AACjC,EAAA,MAAM,KAAA,GAAQ,QAAQ,CAAC,CAAA;AACvB,EAAA,IAAI,SAAS,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,MAAM,CAAC,CAAA;AACjD,EAAA,MAAM,QAAA,GAAW,eAAe,OAAO,CAAA;AACvC,EAAA,IAAI,CAAC,UAAU,OAAO,MAAA;AACtB,EAAA,OAAO,UAAU,QAAQ,CAAA;AAC7B;AAEA,IAAO,aAAA,GAAQ","file":"index.cjs","sourcesContent":["type ProbabilityMap = Record<string, number>\r\ntype Entry = [string, number]\r\n\r\nfunction filterNumbers(entries: Entry[]) {\r\n return entries.filter(e => typeof e[1] === 'number' && !Number.isNaN(e[1]))\r\n}\r\n\r\nfunction sortEntries(entries: Entry[]) {\r\n return entries.sort((a, b) => a[1] - b[1])\r\n}\r\n\r\nfunction sumProbabilities(entries: Entry[]) {\r\n return entries.reduce((acc, e) => acc + e[1], 0)\r\n}\r\n\r\nfunction normalizeProbabilities(entries: Entry[], total: number) {\r\n return entries.map(([k, v]) => [k, Math.round((v / total) * 100)] as Entry)\r\n}\r\n\r\nfunction prepareEntries(entries: Entry[]) {\r\n const filtered = filterNumbers(entries)\r\n if (filtered.length === 0) return\r\n const sorted = sortEntries(filtered)\r\n const sum = sumProbabilities(sorted)\r\n if (sum === 0) return\r\n if (sum === 100) return sorted\r\n return normalizeProbabilities(sorted, sum)\r\n}\r\n\r\nfunction secureRandom(): number {\r\n if (typeof globalThis.crypto === 'undefined' || typeof globalThis.crypto.getRandomValues !== 'function') {\r\n return Math.random()\r\n }\r\n const array = new Uint32Array(1)\r\n globalThis.crypto.getRandomValues(array)\r\n return array[0]! / 0x100000000\r\n}\r\n\r\nfunction random(min: number, max: number) {\r\n return secureRandom() * (max - min) + min\r\n}\r\n\r\nfunction chooseOne(entries: Entry[]) {\r\n const num = random(1, 100)\r\n let count = 0\r\n for (const entry of entries) {\r\n count += entry[1]\r\n if (num <= count) return entry[0]\r\n }\r\n return entries[entries.length - 1]![0]\r\n}\r\n\r\nfunction probabilityPicker(map: ProbabilityMap): string | null | undefined {\r\n if (!(map instanceof Object)) return null\r\n const entries = Object.entries(map) as Entry[]\r\n if (entries.length === 0) return undefined\r\n const first = entries[0]\r\n if (first && entries.length === 1) return first[0]\r\n const prepared = prepareEntries(entries)\r\n if (!prepared) return undefined\r\n return chooseOne(prepared)\r\n}\r\n\r\nexport default probabilityPicker;"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/picker.ts","../src/bag.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { NestedMap } from './types'\r\n\r\nexport const secureRandom = (): number => {\r\n const array = new Uint32Array(1)\r\n globalThis.crypto.getRandomValues(array)\r\n return array[0]! / 0x100000000\r\n}\r\n\r\nexport const normalize = (map: NestedMap, luck: number): [string, number][] => {\r\n const valid: [string, number][] = []\r\n\r\n for (const [key, value] of Object.entries(map)) {\r\n const weight = typeof value === 'number' ? value : 1\r\n if (!Number.isNaN(weight) && weight > 0) {\r\n valid.push([key, Math.pow(weight, 1 / luck)])\r\n }\r\n }\r\n\r\n return valid\r\n}\r\n\r\nexport const select = (items: [string, number][]): string => {\r\n const total = items.reduce((acc, [, v]) => acc + v, 0)\r\n let target = secureRandom() * total\r\n\r\n for (const [k, v] of items) {\r\n target -= v\r\n if (target <= 0) return k\r\n }\r\n\r\n return items[items.length - 1]![0]\r\n}\r\n","import type { NestedMap, PickerInstance } from './types'\r\nimport { normalize, select } from './utils'\r\n\r\nclass ProbabilityEngine<T extends string> implements PickerInstance<T> {\r\n private _luck = 1\r\n private _map: NestedMap\r\n\r\n constructor(map: NestedMap) {\r\n this._map = map\r\n }\r\n\r\n luck(value: number) {\r\n this._luck = value\r\n return this\r\n }\r\n\r\n one(): T | undefined {\r\n const entries = normalize(this._map, this._luck)\r\n if (entries.length === 0) return\r\n\r\n const key = select(entries)\r\n const value = this._map[key]\r\n\r\n if (value && typeof value === 'object' && !Array.isArray(value)) {\r\n return new ProbabilityEngine(value).luck(this._luck).one() as T\r\n }\r\n\r\n return key as T\r\n }\r\n\r\n take(n: number): T[] {\r\n const results: T[] = []\r\n for (let i = 0; i < n; i++) {\r\n const picked = this.one()\r\n if (picked) results.push(picked)\r\n }\r\n return results\r\n }\r\n}\r\n\r\nexport function picker<T extends string>(map: Record<T, number | NestedMap>): PickerInstance<T> {\r\n if (!map || typeof map !== 'object') {\r\n return new ProbabilityEngine<T>({} as NestedMap)\r\n }\r\n return new ProbabilityEngine<T>(map as NestedMap)\r\n}\r\n","import { picker } from './picker'\r\n\r\nexport function bag<T extends string>(map: Record<T, number>) {\r\n let remaining = { ...map }\r\n\r\n return {\r\n next(): T | undefined {\r\n const keys = Object.keys(remaining)\r\n if (keys.length === 0) remaining = { ...map }\r\n const picked = picker(remaining).one()\r\n if (picked) delete remaining[picked as T]\r\n return picked as T\r\n }\r\n }\r\n}\r\n"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,16 @@
1
- type ProbabilityMap = Record<string, number>;
2
- declare function probabilityPicker(map: ProbabilityMap): string | null | undefined;
1
+ type NestedMap = {
2
+ [key: string]: number | NestedMap;
3
+ };
4
+ interface PickerInstance<T extends string> {
5
+ luck(value: number): this;
6
+ one(): T | undefined;
7
+ take(n: number): T[];
8
+ }
3
9
 
4
- export { probabilityPicker as default };
10
+ declare function picker<T extends string>(map: Record<T, number | NestedMap>): PickerInstance<T>;
11
+
12
+ declare function bag<T extends string>(map: Record<T, number>): {
13
+ next(): T | undefined;
14
+ };
15
+
16
+ export { type NestedMap, type PickerInstance, bag, picker };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,16 @@
1
- type ProbabilityMap = Record<string, number>;
2
- declare function probabilityPicker(map: ProbabilityMap): string | null | undefined;
1
+ type NestedMap = {
2
+ [key: string]: number | NestedMap;
3
+ };
4
+ interface PickerInstance<T extends string> {
5
+ luck(value: number): this;
6
+ one(): T | undefined;
7
+ take(n: number): T[];
8
+ }
3
9
 
4
- export { probabilityPicker as default };
10
+ declare function picker<T extends string>(map: Record<T, number | NestedMap>): PickerInstance<T>;
11
+
12
+ declare function bag<T extends string>(map: Record<T, number>): {
13
+ next(): T | undefined;
14
+ };
15
+
16
+ export { type NestedMap, type PickerInstance, bag, picker };
package/dist/index.js CHANGED
@@ -1,57 +1,80 @@
1
- // src/index.ts
2
- function filterNumbers(entries) {
3
- return entries.filter((e) => typeof e[1] === "number" && !Number.isNaN(e[1]));
4
- }
5
- function sortEntries(entries) {
6
- return entries.sort((a, b) => a[1] - b[1]);
7
- }
8
- function sumProbabilities(entries) {
9
- return entries.reduce((acc, e) => acc + e[1], 0);
10
- }
11
- function normalizeProbabilities(entries, total) {
12
- return entries.map(([k, v]) => [k, Math.round(v / total * 100)]);
13
- }
14
- function prepareEntries(entries) {
15
- const filtered = filterNumbers(entries);
16
- if (filtered.length === 0) return;
17
- const sorted = sortEntries(filtered);
18
- const sum = sumProbabilities(sorted);
19
- if (sum === 0) return;
20
- if (sum === 100) return sorted;
21
- return normalizeProbabilities(sorted, sum);
22
- }
23
- function secureRandom() {
24
- if (typeof globalThis.crypto === "undefined" || typeof globalThis.crypto.getRandomValues !== "function") {
25
- return Math.random();
26
- }
1
+ // src/utils.ts
2
+ var secureRandom = () => {
27
3
  const array = new Uint32Array(1);
28
4
  globalThis.crypto.getRandomValues(array);
29
5
  return array[0] / 4294967296;
30
- }
31
- function random(min, max) {
32
- return secureRandom() * (max - min) + min;
33
- }
34
- function chooseOne(entries) {
35
- const num = random(1, 100);
36
- let count = 0;
37
- for (const entry of entries) {
38
- count += entry[1];
39
- if (num <= count) return entry[0];
6
+ };
7
+ var normalize = (map, luck) => {
8
+ const valid = [];
9
+ for (const [key, value] of Object.entries(map)) {
10
+ const weight = typeof value === "number" ? value : 1;
11
+ if (!Number.isNaN(weight) && weight > 0) {
12
+ valid.push([key, Math.pow(weight, 1 / luck)]);
13
+ }
40
14
  }
41
- return entries[entries.length - 1][0];
15
+ return valid;
16
+ };
17
+ var select = (items) => {
18
+ const total = items.reduce((acc, [, v]) => acc + v, 0);
19
+ let target = secureRandom() * total;
20
+ for (const [k, v] of items) {
21
+ target -= v;
22
+ if (target <= 0) return k;
23
+ }
24
+ return items[items.length - 1][0];
25
+ };
26
+
27
+ // src/picker.ts
28
+ var ProbabilityEngine = class _ProbabilityEngine {
29
+ _luck = 1;
30
+ _map;
31
+ constructor(map) {
32
+ this._map = map;
33
+ }
34
+ luck(value) {
35
+ this._luck = value;
36
+ return this;
37
+ }
38
+ one() {
39
+ const entries = normalize(this._map, this._luck);
40
+ if (entries.length === 0) return;
41
+ const key = select(entries);
42
+ const value = this._map[key];
43
+ if (value && typeof value === "object" && !Array.isArray(value)) {
44
+ return new _ProbabilityEngine(value).luck(this._luck).one();
45
+ }
46
+ return key;
47
+ }
48
+ take(n) {
49
+ const results = [];
50
+ for (let i = 0; i < n; i++) {
51
+ const picked = this.one();
52
+ if (picked) results.push(picked);
53
+ }
54
+ return results;
55
+ }
56
+ };
57
+ function picker(map) {
58
+ if (!map || typeof map !== "object") {
59
+ return new ProbabilityEngine({});
60
+ }
61
+ return new ProbabilityEngine(map);
42
62
  }
43
- function probabilityPicker(map) {
44
- if (!(map instanceof Object)) return null;
45
- const entries = Object.entries(map);
46
- if (entries.length === 0) return void 0;
47
- const first = entries[0];
48
- if (first && entries.length === 1) return first[0];
49
- const prepared = prepareEntries(entries);
50
- if (!prepared) return void 0;
51
- return chooseOne(prepared);
63
+
64
+ // src/bag.ts
65
+ function bag(map) {
66
+ let remaining = { ...map };
67
+ return {
68
+ next() {
69
+ const keys = Object.keys(remaining);
70
+ if (keys.length === 0) remaining = { ...map };
71
+ const picked = picker(remaining).one();
72
+ if (picked) delete remaining[picked];
73
+ return picked;
74
+ }
75
+ };
52
76
  }
53
- var index_default = probabilityPicker;
54
77
 
55
- export { index_default as default };
78
+ export { bag, picker };
56
79
  //# sourceMappingURL=index.js.map
57
80
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["type ProbabilityMap = Record<string, number>\r\ntype Entry = [string, number]\r\n\r\nfunction filterNumbers(entries: Entry[]) {\r\n return entries.filter(e => typeof e[1] === 'number' && !Number.isNaN(e[1]))\r\n}\r\n\r\nfunction sortEntries(entries: Entry[]) {\r\n return entries.sort((a, b) => a[1] - b[1])\r\n}\r\n\r\nfunction sumProbabilities(entries: Entry[]) {\r\n return entries.reduce((acc, e) => acc + e[1], 0)\r\n}\r\n\r\nfunction normalizeProbabilities(entries: Entry[], total: number) {\r\n return entries.map(([k, v]) => [k, Math.round((v / total) * 100)] as Entry)\r\n}\r\n\r\nfunction prepareEntries(entries: Entry[]) {\r\n const filtered = filterNumbers(entries)\r\n if (filtered.length === 0) return\r\n const sorted = sortEntries(filtered)\r\n const sum = sumProbabilities(sorted)\r\n if (sum === 0) return\r\n if (sum === 100) return sorted\r\n return normalizeProbabilities(sorted, sum)\r\n}\r\n\r\nfunction secureRandom(): number {\r\n if (typeof globalThis.crypto === 'undefined' || typeof globalThis.crypto.getRandomValues !== 'function') {\r\n return Math.random()\r\n }\r\n const array = new Uint32Array(1)\r\n globalThis.crypto.getRandomValues(array)\r\n return array[0]! / 0x100000000\r\n}\r\n\r\nfunction random(min: number, max: number) {\r\n return secureRandom() * (max - min) + min\r\n}\r\n\r\nfunction chooseOne(entries: Entry[]) {\r\n const num = random(1, 100)\r\n let count = 0\r\n for (const entry of entries) {\r\n count += entry[1]\r\n if (num <= count) return entry[0]\r\n }\r\n return entries[entries.length - 1]![0]\r\n}\r\n\r\nfunction probabilityPicker(map: ProbabilityMap): string | null | undefined {\r\n if (!(map instanceof Object)) return null\r\n const entries = Object.entries(map) as Entry[]\r\n if (entries.length === 0) return undefined\r\n const first = entries[0]\r\n if (first && entries.length === 1) return first[0]\r\n const prepared = prepareEntries(entries)\r\n if (!prepared) return undefined\r\n return chooseOne(prepared)\r\n}\r\n\r\nexport default probabilityPicker;"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/picker.ts","../src/bag.ts"],"names":[],"mappings":";AAEO,IAAM,eAAe,MAAc;AACtC,EAAA,MAAM,KAAA,GAAQ,IAAI,WAAA,CAAY,CAAC,CAAA;AAC/B,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA,CAAM,CAAC,CAAA,GAAK,UAAA;AACvB,CAAA;AAEO,IAAM,SAAA,GAAY,CAAC,GAAA,EAAgB,IAAA,KAAqC;AAC3E,EAAA,MAAM,QAA4B,EAAC;AAEnC,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC5C,IAAA,MAAM,MAAA,GAAS,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,CAAA;AACnD,IAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,MAAM,CAAA,IAAK,SAAS,CAAA,EAAG;AACrC,MAAA,KAAA,CAAM,IAAA,CAAK,CAAC,GAAA,EAAK,IAAA,CAAK,IAAI,MAAA,EAAQ,CAAA,GAAI,IAAI,CAAC,CAAC,CAAA;AAAA,IAChD;AAAA,EACJ;AAEA,EAAA,OAAO,KAAA;AACX,CAAA;AAEO,IAAM,MAAA,GAAS,CAAC,KAAA,KAAsC;AACzD,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,MAAA,CAAO,CAAC,GAAA,EAAK,GAAG,CAAC,CAAA,KAAM,GAAA,GAAM,CAAA,EAAG,CAAC,CAAA;AACrD,EAAA,IAAI,MAAA,GAAS,cAAa,GAAI,KAAA;AAE9B,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,CAAA,IAAK,KAAA,EAAO;AACxB,IAAA,MAAA,IAAU,CAAA;AACV,IAAA,IAAI,MAAA,IAAU,GAAG,OAAO,CAAA;AAAA,EAC5B;AAEA,EAAA,OAAO,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,EAAG,CAAC,CAAA;AACrC,CAAA;;;AC5BA,IAAM,iBAAA,GAAN,MAAM,kBAAA,CAAiE;AAAA,EAC3D,KAAA,GAAQ,CAAA;AAAA,EACR,IAAA;AAAA,EAER,YAAY,GAAA,EAAgB;AACxB,IAAA,IAAA,CAAK,IAAA,GAAO,GAAA;AAAA,EAChB;AAAA,EAEA,KAAK,KAAA,EAAe;AAChB,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AACb,IAAA,OAAO,IAAA;AAAA,EACX;AAAA,EAEA,GAAA,GAAqB;AACjB,IAAA,MAAM,OAAA,GAAU,SAAA,CAAU,IAAA,CAAK,IAAA,EAAM,KAAK,KAAK,CAAA;AAC/C,IAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AAE1B,IAAA,MAAM,GAAA,GAAM,OAAO,OAAO,CAAA;AAC1B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA;AAE3B,IAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AAC7D,MAAA,OAAO,IAAI,mBAAkB,KAAK,CAAA,CAAE,KAAK,IAAA,CAAK,KAAK,EAAE,GAAA,EAAI;AAAA,IAC7D;AAEA,IAAA,OAAO,GAAA;AAAA,EACX;AAAA,EAEA,KAAK,CAAA,EAAgB;AACjB,IAAA,MAAM,UAAe,EAAC;AACtB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,CAAA,EAAG,CAAA,EAAA,EAAK;AACxB,MAAA,MAAM,MAAA,GAAS,KAAK,GAAA,EAAI;AACxB,MAAA,IAAI,MAAA,EAAQ,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA;AAAA,IACnC;AACA,IAAA,OAAO,OAAA;AAAA,EACX;AACJ,CAAA;AAEO,SAAS,OAAyB,GAAA,EAAuD;AAC5F,EAAA,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,EAAU;AACjC,IAAA,OAAO,IAAI,iBAAA,CAAqB,EAAe,CAAA;AAAA,EACnD;AACA,EAAA,OAAO,IAAI,kBAAqB,GAAgB,CAAA;AACpD;;;AC3CO,SAAS,IAAsB,GAAA,EAAwB;AAC1D,EAAA,IAAI,SAAA,GAAY,EAAE,GAAG,GAAA,EAAI;AAEzB,EAAA,OAAO;AAAA,IACH,IAAA,GAAsB;AAClB,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA;AAClC,MAAA,IAAI,KAAK,MAAA,KAAW,CAAA,EAAG,SAAA,GAAY,EAAE,GAAG,GAAA,EAAI;AAC5C,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAS,CAAA,CAAE,GAAA,EAAI;AACrC,MAAA,IAAI,MAAA,EAAQ,OAAO,SAAA,CAAU,MAAW,CAAA;AACxC,MAAA,OAAO,MAAA;AAAA,IACX;AAAA,GACJ;AACJ","file":"index.js","sourcesContent":["import type { NestedMap } from './types'\r\n\r\nexport const secureRandom = (): number => {\r\n const array = new Uint32Array(1)\r\n globalThis.crypto.getRandomValues(array)\r\n return array[0]! / 0x100000000\r\n}\r\n\r\nexport const normalize = (map: NestedMap, luck: number): [string, number][] => {\r\n const valid: [string, number][] = []\r\n\r\n for (const [key, value] of Object.entries(map)) {\r\n const weight = typeof value === 'number' ? value : 1\r\n if (!Number.isNaN(weight) && weight > 0) {\r\n valid.push([key, Math.pow(weight, 1 / luck)])\r\n }\r\n }\r\n\r\n return valid\r\n}\r\n\r\nexport const select = (items: [string, number][]): string => {\r\n const total = items.reduce((acc, [, v]) => acc + v, 0)\r\n let target = secureRandom() * total\r\n\r\n for (const [k, v] of items) {\r\n target -= v\r\n if (target <= 0) return k\r\n }\r\n\r\n return items[items.length - 1]![0]\r\n}\r\n","import type { NestedMap, PickerInstance } from './types'\r\nimport { normalize, select } from './utils'\r\n\r\nclass ProbabilityEngine<T extends string> implements PickerInstance<T> {\r\n private _luck = 1\r\n private _map: NestedMap\r\n\r\n constructor(map: NestedMap) {\r\n this._map = map\r\n }\r\n\r\n luck(value: number) {\r\n this._luck = value\r\n return this\r\n }\r\n\r\n one(): T | undefined {\r\n const entries = normalize(this._map, this._luck)\r\n if (entries.length === 0) return\r\n\r\n const key = select(entries)\r\n const value = this._map[key]\r\n\r\n if (value && typeof value === 'object' && !Array.isArray(value)) {\r\n return new ProbabilityEngine(value).luck(this._luck).one() as T\r\n }\r\n\r\n return key as T\r\n }\r\n\r\n take(n: number): T[] {\r\n const results: T[] = []\r\n for (let i = 0; i < n; i++) {\r\n const picked = this.one()\r\n if (picked) results.push(picked)\r\n }\r\n return results\r\n }\r\n}\r\n\r\nexport function picker<T extends string>(map: Record<T, number | NestedMap>): PickerInstance<T> {\r\n if (!map || typeof map !== 'object') {\r\n return new ProbabilityEngine<T>({} as NestedMap)\r\n }\r\n return new ProbabilityEngine<T>(map as NestedMap)\r\n}\r\n","import { picker } from './picker'\r\n\r\nexport function bag<T extends string>(map: Record<T, number>) {\r\n let remaining = { ...map }\r\n\r\n return {\r\n next(): T | undefined {\r\n const keys = Object.keys(remaining)\r\n if (keys.length === 0) remaining = { ...map }\r\n const picked = picker(remaining).one()\r\n if (picked) delete remaining[picked as T]\r\n return picked as T\r\n }\r\n }\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,42 +1,53 @@
1
- {
2
- "name": "probability-picker",
3
- "version": "1.2.0",
4
- "description": "A lightweight library for selecting random values based on weighted probabilities.",
5
- "main": "./dist/index.cjs",
6
- "module": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js",
12
- "require": "./dist/index.cjs"
13
- }
14
- },
15
- "files": [
16
- "dist",
17
- "README.md",
18
- "LICENSE"
19
- ],
20
- "scripts": {
21
- "build": "tsup",
22
- "dev": "tsup --watch",
23
- "lint": "tsc --noEmit"
24
- },
25
- "keywords": [
26
- "probability",
27
- "weighted",
28
- "random"
29
- ],
30
- "author": "qgave (https://github.com/qgave)",
31
- "license": "MIT",
32
- "repository": {
33
- "type": "git",
34
- "url": "https://github.com/qgave/probability-picker"
35
- },
36
- "type": "module",
37
- "sideEffects": false,
38
- "devDependencies": {
39
- "tsup": "^8.5.1",
40
- "typescript": "^5.9.3"
41
- }
1
+ {
2
+ "name": "probability-picker",
3
+ "version": "2.0.0",
4
+ "description": "A lightweight library for selecting random values based on weighted probabilities.",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "lint": "tsc --noEmit",
25
+ "test": "vitest run"
26
+ },
27
+ "keywords": [
28
+ "probability",
29
+ "weighted",
30
+ "random"
31
+ ],
32
+ "author": {
33
+ "name": "qgave",
34
+ "url": "https://github.com/qgave"
35
+ },
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/qgave/probability-picker"
40
+ },
41
+ "sideEffects": false,
42
+ "engines": {
43
+ "node": ">=18"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "devDependencies": {
49
+ "typescript": "^5.9.3",
50
+ "tsup": "^8.5.1",
51
+ "vitest": "^4.0.17"
52
+ }
42
53
  }