@simcups/chance-utils 1.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.
@@ -0,0 +1,6 @@
1
+ export declare function randomInteger(min: number, max: number): number;
2
+ export declare function randomFloat(min: number, max: number): number;
3
+ export declare function chance(likelihood?: number, max?: number): boolean;
4
+ export declare function weightedBool(trueLikelihood: number, falseLikelihood: number): boolean;
5
+ export declare function sample<T>(array: T[]): T | undefined;
6
+ //# sourceMappingURL=chance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chance.d.ts","sourceRoot":"","sources":["../src/chance.ts"],"names":[],"mappings":"AAGA,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAErD;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5D;AAED,wBAAgB,MAAM,CAAC,UAAU,GAAE,MAAW,EAAE,GAAG,GAAE,MAAU,GAAG,OAAO,CAExE;AAED,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EACtB,eAAe,EAAE,MAAM,GACtB,OAAO,CAET;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,iBAEnC"}
package/dist/chance.js ADDED
@@ -0,0 +1,19 @@
1
+ import { clampLikelihood } from "./clamp.js";
2
+ import { weightedSample } from "./weighted-sample.js";
3
+ export function randomInteger(min, max) {
4
+ return Math.floor(Math.random() * (max - min + 1)) + min;
5
+ }
6
+ export function randomFloat(min, max) {
7
+ const randomNum = Math.random() * (max - min) + min;
8
+ return Math.round(randomNum * 100) / 100; // Round to two decimal places
9
+ }
10
+ export function chance(likelihood = 50, max = 0) {
11
+ return randomInteger(1, 100) <= clampLikelihood(Math.max(likelihood, max));
12
+ }
13
+ export function weightedBool(trueLikelihood, falseLikelihood) {
14
+ return weightedSample([true, false], [trueLikelihood, falseLikelihood]);
15
+ }
16
+ export function sample(array) {
17
+ return array[randomInteger(0, array.length - 1)];
18
+ }
19
+ //# sourceMappingURL=chance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chance.js","sourceRoot":"","sources":["../src/chance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAC;AAEpD,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,GAAW;IACpD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,GAAW;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IACpD,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,8BAA8B;AAC1E,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,aAAqB,EAAE,EAAE,MAAc,CAAC;IAC7D,OAAO,aAAa,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,cAAsB,EACtB,eAAuB;IAEvB,OAAO,cAAc,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC,CAAE,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,MAAM,CAAI,KAAU;IAClC,OAAO,KAAK,CAAC,aAAa,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function clamp(value: number, min: number, max: number): number;
2
+ export declare function clampLikelihood(likelihood: number): number;
3
+ export declare function clampAttribute(attribute: number, min?: number): number;
4
+ //# sourceMappingURL=clamp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clamp.d.ts","sourceRoot":"","sources":["../src/clamp.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,UAEjD;AAID,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,SAAI,UAExD"}
package/dist/clamp.js ADDED
@@ -0,0 +1,11 @@
1
+ export function clamp(value, min, max) {
2
+ return Math.min(Math.max(value, min), max);
3
+ }
4
+ export function clampLikelihood(likelihood) {
5
+ return clamp(Math.round(likelihood), 0, 100);
6
+ }
7
+ const MAX_POINT = 100;
8
+ export function clampAttribute(attribute, min = 1) {
9
+ return clamp(Math.round(attribute), min, MAX_POINT);
10
+ }
11
+ //# sourceMappingURL=clamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clamp.js","sourceRoot":"","sources":["../src/clamp.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,KAAK,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IACzD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,UAAkB;IAC9C,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,SAAS,GAAG,GAAG,CAAA;AAErB,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,GAAG,GAAG,CAAC;IACrD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,4 @@
1
+ export * from './weighted-sample';
2
+ export * from './chance';
3
+ export * from "./clamp";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from './weighted-sample';
2
+ export * from './chance';
3
+ export * from "./clamp";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAA"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * https://trekhleb.medium.com/weighted-random-in-javascript-4748ab3a1500
3
+ * For example:
4
+ * - items = ['banana', 'orange', 'apple']
5
+ * - weights = [0, 0.2, 0.8]
6
+ * - weightedSample(items, weights) in 80% of cases will return 'apple', in 20% of cases will return
7
+ * 'orange' and it may return banana too even though the weight is 0
8
+ *
9
+ * Item with a -1 weight will nbe ignored.
10
+ * @param items
11
+ * @param weights
12
+ */
13
+ export declare function weightedSample<T>(items: T[], weights: number[]): T;
14
+ export declare function weightedSampleSize<T>(items: T[], weights: number[], size: number): T[];
15
+ //# sourceMappingURL=weighted-sample.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weighted-sample.d.ts","sourceRoot":"","sources":["../src/weighted-sample.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC5B,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,EAAE,MAAM,EAAE,GAClB,CAAC,CA2BH;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAChC,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,EAAE,MAAM,EAAE,EACjB,IAAI,EAAE,MAAM,GACb,CAAC,EAAE,CAkCL"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * https://trekhleb.medium.com/weighted-random-in-javascript-4748ab3a1500
3
+ * For example:
4
+ * - items = ['banana', 'orange', 'apple']
5
+ * - weights = [0, 0.2, 0.8]
6
+ * - weightedSample(items, weights) in 80% of cases will return 'apple', in 20% of cases will return
7
+ * 'orange' and it may return banana too even though the weight is 0
8
+ *
9
+ * Item with a -1 weight will nbe ignored.
10
+ * @param items
11
+ * @param weights
12
+ */
13
+ export function weightedSample(items, weights) {
14
+ if (items.length !== weights.length) {
15
+ throw new Error("Items and weights must be of the same size");
16
+ }
17
+ if (items.length === 0) {
18
+ throw new Error("Items must not be empty");
19
+ }
20
+ const cumulativeWeights = [];
21
+ for (let i = 0; i < weights.length; i += 1) {
22
+ const prevCumulative = cumulativeWeights[i - 1] ?? 0;
23
+ cumulativeWeights[i] = (weights[i] ?? 0) + prevCumulative;
24
+ }
25
+ // Use '!' or a fallback because we've already checked items.length > 0
26
+ const maxCumulativeWeight = cumulativeWeights[cumulativeWeights.length - 1];
27
+ const randomNumber = maxCumulativeWeight * Math.random();
28
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex += 1) {
29
+ if (cumulativeWeights[itemIndex] >= randomNumber) {
30
+ return items[itemIndex];
31
+ }
32
+ }
33
+ // Fallback for TS satisfaction, though mathematically unreachable
34
+ return items[items.length - 1];
35
+ }
36
+ export function weightedSampleSize(items, weights, size) {
37
+ // 1. Validation
38
+ if (items.length !== weights.length) {
39
+ throw new Error("Items and weights must be of the same size");
40
+ }
41
+ if (size > items.length) {
42
+ throw new Error("Sample size cannot be larger than the number of items");
43
+ }
44
+ // 2. Setup a pool to track remaining items and weights
45
+ const result = [];
46
+ const pool = items.map((item, i) => ({
47
+ item,
48
+ weight: weights[i] ?? 0 // Handle potential undefined/null weights
49
+ }));
50
+ // 3. Selection Loop
51
+ for (let i = 0; i < size; i++) {
52
+ const totalWeight = pool.reduce((sum, p) => sum + p.weight, 0);
53
+ let randomNumber = Math.random() * totalWeight;
54
+ for (let j = 0; j < pool.length; j++) {
55
+ const entry = pool[j]; // Safe because we are within pool.length
56
+ randomNumber -= entry.weight;
57
+ if (randomNumber <= 0 || j === pool.length - 1) {
58
+ result.push(entry.item);
59
+ pool.splice(j, 1); // Remove to ensure uniqueness
60
+ break;
61
+ }
62
+ }
63
+ }
64
+ return result;
65
+ }
66
+ //# sourceMappingURL=weighted-sample.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weighted-sample.js","sourceRoot":"","sources":["../src/weighted-sample.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAC1B,KAAU,EACV,OAAiB;IAEnB,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,iBAAiB,GAAa,EAAE,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,cAAc,GAAG,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QACrD,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,cAAc,CAAC;IAC5D,CAAC;IAED,uEAAuE;IACvE,MAAM,mBAAmB,GAAG,iBAAiB,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;IAC7E,MAAM,YAAY,GAAG,mBAAmB,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAEzD,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,CAAC,EAAE,CAAC;QACjE,IAAI,iBAAiB,CAAC,SAAS,CAAE,IAAI,YAAY,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC,SAAS,CAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAC9B,KAAU,EACV,OAAiB,EACjB,IAAY;IAEd,gBAAgB;IAChB,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IACD,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IAED,uDAAuD;IACvD,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACnC,IAAI;QACJ,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,0CAA0C;KACnE,CAAC,CAAC,CAAC;IAEJ,oBAAoB;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/D,IAAI,YAAY,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,WAAW,CAAC;QAE/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC,yCAAyC;YACjE,YAAY,IAAI,KAAK,CAAC,MAAM,CAAC;YAE7B,IAAI,YAAY,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACxB,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,8BAA8B;gBACjD,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@simcups/chance-utils",
3
+ "version": "1.0.0",
4
+ "description": "Slim utility for weighted sampling, chance logic, and math clamping.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": "./dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "prepare": "npm run build",
14
+ "prepublishOnly": "npm run build",
15
+ "clean": "rm -rf dist"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "author": "kaichunglau <laukaichung@simcups.com>",
21
+ "license": "MIT",
22
+ "devDependencies": {
23
+ "typescript": "^6.0.2"
24
+ }
25
+ }