@allemandi/gacha-engine 0.2.2 → 0.3.3

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
@@ -3,12 +3,15 @@
3
3
  [![NPM Version](https://img.shields.io/npm/v/@allemandi/gacha-engine)](https://www.npmjs.com/package/@allemandi/gacha-engine)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/allemandi/gacha-engine/blob/main/LICENSE)
5
5
 
6
- > **Practical, type-safe toolkit for simulating and understanding gacha rates and rate-ups.**
7
- >
8
- > Works in Node.js, browsers – supports ESM, CommonJS, and UMD
6
+ > **Practical, type-safe toolkit for simulating and understanding gacha rates and rate-ups.**
7
+ > Supports `"weighted"` and `"flatRate"` modes for different gacha strategies.
8
+ > Works in Node.js and browsers – supports ESM, CommonJS, and UMD.
9
+
10
+ ---
9
11
 
10
12
  <!-- omit from toc -->
11
13
  ## 🔖 Table of Contents
14
+
12
15
  - [✨ Features](#-features)
13
16
  - [🛠️ Installation](#️-installation)
14
17
  - [🚀 Quick Usage Examples](#-quick-usage-examples)
@@ -17,13 +20,17 @@
17
20
  - [🔗 Related Projects](#-related-projects)
18
21
  - [🤝 Contributing](#-contributing)
19
22
 
23
+ ---
20
24
 
21
25
  ## ✨ Features
22
- - 🎲 **Roll simulation**: Perform actual gacha rolls with weighted probabilities
23
- - 🔍 **Probability analysis**: Calculate drop rates, cumulative probabilities, and rolls needed
24
- - 📐 **Multi-rarity support**: Handle complex gacha systems with multiple rarity tiers
25
- - **Performance optimized**: Cached calculations and efficient algorithms
26
- - 🛡️ **Type-safe**: Full TypeScript support with comprehensive validation
26
+
27
+ - 🎲 **Roll simulation** Perform gacha rolls with weighted or flat-rate logic
28
+ - 🔍 **Probability analysis** Drop rates, cumulative probabilities, target probabilities
29
+ - 📐 **Multi-rarity support** Flexible rarity-based or item-based probability distributions
30
+ - **Performance optimized** Efficient with cached calculations
31
+ - 🛡️ **Type-safe** – Written in TypeScript with strict configuration validation
32
+
33
+ ---
27
34
 
28
35
  ## 🛠️ Installation
29
36
  ```bash
@@ -37,7 +44,7 @@ npm install @allemandi/gacha-engine
37
44
 
38
45
  ## 🚀 Quick Usage Examples
39
46
 
40
- **ESM**
47
+ **ESM (Weighted Mode)**
41
48
  ```js
42
49
  import { GachaEngine } from '@allemandi/gacha-engine';
43
50
 
@@ -47,17 +54,15 @@ const pools = [
47
54
  items: [
48
55
  { name: 'Super Hobo', weight: 0.8, rateUp: true },
49
56
  { name: 'Broke King', weight: 0.4 },
50
- { name: 'Cardboard Hero', weight: 0.4 },
51
- { name: 'Newspaper Warmer', weight: 0.4 }
57
+ { name: 'Cardboard Hero', weight: 0.4 }
52
58
  ]
53
59
  },
54
60
  {
55
- rarity: 'SR',
61
+ rarity: 'SR',
56
62
  items: [
57
63
  { name: 'Cold Salaryman', weight: 1.5, rateUp: true },
58
64
  { name: 'Numb Artist', weight: 1.8 },
59
- { name: 'Crying Cook', weight: 1.8 },
60
- { name: 'Lonely Cat', weight: 1.8 }
65
+ { name: 'Crying Cook', weight: 1.8 }
61
66
  ]
62
67
  },
63
68
  {
@@ -70,80 +75,138 @@ const pools = [
70
75
  ];
71
76
 
72
77
  const rarityRates = {
73
- SSR: 0.01, // 1% chance for SSR
74
- SR: 0.05, // 5% chance for SR
75
- R: 0.94 // 94% chance for R
78
+ SSR: 0.01,
79
+ SR: 0.05,
80
+ R: 0.94
76
81
  };
77
82
 
78
- const engine = new GachaEngine({ pools, rarityRates });
83
+ const engine = new GachaEngine({ mode: 'weighted', pools, rarityRates });
84
+
85
+ console.log('10 rolls:', engine.roll(10).join(', '));
79
86
 
80
- // Perform actual rolls
81
- const results = engine.roll(10);
82
- console.log(`10 rolls result: ${results.join(', ')}`);
87
+ const rate = engine.getItemDropRate('Super Hobo');
88
+ console.log('Drop rate for Super Hobo:', (rate * 100).toFixed(3) + '%');
89
+ // ~0.4% → (0.8 / 1.6) * 0.01 = 0.005 → 0.5%
83
90
 
84
- // Analyze probabilities
85
- const dropRate = engine.getItemDropRate('Super Hobo');
86
- console.log(`Drop rate for Super Hobo: ${(dropRate * 100).toFixed(3)}%`);
87
- // Output: ~0.4% (0.8/2.0 * 0.01)
91
+ const cumulative = engine.getCumulativeProbabilityForItem('Super Hobo', 300);
92
+ console.log('Probability in 300 rolls:', (cumulative * 100).toFixed(1) + '%');
93
+ // ~77.7%
88
94
 
89
- const cumulativeProb = engine.getCumulativeProbabilityForItem('Super Hobo', 300);
90
- console.log(`Probability of getting Super Hobo in 300 rolls: ${(cumulativeProb * 100).toFixed(1)}%`);
95
+ console.log('Rolls for 50% chance:', engine.getRollsForTargetProbability('Super Hobo', 0.5));
96
+ // ~138
91
97
 
92
- const rollsNeeded = engine.getRollsForTargetProbability('Super Hobo', 0.5);
93
- console.log(`Rolls needed for 50% chance: ${rollsNeeded}`);
98
+ console.log('Rate-up items:', engine.getRateUpItems().join(', '));
99
+ // Super Hobo, Cold Salaryman
94
100
 
95
- const rateUpItems = engine.getRateUpItems();
96
- console.log(`Current rate-up items: ${rateUpItems.join(', ')}`);
97
- // Output: "Super Hobo, Cold Salaryman"
101
+ console.log('All items:', engine.getAllItems().join(', '));
102
+ // Super Hobo, Broke King, Cardboard Hero, Cold Salaryman, Numb Artist, Crying Cook, Regular Joe, Normal Person
98
103
  ```
99
104
 
100
- **CommonJS**
105
+ **CommonJS (Flat Mode)**
101
106
  ```js
102
107
  const { GachaEngine } = require('@allemandi/gacha-engine');
103
108
 
104
- // Same configuration as above
105
- const engine = new GachaEngine({ pools, rarityRates });
106
- console.log('Single roll:', engine.roll());
109
+ const pools = [
110
+ {
111
+ rarity: 'SSR',
112
+ items: [
113
+ { name: 'God-Tier Rat', flatRate: 0.003, rateUp: true },
114
+ { name: 'Dumpster King', flatRate: 0.002 }
115
+ ]
116
+ },
117
+ {
118
+ rarity: 'SR',
119
+ items: [
120
+ { name: 'Sleepy Chef', flatRate: 0.015 }
121
+ ]
122
+ },
123
+ {
124
+ rarity: 'R',
125
+ items: [
126
+ { name: 'Unknown Student', flatRate: 0.1 }
127
+ ]
128
+ }
129
+ ];
130
+
131
+ const engine = new GachaEngine({ mode: 'flat', pools });
132
+
133
+ console.log('Roll x5:', engine.roll(5).join(', '));
134
+
135
+ const dropRate = engine.getItemDropRate('God-Tier Rat');
136
+ console.log('Drop rate for God-Tier Rat:', (dropRate * 100).toFixed(3) + '%');
137
+ // 0.3%
138
+
139
+ const cumulative = engine.getCumulativeProbabilityForItem('God-Tier Rat', 500);
140
+ console.log('Chance after 500 rolls:', (cumulative * 100).toFixed(1) + '%');
141
+ // ~78.5%
142
+
143
+ const rollsFor50 = engine.getRollsForTargetProbability('God-Tier Rat', 0.5);
144
+ console.log('Rolls for 50% chance:', rollsFor50);
145
+ // ~231
146
+
147
+ console.log('Rate-up items:', engine.getRateUpItems().join(', '));
148
+ // God-Tier Rat
149
+
150
+ console.log('All items:', engine.getAllItems().join(', '));
151
+ // God-Tier Rat, Dumpster King, Sleepy Chef, Unknown Student
107
152
  ```
108
153
 
109
- **UMD (Browser)**
154
+ **UMD (Browser, Weighted Mode)**
110
155
  ```html
111
156
  <script src="https://unpkg.com/@allemandi/gacha-engine"></script>
112
157
  <script>
113
- // Access the GachaEngine class
114
158
  const { GachaEngine } = window.AllemandiGachaEngine;
115
-
159
+
116
160
  const engine = new GachaEngine({
117
- rarityRates: {
118
- SSR: 0.01,
119
- SR: 0.05,
120
- R: 0.94
161
+ mode: 'weighted',
162
+ rarityRates: {
163
+ SSR: 0.02,
164
+ SR: 0.08,
165
+ R: 0.90
121
166
  },
122
167
  pools: [
123
168
  {
124
169
  rarity: 'SSR',
125
170
  items: [
126
- { name: 'Park Master', weight: 0.7, rateUp: true },
127
- { name: 'Trash Titan', weight: 0.3 }
171
+ { name: 'Trash Wizard', weight: 1.0 },
172
+ { name: 'Park Master', weight: 1.0, rateUp: true }
128
173
  ]
129
174
  },
130
175
  {
131
176
  rarity: 'SR',
132
177
  items: [
133
- { name: 'Street Sweeper', weight: 1.0 }
178
+ { name: 'Street Sweeper', weight: 2.0 },
179
+ { name: 'Bench Philosopher', weight: 1.0 }
134
180
  ]
135
181
  },
136
182
  {
137
183
  rarity: 'R',
138
184
  items: [
139
- { name: 'Regular Person', weight: 1.0 }
185
+ { name: 'Bus Stop Ghost', weight: 5.0 }
140
186
  ]
141
187
  }
142
188
  ]
143
189
  });
144
190
 
145
- console.log('Single roll:', engine.roll());
146
- console.log('Drop rate for Park Master:', engine.getItemDropRate('Park Master'));
191
+ const rate = engine.getItemDropRate('Park Master');
192
+ const rolls = engine.getRollsForTargetProbability('Park Master', 0.75);
193
+ const cumulative = engine.getCumulativeProbabilityForItem('Park Master', 200);
194
+
195
+ console.log('1x Roll:', engine.roll());
196
+ console.log('Drop rate for Park Master:', (rate * 100).toFixed(2) + '%');
197
+ // 1.0 / 2.0 * 0.02 = 0.01 → 1.00%
198
+
199
+ console.log('Cumulative 200 rolls:', (cumulative * 100).toFixed(1) + '%');
200
+ // ~86.6%
201
+
202
+ console.log('Rolls for 75% chance:', rolls);
203
+ // ~139
204
+
205
+ console.log('Rate-up items:', engine.getRateUpItems().join(', '));
206
+ // Park Master
207
+
208
+ console.log('All items:', engine.getAllItems().join(', '));
209
+ // Trash Wizard, Park Master, Street Sweeper, Bench Philosopher, Bus Stop Ghost
147
210
  </script>
148
211
  ```
149
212
 
@@ -154,14 +217,38 @@ console.log('Single roll:', engine.roll());
154
217
 
155
218
  Creates a new GachaEngine instance with validation.
156
219
 
157
- **Config Properties:**
158
- - `rarityRates` **(required)**: Object mapping rarity names to their base probabilities (should sum to ≤ 1.0)
159
- - `pools` **(required)**: Array of rarity pools, each containing:
160
- - `rarity`: String identifier matching a key in `rarityRates`
161
- - `items`: Array of items with:
162
- - `name`: Unique item identifier
163
- - `weight`: Relative weight within the rarity pool (higher = more likely)
164
- - `rateUp?`: Optional boolean flag for rate-up items
220
+ **Config Options:**
221
+
222
+ - Weighted Mode
223
+ ```ts
224
+ {
225
+ mode: 'weighted'; // (default)
226
+ rarityRates: Record<string, number>; // Required: must sum to 1.0
227
+ pools: Array<{
228
+ rarity: string; // Must match a key in `rarityRates`
229
+ items: Array<{
230
+ name: string;
231
+ weight: number;
232
+ rateUp?: boolean;
233
+ }>
234
+ }>
235
+ }
236
+ ```
237
+ - Flat Rate Mode
238
+
239
+ ```ts
240
+ {
241
+ mode: 'flatRate';
242
+ pools: Array<{
243
+ rarity: string; // Used only for categorization
244
+ items: Array<{
245
+ name: string;
246
+ weight: number; // Interpreted as direct probability (must sum to 1.0 across all items)
247
+ rateUp?: boolean;
248
+ }>
249
+ }>
250
+ }
251
+ ```
165
252
 
166
253
  ### Methods
167
254
 
@@ -173,26 +260,36 @@ Creates a new GachaEngine instance with validation.
173
260
 
174
261
  #### Analysis
175
262
  `getItemDropRate(name: string): number`
176
- - Returns the effective drop rate (0-1) for a specific item
177
- - Calculated as: `(item.weight / pool.totalWeight) × rarity.baseRate`
263
+ - Returns the effective drop rate for a specific item
264
+ - In weighted mode:
265
+ - Computed as `dropRate = (item.weight / totalPoolWeight) × rarityBaseRate`
266
+ - In flat rate mode:
267
+ - `Returns the item's defined probability.
268
+ - Throws if the item does not exist.
178
269
 
179
270
  `getRarityProbability(rarity: string): number`
180
- - Returns the base probability for a rarity tier
271
+ - Returns the base probability for a given rarity tier
272
+ - Only in "weighted" mode.
273
+ - Throws in flatRate mode.
181
274
 
182
275
  `getCumulativeProbabilityForItem(name: string, rolls: number): number`
183
276
  - Calculates probability of getting the item at least once in N rolls
184
277
  - Uses formula: `1 - (1 - dropRate)^rolls`
185
278
 
186
279
  `getRollsForTargetProbability(name: string, targetProbability: number): number`
187
- - Returns minimum rolls needed to reach target probability for an item
280
+ - Calculates the minimum number of rolls needed to reach a specific probability of pulling a given item.
188
281
  - Returns `Infinity` if item has zero drop rate
282
+ - Returns 1 if target probability ≥ 1.0
189
283
 
190
284
  #### Utility
191
285
  `getRateUpItems(): string[]`
192
286
  - Returns names of all items marked with `rateUp: true`
193
287
 
194
288
  `getAllItemDropRates(): Array<{name: string, dropRate: number, rarity: string}>`
195
- - Returns complete list of all items with their calculated drop rates and rarities
289
+ - Returns a list of all items with:
290
+ - name: Item name
291
+ - dropRate: Calculated drop probability
292
+ - rarity: Associated rarity (or "flatRate" in flat mode)
196
293
 
197
294
  ## 🧪 Tests
198
295
 
@@ -1,12 +1,18 @@
1
1
  import { GachaEngineConfig } from './types';
2
2
  export declare class GachaEngine {
3
+ private static readonly SCALE;
4
+ private static readonly MAX_SAFE_SCALE;
5
+ private mode;
3
6
  private pools;
4
- private rarityRates;
5
- private dropRateCache;
6
- constructor({ rarityRates, pools }: GachaEngineConfig);
7
+ private rarityRatesScaled;
8
+ private flatRateMap;
9
+ private dropRateCacheScaled;
10
+ constructor(config: GachaEngineConfig);
11
+ private scaleRarityRates;
12
+ private toScaled;
13
+ private fromScaled;
7
14
  private validateConfig;
8
15
  getItemDropRate(name: string): number;
9
- getRarityProbability(rarity: string): number;
10
16
  getCumulativeProbabilityForItem(name: string, rolls: number): number;
11
17
  getRollsForTargetProbability(name: string, targetProbability: number): number;
12
18
  getRateUpItems(): string[];
@@ -18,4 +24,9 @@ export declare class GachaEngine {
18
24
  roll(count?: number): string[];
19
25
  private selectRarity;
20
26
  private selectItemFromPool;
27
+ getDebugInfo(): {
28
+ scale: number;
29
+ rarityRatesScaled: Record<string, number>;
30
+ rarityRatesFloat: Record<string, number>;
31
+ };
21
32
  }
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- function t(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=Array(r);e<r;e++)n[e]=t[e];return n}function r(r,e){var n="undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(n)return(n=n.call(r)).next.bind(n);if(Array.isArray(r)||(n=function(r,e){if(r){if("string"==typeof r)return t(r,e);var n={}.toString.call(r).slice(8,-1);return"Object"===n&&r.constructor&&(n=r.constructor.name),"Map"===n||"Set"===n?Array.from(r):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?t(r,e):void 0}}(r))||e&&r&&"number"==typeof r.length){n&&(r=n);var i=0;return function(){return i>=r.length?{done:!0}:{done:!1,value:r[i++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}exports.GachaEngine=/*#__PURE__*/function(){function t(t){var r=t.rarityRates,e=t.pools;this.pools=void 0,this.rarityRates=void 0,this.dropRateCache=new Map,this.pools=e,this.rarityRates=r,this.validateConfig()}var e=t.prototype;return e.validateConfig=function(){var t=new Set(Object.keys(this.rarityRates)),e=new Set(this.pools.map(function(t){return t.rarity})),n=Array.from(e).filter(function(r){return!t.has(r)});if(n.length>0)throw new Error("Missing rarity rates for: "+n.join(", "));for(var i,o=r(this.pools);!(i=o()).done;){var a=i.value;if(0===a.items.length)throw new Error('Rarity "'+a.rarity+'" has no items');if(a.items.reduce(function(t,r){return t+r.weight},0)<=0)throw new Error('Rarity "'+a.rarity+'" has zero total weight')}},e.getItemDropRate=function(t){if(this.dropRateCache.has(t))return this.dropRateCache.get(t);for(var e,n=r(this.pools);!(e=n()).done;){var i=e.value,o=i.items.find(function(r){return r.name===t});if(o){var a=i.items.reduce(function(t,r){return t+r.weight},0),s=o.weight/a*this.rarityRates[i.rarity];return this.dropRateCache.set(t,s),s}}throw new Error('Item "'+t+'" not found')},e.getRarityProbability=function(t){if(!this.rarityRates[t])throw new Error('Rarity "'+t+'" not found');return this.rarityRates[t]},e.getCumulativeProbabilityForItem=function(t,r){var e=this.getItemDropRate(t);return 1-Math.pow(1-e,r)},e.getRollsForTargetProbability=function(t,r){var e=this.getItemDropRate(t);return e<=0?Infinity:Math.ceil(Math.log(1-r)/Math.log(1-e))},e.getRateUpItems=function(){return this.pools.flatMap(function(t){return t.items.filter(function(t){return t.rateUp}).map(function(t){return t.name})})},e.getAllItemDropRates=function(){var t=this;return this.pools.flatMap(function(r){return r.items.map(function(e){return{name:e.name,dropRate:t.getItemDropRate(e.name),rarity:r.rarity}})})},e.roll=function(t){var r=this;void 0===t&&(t=1);for(var e=[],n=function(){var t=r.selectRarity(),n=r.pools.find(function(r){return r.rarity===t}),i=r.selectItemFromPool(n);e.push(i.name)},i=0;i<t;i++)n();return e},e.selectRarity=function(){for(var t=Math.random(),r=0,e=0,n=Object.entries(this.rarityRates);e<n.length;e++){var i=n[e];if(t<=(r+=i[1]))return i[0]}return Object.keys(this.rarityRates)[0]},e.selectItemFromPool=function(t){for(var e,n=t.items.reduce(function(t,r){return t+r.weight},0),i=Math.random()*n,o=0,a=r(t.items);!(e=a()).done;){var s=e.value;if(i<=(o+=s.weight))return s}return t.items[0]},t}();
1
+ function t(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,a=Array(e);r<e;r++)a[r]=t[r];return a}function e(e,r){var a="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(a)return(a=a.call(e)).next.bind(a);if(Array.isArray(e)||(a=function(e,r){if(e){if("string"==typeof e)return t(e,r);var a={}.toString.call(e).slice(8,-1);return"Object"===a&&e.constructor&&(a=e.constructor.name),"Map"===a||"Set"===a?Array.from(e):"Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?t(e,r):void 0}}(e))||r&&e&&"number"==typeof e.length){a&&(e=a);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function r(){return r=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var a in r)({}).hasOwnProperty.call(r,a)&&(t[a]=r[a])}return t},r.apply(null,arguments)}var a,n=/*#__PURE__*/function(){function t(t){if(this.mode=void 0,this.pools=[],this.rarityRatesScaled={},this.flatRateMap=new Map,this.dropRateCacheScaled=new Map,this.mode=t.mode,"weighted"===t.mode){var r=t;this.pools=r.pools,this.rarityRatesScaled=this.scaleRarityRates(r.rarityRates),this.validateConfig(r.rarityRates)}else{if("flatRate"!==t.mode)throw new Error("Unknown gacha mode: "+this.mode);for(var a,n=e(t.pools);!(a=n()).done;)for(var i,o=e(a.value.items);!(i=o()).done;){var s=i.value;if(s.weight<0)throw new Error('FlatRate item "'+s.name+'" must have non-negative weight');this.flatRateMap.set(s.name,s.weight)}var u=Array.from(this.flatRateMap.values()).reduce(function(t,e){return t+e},0);if(Math.abs(u-1)>1e-6)throw new Error("FlatRate item rates must sum to 1.0, but got "+u)}}var a=t.prototype;return a.scaleRarityRates=function(t){for(var e={},r=0,a=Object.entries(t);r<a.length;r++){var n=a[r],i=n[0],o=n[1];if(o<0||o>1)throw new Error('Rarity rate for "'+i+'" must be between 0 and 1, got '+o);e[i]=this.toScaled(o)}return e},a.toScaled=function(e){if(e>t.MAX_SAFE_SCALE/t.SCALE)throw new Error("Probability "+e+" too large for safe integer arithmetic");return Math.round(e*t.SCALE)},a.fromScaled=function(e){return e/t.SCALE},a.validateConfig=function(t){var r=new Set(Object.keys(this.rarityRatesScaled)),a=new Set(this.pools.map(function(t){return t.rarity})),n=Array.from(a).filter(function(t){return!r.has(t)});if(n.length>0)throw new Error("Missing rarity rates for: "+n.join(", "));var i=Object.values(t).reduce(function(t,e){return t+e},0);if(Math.abs(i-1)>1e-10)throw new Error("Rarity rates must sum to 1.0, got "+i);for(var o,s=e(this.pools);!(o=s()).done;){var u=o.value;if(0===u.items.length)throw new Error('Rarity "'+u.rarity+'" has no items');if(u.items.reduce(function(t,e){return t+e.weight},0)<=0)throw new Error('Rarity "'+u.rarity+'" has zero total weight');for(var l,f=e(u.items);!(l=f()).done;){var h=l.value;if(h.weight<0)throw new Error('Item "'+h.name+'" weight must be non-negative, got '+h.weight)}if(!u.items.some(function(t){return t.weight>0}))throw new Error('Rarity "'+u.rarity+'" must have at least one item with positive weight')}},a.getItemDropRate=function(r){if("flatRate"===this.mode)return this.flatRateMap.get(r)||0;if(this.dropRateCacheScaled.has(r))return this.fromScaled(this.dropRateCacheScaled.get(r));for(var a,n=e(this.pools);!(a=n()).done;){var i=a.value,o=i.items.find(function(t){return t.name===r});if(o){if(0===o.weight)return this.dropRateCacheScaled.set(r,0),0;var s=i.items.reduce(function(t,e){return t+e.weight},0),u=this.rarityRatesScaled[i.rarity],l=this.toScaled(o.weight),f=this.toScaled(s),h=Math.round(l*u/t.SCALE),c=Math.round(h*t.SCALE/f);return this.dropRateCacheScaled.set(r,c),this.fromScaled(c)}}throw new Error('Item "'+r+'" not found')},a.getCumulativeProbabilityForItem=function(t,e){var r=this.getItemDropRate(t);return 0===r?0:r>=1?1:1-Math.pow(1-r,e)},a.getRollsForTargetProbability=function(t,e){if(e<=0)return 0;if(e>=1)return 1;var r=this.getItemDropRate(t);return r<=0?Infinity:Math.ceil(Math.log(1-e)/Math.log(1-r))},a.getRateUpItems=function(){return this.pools.flatMap(function(t){return t.items.filter(function(t){return t.rateUp}).map(function(t){return t.name})})},a.getAllItemDropRates=function(){var t=this;return"flatRate"===this.mode?Array.from(this.flatRateMap.entries()).map(function(t){return{name:t[0],dropRate:t[1],rarity:"flatRate"}}):this.pools.flatMap(function(e){return e.items.map(function(r){return{name:r.name,dropRate:t.getItemDropRate(r.name),rarity:e.rarity}})})},a.roll=function(t){var r=this;void 0===t&&(t=1);for(var a=[],n=function(){if("flatRate"===r.mode)for(var t,n=Math.random(),i=0,o=e(r.flatRateMap.entries());!(t=o()).done;){var s=t.value;if(n<(i+=s[1])){a.push(s[0]);break}}else{var u=r.selectRarity(),l=r.pools.find(function(t){return t.rarity===u}),f=r.selectItemFromPool(l);a.push(f.name)}},i=0;i<t;i++)n();return a},a.selectRarity=function(){for(var e=Math.floor(Math.random()*t.SCALE),r=0,a=0,n=Object.entries(this.rarityRatesScaled);a<n.length;a++){var i=n[a];if(e<(r+=i[1]))return i[0]}return Object.keys(this.rarityRatesScaled)[0]},a.selectItemFromPool=function(t){for(var a,n=this,i=t.items.filter(function(t){return t.weight>0}),o=i.map(function(t){return r({},t,{scaledWeight:n.toScaled(t.weight)})}),s=o.reduce(function(t,e){return t+e.scaledWeight},0),u=Math.floor(Math.random()*s),l=0,f=e(o);!(a=f()).done;){var h=a.value;if(u<(l+=h.scaledWeight))return{name:h.name,weight:h.weight}}return i[0]},a.getDebugInfo=function(){for(var e={},a=0,n=Object.entries(this.rarityRatesScaled);a<n.length;a++){var i=n[a];e[i[0]]=this.fromScaled(i[1])}return{scale:t.SCALE,rarityRatesScaled:r({},this.rarityRatesScaled),rarityRatesFloat:e}},t}();a=n,n.SCALE=1e6,n.MAX_SAFE_SCALE=Math.floor(Number.MAX_SAFE_INTEGER/a.SCALE),exports.GachaEngine=n;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/gacha-engine.ts"],"sourcesContent":["import { RarityInput, GachaEngineConfig } from './types';\n\nexport class GachaEngine {\n private pools: RarityInput[];\n private rarityRates: Record<string, number>;\n private dropRateCache = new Map<string, number>();\n\n constructor({ rarityRates, pools }: GachaEngineConfig) {\n this.pools = pools;\n this.rarityRates = rarityRates;\n this.validateConfig();\n }\n\n private validateConfig(): void {\n const configuredRarities = new Set(Object.keys(this.rarityRates));\n const usedRarities = new Set(this.pools.map(p => p.rarity));\n const missingArray = Array.from(usedRarities).filter(r => !configuredRarities.has(r));\n if (missingArray.length > 0) {\n throw new Error(`Missing rarity rates for: ${missingArray.join(', ')}`);\n }\n\n for (const pool of this.pools) {\n if (pool.items.length === 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has no items`);\n }\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n if (totalWeight <= 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has zero total weight`);\n }\n }\n }\n\n getItemDropRate(name: string): number {\n if (this.dropRateCache.has(name)) {\n return this.dropRateCache.get(name)!;\n }\n\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n const totalPoolWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const baseRarityRate = this.rarityRates[pool.rarity];\n const rate = (item.weight / totalPoolWeight) * baseRarityRate;\n this.dropRateCache.set(name, rate);\n return rate;\n }\n }\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getRarityProbability(rarity: string): number {\n if (!this.rarityRates[rarity]) {\n throw new Error(`Rarity \"${rarity}\" not found`);\n }\n return this.rarityRates[rarity];\n }\n\n getCumulativeProbabilityForItem(name: string, rolls: number): number {\n const rate = this.getItemDropRate(name);\n return 1 - Math.pow(1 - rate, rolls);\n }\n\n getRollsForTargetProbability(name: string, targetProbability: number): number {\n const rate = this.getItemDropRate(name);\n if (rate <= 0) return Infinity;\n return Math.ceil(Math.log(1 - targetProbability) / Math.log(1 - rate));\n }\n\n getRateUpItems(): string[] {\n return this.pools.flatMap(p =>\n p.items.filter(i => i.rateUp).map(i => i.name)\n );\n }\n\n getAllItemDropRates(): { name: string; dropRate: number; rarity: string }[] {\n return this.pools.flatMap(p =>\n p.items.map(i => ({\n name: i.name,\n dropRate: this.getItemDropRate(i.name),\n rarity: p.rarity\n }))\n );\n }\n\n roll(count: number = 1): string[] {\n const results: string[] = [];\n for (let i = 0; i < count; i++) {\n const rarity = this.selectRarity();\n const pool = this.pools.find(p => p.rarity === rarity)!;\n const item = this.selectItemFromPool(pool);\n results.push(item.name);\n }\n return results;\n }\n\n private selectRarity(): string {\n const rand = Math.random();\n let cumulative = 0;\n for (const [rarity, rate] of Object.entries(this.rarityRates)) {\n cumulative += rate;\n if (rand <= cumulative) return rarity;\n }\n return Object.keys(this.rarityRates)[0];\n }\n\n private selectItemFromPool(pool: RarityInput): { name: string; weight: number } {\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const rand = Math.random() * totalWeight;\n let cumulative = 0;\n for (const item of pool.items) {\n cumulative += item.weight;\n if (rand <= cumulative) return item;\n }\n return pool.items[0];\n }\n}"],"names":["GachaEngine","_ref","rarityRates","pools","this","dropRateCache","Map","validateConfig","_proto","prototype","configuredRarities","Set","Object","keys","usedRarities","map","p","rarity","missingArray","Array","from","filter","r","has","length","Error","join","_step","_iterator","_createForOfIteratorHelperLoose","done","pool","value","items","reduce","sum","i","weight","getItemDropRate","name","get","_step2","_iterator2","item","find","totalPoolWeight","rate","set","getRarityProbability","getCumulativeProbabilityForItem","rolls","Math","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","rateUp","getAllItemDropRates","_this","dropRate","roll","count","_this2","results","_loop","selectRarity","selectItemFromPool","push","rand","random","cumulative","_i","_Object$entries","entries","_Object$entries$_i","_step3","totalWeight","_iterator3"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/gacha-engine.ts"],"sourcesContent":["import {\n RarityInput,\n GachaEngineConfig,\n WeightedGachaEngineConfig,\n FlatRateGachaEngineConfig,\n} from './types';\n\nexport class GachaEngine {\n private static readonly SCALE = 1_000_000;\n private static readonly MAX_SAFE_SCALE = Math.floor(Number.MAX_SAFE_INTEGER / GachaEngine.SCALE);\n\n private mode: 'weighted' | 'flatRate';\n private pools: RarityInput[] = [];\n private rarityRatesScaled: Record<string, number> = {};\n private flatRateMap: Map<string, number> = new Map();\n private dropRateCacheScaled = new Map<string, number>();\n\n constructor(config: GachaEngineConfig) {\n this.mode = config.mode;\n\n if (config.mode === 'weighted') {\n const weightedConfig = config as WeightedGachaEngineConfig;\n this.pools = weightedConfig.pools;\n this.rarityRatesScaled = this.scaleRarityRates(weightedConfig.rarityRates);\n this.validateConfig(weightedConfig.rarityRates);\n } else if (config.mode === 'flatRate') {\n const flatConfig = config as FlatRateGachaEngineConfig;\n for (const pool of flatConfig.pools) {\n for (const item of pool.items) {\n if (item.weight < 0) {\n throw new Error(`FlatRate item \"${item.name}\" must have non-negative weight`);\n }\n this.flatRateMap.set(item.name, item.weight); // Here, interpreted as direct probability\n }\n }\n const total = Array.from(this.flatRateMap.values()).reduce((sum, v) => sum + v, 0);\n if (Math.abs(total - 1.0) > 1e-6) {\n throw new Error(`FlatRate item rates must sum to 1.0, but got ${total}`);\n }\n } else {\n throw new Error(`Unknown gacha mode: ${this.mode}`);\n }\n }\n\n private scaleRarityRates(rarityRates: Record<string, number>): Record<string, number> {\n const scaled: Record<string, number> = {};\n for (const [rarity, rate] of Object.entries(rarityRates)) {\n if (rate < 0 || rate > 1) {\n throw new Error(`Rarity rate for \"${rarity}\" must be between 0 and 1, got ${rate}`);\n }\n scaled[rarity] = this.toScaled(rate);\n }\n return scaled;\n }\n\n private toScaled(probability: number): number {\n if (probability > GachaEngine.MAX_SAFE_SCALE / GachaEngine.SCALE) {\n throw new Error(`Probability ${probability} too large for safe integer arithmetic`);\n }\n return Math.round(probability * GachaEngine.SCALE);\n }\n\n private fromScaled(scaledInt: number): number {\n return scaledInt / GachaEngine.SCALE;\n }\n\n private validateConfig(originalRates: Record<string, number>): void {\n const configuredRarities = new Set(Object.keys(this.rarityRatesScaled));\n const usedRarities = new Set(this.pools.map(p => p.rarity));\n const missing = Array.from(usedRarities).filter(r => !configuredRarities.has(r));\n\n if (missing.length > 0) {\n throw new Error(`Missing rarity rates for: ${missing.join(', ')}`);\n }\n\n const totalRate = Object.values(originalRates).reduce((sum, rate) => sum + rate, 0);\n if (Math.abs(totalRate - 1.0) > 1e-10) {\n throw new Error(`Rarity rates must sum to 1.0, got ${totalRate}`);\n }\n\n for (const pool of this.pools) {\n if (pool.items.length === 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has no items`);\n }\n\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n if (totalWeight <= 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has zero total weight`);\n }\n\n for (const item of pool.items) {\n if (item.weight < 0) {\n throw new Error(`Item \"${item.name}\" weight must be non-negative, got ${item.weight}`);\n }\n }\n\n if (!pool.items.some(i => i.weight > 0)) {\n throw new Error(`Rarity \"${pool.rarity}\" must have at least one item with positive weight`);\n }\n }\n }\n\n getItemDropRate(name: string): number {\n if (this.mode === 'flatRate') {\n return this.flatRateMap.get(name) || 0;\n }\n\n if (this.dropRateCacheScaled.has(name)) {\n return this.fromScaled(this.dropRateCacheScaled.get(name)!);\n }\n\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n if (item.weight === 0) {\n this.dropRateCacheScaled.set(name, 0);\n return 0;\n }\n\n const totalPoolWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const baseRarityRateScaled = this.rarityRatesScaled[pool.rarity];\n const itemWeightScaled = this.toScaled(item.weight);\n const totalWeightScaled = this.toScaled(totalPoolWeight);\n const numeratorScaled = Math.round((itemWeightScaled * baseRarityRateScaled) / GachaEngine.SCALE);\n const rateScaled = Math.round((numeratorScaled * GachaEngine.SCALE) / totalWeightScaled);\n\n this.dropRateCacheScaled.set(name, rateScaled);\n return this.fromScaled(rateScaled);\n }\n }\n\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getCumulativeProbabilityForItem(name: string, rolls: number): number {\n const rate = this.getItemDropRate(name);\n if (rate === 0) return 0;\n if (rate >= 1) return 1;\n\n const cumulativeFailProbability = Math.pow(1 - rate, rolls);\n return 1 - cumulativeFailProbability;\n }\n\n getRollsForTargetProbability(name: string, targetProbability: number): number {\n if (targetProbability <= 0) return 0;\n if (targetProbability >= 1) return 1;\n\n const rate = this.getItemDropRate(name);\n if (rate <= 0) return Infinity;\n return Math.ceil(Math.log(1 - targetProbability) / Math.log(1 - rate));\n }\n\n getRateUpItems(): string[] {\n return this.pools.flatMap(p => p.items.filter(i => i.rateUp).map(i => i.name));\n }\n\n getAllItemDropRates(): { name: string; dropRate: number; rarity: string }[] {\n if (this.mode === 'flatRate') {\n return Array.from(this.flatRateMap.entries()).map(([name, dropRate]) => ({\n name,\n dropRate,\n rarity: 'flatRate',\n }));\n }\n\n return this.pools.flatMap(p =>\n p.items.map(i => ({\n name: i.name,\n dropRate: this.getItemDropRate(i.name),\n rarity: p.rarity,\n }))\n );\n }\n\n roll(count: number = 1): string[] {\n const results: string[] = [];\n for (let i = 0; i < count; i++) {\n if (this.mode === 'flatRate') {\n const rand = Math.random();\n let cumulative = 0;\n for (const [name, rate] of this.flatRateMap.entries()) {\n cumulative += rate;\n if (rand < cumulative) {\n results.push(name);\n break;\n }\n }\n } else {\n const rarity = this.selectRarity();\n const pool = this.pools.find(p => p.rarity === rarity)!;\n const item = this.selectItemFromPool(pool);\n results.push(item.name);\n }\n }\n return results;\n }\n\n private selectRarity(): string {\n const rand = Math.floor(Math.random() * GachaEngine.SCALE);\n let cumulative = 0;\n\n for (const [rarity, scaledRate] of Object.entries(this.rarityRatesScaled)) {\n cumulative += scaledRate;\n if (rand < cumulative) return rarity;\n }\n\n return Object.keys(this.rarityRatesScaled)[0];\n }\n\n private selectItemFromPool(pool: RarityInput): { name: string; weight: number } {\n const items = pool.items.filter(i => i.weight > 0);\n const scaledItems = items.map(i => ({\n ...i,\n scaledWeight: this.toScaled(i.weight),\n }));\n\n const totalScaledWeight = scaledItems.reduce((sum, i) => sum + i.scaledWeight, 0);\n const rand = Math.floor(Math.random() * totalScaledWeight);\n let cumulative = 0;\n\n for (const item of scaledItems) {\n cumulative += item.scaledWeight;\n if (rand < cumulative) {\n return { name: item.name, weight: item.weight };\n }\n }\n\n return items[0]; // Fallback\n }\n\n getDebugInfo(): {\n scale: number;\n rarityRatesScaled: Record<string, number>;\n rarityRatesFloat: Record<string, number>;\n } {\n const rarityRatesFloat: Record<string, number> = {};\n for (const [rarity, scaledRate] of Object.entries(this.rarityRatesScaled)) {\n rarityRatesFloat[rarity] = this.fromScaled(scaledRate);\n }\n\n return {\n scale: GachaEngine.SCALE,\n rarityRatesScaled: { ...this.rarityRatesScaled },\n rarityRatesFloat,\n };\n }\n}\n"],"names":["GachaEngine","config","this","mode","pools","rarityRatesScaled","flatRateMap","Map","dropRateCacheScaled","weightedConfig","scaleRarityRates","rarityRates","validateConfig","Error","_step","_iterator","_createForOfIteratorHelperLoose","done","_step2","_iterator2","value","items","item","weight","name","set","total","Array","from","values","reduce","sum","v","Math","abs","_proto","prototype","scaled","_i","_Object$entries","Object","entries","length","_Object$entries$_i","rarity","rate","toScaled","probability","MAX_SAFE_SCALE","SCALE","round","fromScaled","scaledInt","originalRates","configuredRarities","Set","keys","usedRarities","map","p","missing","filter","r","has","join","totalRate","_iterator3","_step3","pool","i","_step4","_iterator4","some","getItemDropRate","get","_iterator5","_step5","find","totalPoolWeight","baseRarityRateScaled","itemWeightScaled","totalWeightScaled","numeratorScaled","rateScaled","getCumulativeProbabilityForItem","rolls","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","rateUp","getAllItemDropRates","_this","_ref","dropRate","roll","count","_this2","results","_loop","_step6","rand","random","cumulative","_iterator6","_step6$value","push","selectRarity","selectItemFromPool","floor","_i2","_Object$entries2","_Object$entries2$_i","scaledRate","_step7","_this3","scaledItems","_extends","scaledWeight","totalScaledWeight","_iterator7","getDebugInfo","rarityRatesFloat","_i3","_Object$entries3","_Object$entries3$_i","scale","Number","MAX_SAFE_INTEGER"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- function t(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=Array(r);e<r;e++)n[e]=t[e];return n}function r(r,e){var n="undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(n)return(n=n.call(r)).next.bind(n);if(Array.isArray(r)||(n=function(r,e){if(r){if("string"==typeof r)return t(r,e);var n={}.toString.call(r).slice(8,-1);return"Object"===n&&r.constructor&&(n=r.constructor.name),"Map"===n||"Set"===n?Array.from(r):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?t(r,e):void 0}}(r))||e&&r&&"number"==typeof r.length){n&&(r=n);var i=0;return function(){return i>=r.length?{done:!0}:{done:!1,value:r[i++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var e=/*#__PURE__*/function(){function t(t){var r=t.rarityRates,e=t.pools;this.pools=void 0,this.rarityRates=void 0,this.dropRateCache=new Map,this.pools=e,this.rarityRates=r,this.validateConfig()}var e=t.prototype;return e.validateConfig=function(){var t=new Set(Object.keys(this.rarityRates)),e=new Set(this.pools.map(function(t){return t.rarity})),n=Array.from(e).filter(function(r){return!t.has(r)});if(n.length>0)throw new Error("Missing rarity rates for: "+n.join(", "));for(var i,o=r(this.pools);!(i=o()).done;){var a=i.value;if(0===a.items.length)throw new Error('Rarity "'+a.rarity+'" has no items');if(a.items.reduce(function(t,r){return t+r.weight},0)<=0)throw new Error('Rarity "'+a.rarity+'" has zero total weight')}},e.getItemDropRate=function(t){if(this.dropRateCache.has(t))return this.dropRateCache.get(t);for(var e,n=r(this.pools);!(e=n()).done;){var i=e.value,o=i.items.find(function(r){return r.name===t});if(o){var a=i.items.reduce(function(t,r){return t+r.weight},0),s=o.weight/a*this.rarityRates[i.rarity];return this.dropRateCache.set(t,s),s}}throw new Error('Item "'+t+'" not found')},e.getRarityProbability=function(t){if(!this.rarityRates[t])throw new Error('Rarity "'+t+'" not found');return this.rarityRates[t]},e.getCumulativeProbabilityForItem=function(t,r){var e=this.getItemDropRate(t);return 1-Math.pow(1-e,r)},e.getRollsForTargetProbability=function(t,r){var e=this.getItemDropRate(t);return e<=0?Infinity:Math.ceil(Math.log(1-r)/Math.log(1-e))},e.getRateUpItems=function(){return this.pools.flatMap(function(t){return t.items.filter(function(t){return t.rateUp}).map(function(t){return t.name})})},e.getAllItemDropRates=function(){var t=this;return this.pools.flatMap(function(r){return r.items.map(function(e){return{name:e.name,dropRate:t.getItemDropRate(e.name),rarity:r.rarity}})})},e.roll=function(t){var r=this;void 0===t&&(t=1);for(var e=[],n=function(){var t=r.selectRarity(),n=r.pools.find(function(r){return r.rarity===t}),i=r.selectItemFromPool(n);e.push(i.name)},i=0;i<t;i++)n();return e},e.selectRarity=function(){for(var t=Math.random(),r=0,e=0,n=Object.entries(this.rarityRates);e<n.length;e++){var i=n[e];if(t<=(r+=i[1]))return i[0]}return Object.keys(this.rarityRates)[0]},e.selectItemFromPool=function(t){for(var e,n=t.items.reduce(function(t,r){return t+r.weight},0),i=Math.random()*n,o=0,a=r(t.items);!(e=a()).done;){var s=e.value;if(i<=(o+=s.weight))return s}return t.items[0]},t}();export{e as GachaEngine};
1
+ function t(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,a=Array(e);r<e;r++)a[r]=t[r];return a}function e(e,r){var a="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(a)return(a=a.call(e)).next.bind(a);if(Array.isArray(e)||(a=function(e,r){if(e){if("string"==typeof e)return t(e,r);var a={}.toString.call(e).slice(8,-1);return"Object"===a&&e.constructor&&(a=e.constructor.name),"Map"===a||"Set"===a?Array.from(e):"Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?t(e,r):void 0}}(e))||r&&e&&"number"==typeof e.length){a&&(e=a);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function r(){return r=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var a in r)({}).hasOwnProperty.call(r,a)&&(t[a]=r[a])}return t},r.apply(null,arguments)}var a,n=/*#__PURE__*/function(){function t(t){if(this.mode=void 0,this.pools=[],this.rarityRatesScaled={},this.flatRateMap=new Map,this.dropRateCacheScaled=new Map,this.mode=t.mode,"weighted"===t.mode){var r=t;this.pools=r.pools,this.rarityRatesScaled=this.scaleRarityRates(r.rarityRates),this.validateConfig(r.rarityRates)}else{if("flatRate"!==t.mode)throw new Error("Unknown gacha mode: "+this.mode);for(var a,n=e(t.pools);!(a=n()).done;)for(var i,o=e(a.value.items);!(i=o()).done;){var s=i.value;if(s.weight<0)throw new Error('FlatRate item "'+s.name+'" must have non-negative weight');this.flatRateMap.set(s.name,s.weight)}var u=Array.from(this.flatRateMap.values()).reduce(function(t,e){return t+e},0);if(Math.abs(u-1)>1e-6)throw new Error("FlatRate item rates must sum to 1.0, but got "+u)}}var a=t.prototype;return a.scaleRarityRates=function(t){for(var e={},r=0,a=Object.entries(t);r<a.length;r++){var n=a[r],i=n[0],o=n[1];if(o<0||o>1)throw new Error('Rarity rate for "'+i+'" must be between 0 and 1, got '+o);e[i]=this.toScaled(o)}return e},a.toScaled=function(e){if(e>t.MAX_SAFE_SCALE/t.SCALE)throw new Error("Probability "+e+" too large for safe integer arithmetic");return Math.round(e*t.SCALE)},a.fromScaled=function(e){return e/t.SCALE},a.validateConfig=function(t){var r=new Set(Object.keys(this.rarityRatesScaled)),a=new Set(this.pools.map(function(t){return t.rarity})),n=Array.from(a).filter(function(t){return!r.has(t)});if(n.length>0)throw new Error("Missing rarity rates for: "+n.join(", "));var i=Object.values(t).reduce(function(t,e){return t+e},0);if(Math.abs(i-1)>1e-10)throw new Error("Rarity rates must sum to 1.0, got "+i);for(var o,s=e(this.pools);!(o=s()).done;){var u=o.value;if(0===u.items.length)throw new Error('Rarity "'+u.rarity+'" has no items');if(u.items.reduce(function(t,e){return t+e.weight},0)<=0)throw new Error('Rarity "'+u.rarity+'" has zero total weight');for(var l,f=e(u.items);!(l=f()).done;){var h=l.value;if(h.weight<0)throw new Error('Item "'+h.name+'" weight must be non-negative, got '+h.weight)}if(!u.items.some(function(t){return t.weight>0}))throw new Error('Rarity "'+u.rarity+'" must have at least one item with positive weight')}},a.getItemDropRate=function(r){if("flatRate"===this.mode)return this.flatRateMap.get(r)||0;if(this.dropRateCacheScaled.has(r))return this.fromScaled(this.dropRateCacheScaled.get(r));for(var a,n=e(this.pools);!(a=n()).done;){var i=a.value,o=i.items.find(function(t){return t.name===r});if(o){if(0===o.weight)return this.dropRateCacheScaled.set(r,0),0;var s=i.items.reduce(function(t,e){return t+e.weight},0),u=this.rarityRatesScaled[i.rarity],l=this.toScaled(o.weight),f=this.toScaled(s),h=Math.round(l*u/t.SCALE),c=Math.round(h*t.SCALE/f);return this.dropRateCacheScaled.set(r,c),this.fromScaled(c)}}throw new Error('Item "'+r+'" not found')},a.getCumulativeProbabilityForItem=function(t,e){var r=this.getItemDropRate(t);return 0===r?0:r>=1?1:1-Math.pow(1-r,e)},a.getRollsForTargetProbability=function(t,e){if(e<=0)return 0;if(e>=1)return 1;var r=this.getItemDropRate(t);return r<=0?Infinity:Math.ceil(Math.log(1-e)/Math.log(1-r))},a.getRateUpItems=function(){return this.pools.flatMap(function(t){return t.items.filter(function(t){return t.rateUp}).map(function(t){return t.name})})},a.getAllItemDropRates=function(){var t=this;return"flatRate"===this.mode?Array.from(this.flatRateMap.entries()).map(function(t){return{name:t[0],dropRate:t[1],rarity:"flatRate"}}):this.pools.flatMap(function(e){return e.items.map(function(r){return{name:r.name,dropRate:t.getItemDropRate(r.name),rarity:e.rarity}})})},a.roll=function(t){var r=this;void 0===t&&(t=1);for(var a=[],n=function(){if("flatRate"===r.mode)for(var t,n=Math.random(),i=0,o=e(r.flatRateMap.entries());!(t=o()).done;){var s=t.value;if(n<(i+=s[1])){a.push(s[0]);break}}else{var u=r.selectRarity(),l=r.pools.find(function(t){return t.rarity===u}),f=r.selectItemFromPool(l);a.push(f.name)}},i=0;i<t;i++)n();return a},a.selectRarity=function(){for(var e=Math.floor(Math.random()*t.SCALE),r=0,a=0,n=Object.entries(this.rarityRatesScaled);a<n.length;a++){var i=n[a];if(e<(r+=i[1]))return i[0]}return Object.keys(this.rarityRatesScaled)[0]},a.selectItemFromPool=function(t){for(var a,n=this,i=t.items.filter(function(t){return t.weight>0}),o=i.map(function(t){return r({},t,{scaledWeight:n.toScaled(t.weight)})}),s=o.reduce(function(t,e){return t+e.scaledWeight},0),u=Math.floor(Math.random()*s),l=0,f=e(o);!(a=f()).done;){var h=a.value;if(u<(l+=h.scaledWeight))return{name:h.name,weight:h.weight}}return i[0]},a.getDebugInfo=function(){for(var e={},a=0,n=Object.entries(this.rarityRatesScaled);a<n.length;a++){var i=n[a];e[i[0]]=this.fromScaled(i[1])}return{scale:t.SCALE,rarityRatesScaled:r({},this.rarityRatesScaled),rarityRatesFloat:e}},t}();a=n,n.SCALE=1e6,n.MAX_SAFE_SCALE=Math.floor(Number.MAX_SAFE_INTEGER/a.SCALE);export{n as GachaEngine};
2
2
  //# sourceMappingURL=index.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.module.js","sources":["../src/gacha-engine.ts"],"sourcesContent":["import { RarityInput, GachaEngineConfig } from './types';\n\nexport class GachaEngine {\n private pools: RarityInput[];\n private rarityRates: Record<string, number>;\n private dropRateCache = new Map<string, number>();\n\n constructor({ rarityRates, pools }: GachaEngineConfig) {\n this.pools = pools;\n this.rarityRates = rarityRates;\n this.validateConfig();\n }\n\n private validateConfig(): void {\n const configuredRarities = new Set(Object.keys(this.rarityRates));\n const usedRarities = new Set(this.pools.map(p => p.rarity));\n const missingArray = Array.from(usedRarities).filter(r => !configuredRarities.has(r));\n if (missingArray.length > 0) {\n throw new Error(`Missing rarity rates for: ${missingArray.join(', ')}`);\n }\n\n for (const pool of this.pools) {\n if (pool.items.length === 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has no items`);\n }\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n if (totalWeight <= 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has zero total weight`);\n }\n }\n }\n\n getItemDropRate(name: string): number {\n if (this.dropRateCache.has(name)) {\n return this.dropRateCache.get(name)!;\n }\n\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n const totalPoolWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const baseRarityRate = this.rarityRates[pool.rarity];\n const rate = (item.weight / totalPoolWeight) * baseRarityRate;\n this.dropRateCache.set(name, rate);\n return rate;\n }\n }\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getRarityProbability(rarity: string): number {\n if (!this.rarityRates[rarity]) {\n throw new Error(`Rarity \"${rarity}\" not found`);\n }\n return this.rarityRates[rarity];\n }\n\n getCumulativeProbabilityForItem(name: string, rolls: number): number {\n const rate = this.getItemDropRate(name);\n return 1 - Math.pow(1 - rate, rolls);\n }\n\n getRollsForTargetProbability(name: string, targetProbability: number): number {\n const rate = this.getItemDropRate(name);\n if (rate <= 0) return Infinity;\n return Math.ceil(Math.log(1 - targetProbability) / Math.log(1 - rate));\n }\n\n getRateUpItems(): string[] {\n return this.pools.flatMap(p =>\n p.items.filter(i => i.rateUp).map(i => i.name)\n );\n }\n\n getAllItemDropRates(): { name: string; dropRate: number; rarity: string }[] {\n return this.pools.flatMap(p =>\n p.items.map(i => ({\n name: i.name,\n dropRate: this.getItemDropRate(i.name),\n rarity: p.rarity\n }))\n );\n }\n\n roll(count: number = 1): string[] {\n const results: string[] = [];\n for (let i = 0; i < count; i++) {\n const rarity = this.selectRarity();\n const pool = this.pools.find(p => p.rarity === rarity)!;\n const item = this.selectItemFromPool(pool);\n results.push(item.name);\n }\n return results;\n }\n\n private selectRarity(): string {\n const rand = Math.random();\n let cumulative = 0;\n for (const [rarity, rate] of Object.entries(this.rarityRates)) {\n cumulative += rate;\n if (rand <= cumulative) return rarity;\n }\n return Object.keys(this.rarityRates)[0];\n }\n\n private selectItemFromPool(pool: RarityInput): { name: string; weight: number } {\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const rand = Math.random() * totalWeight;\n let cumulative = 0;\n for (const item of pool.items) {\n cumulative += item.weight;\n if (rand <= cumulative) return item;\n }\n return pool.items[0];\n }\n}"],"names":["GachaEngine","_ref","rarityRates","pools","this","dropRateCache","Map","validateConfig","_proto","prototype","configuredRarities","Set","Object","keys","usedRarities","map","p","rarity","missingArray","Array","from","filter","r","has","length","Error","join","_step","_iterator","_createForOfIteratorHelperLoose","done","pool","value","items","reduce","sum","i","weight","getItemDropRate","name","get","_step2","_iterator2","item","find","totalPoolWeight","rate","set","getRarityProbability","getCumulativeProbabilityForItem","rolls","Math","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","rateUp","getAllItemDropRates","_this","dropRate","roll","count","_this2","results","_loop","selectRarity","selectItemFromPool","push","rand","random","cumulative","_i","_Object$entries","entries","_Object$entries$_i","_step3","totalWeight","_iterator3"],"mappings":"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"}
1
+ {"version":3,"file":"index.module.js","sources":["../src/gacha-engine.ts"],"sourcesContent":["import {\n RarityInput,\n GachaEngineConfig,\n WeightedGachaEngineConfig,\n FlatRateGachaEngineConfig,\n} from './types';\n\nexport class GachaEngine {\n private static readonly SCALE = 1_000_000;\n private static readonly MAX_SAFE_SCALE = Math.floor(Number.MAX_SAFE_INTEGER / GachaEngine.SCALE);\n\n private mode: 'weighted' | 'flatRate';\n private pools: RarityInput[] = [];\n private rarityRatesScaled: Record<string, number> = {};\n private flatRateMap: Map<string, number> = new Map();\n private dropRateCacheScaled = new Map<string, number>();\n\n constructor(config: GachaEngineConfig) {\n this.mode = config.mode;\n\n if (config.mode === 'weighted') {\n const weightedConfig = config as WeightedGachaEngineConfig;\n this.pools = weightedConfig.pools;\n this.rarityRatesScaled = this.scaleRarityRates(weightedConfig.rarityRates);\n this.validateConfig(weightedConfig.rarityRates);\n } else if (config.mode === 'flatRate') {\n const flatConfig = config as FlatRateGachaEngineConfig;\n for (const pool of flatConfig.pools) {\n for (const item of pool.items) {\n if (item.weight < 0) {\n throw new Error(`FlatRate item \"${item.name}\" must have non-negative weight`);\n }\n this.flatRateMap.set(item.name, item.weight); // Here, interpreted as direct probability\n }\n }\n const total = Array.from(this.flatRateMap.values()).reduce((sum, v) => sum + v, 0);\n if (Math.abs(total - 1.0) > 1e-6) {\n throw new Error(`FlatRate item rates must sum to 1.0, but got ${total}`);\n }\n } else {\n throw new Error(`Unknown gacha mode: ${this.mode}`);\n }\n }\n\n private scaleRarityRates(rarityRates: Record<string, number>): Record<string, number> {\n const scaled: Record<string, number> = {};\n for (const [rarity, rate] of Object.entries(rarityRates)) {\n if (rate < 0 || rate > 1) {\n throw new Error(`Rarity rate for \"${rarity}\" must be between 0 and 1, got ${rate}`);\n }\n scaled[rarity] = this.toScaled(rate);\n }\n return scaled;\n }\n\n private toScaled(probability: number): number {\n if (probability > GachaEngine.MAX_SAFE_SCALE / GachaEngine.SCALE) {\n throw new Error(`Probability ${probability} too large for safe integer arithmetic`);\n }\n return Math.round(probability * GachaEngine.SCALE);\n }\n\n private fromScaled(scaledInt: number): number {\n return scaledInt / GachaEngine.SCALE;\n }\n\n private validateConfig(originalRates: Record<string, number>): void {\n const configuredRarities = new Set(Object.keys(this.rarityRatesScaled));\n const usedRarities = new Set(this.pools.map(p => p.rarity));\n const missing = Array.from(usedRarities).filter(r => !configuredRarities.has(r));\n\n if (missing.length > 0) {\n throw new Error(`Missing rarity rates for: ${missing.join(', ')}`);\n }\n\n const totalRate = Object.values(originalRates).reduce((sum, rate) => sum + rate, 0);\n if (Math.abs(totalRate - 1.0) > 1e-10) {\n throw new Error(`Rarity rates must sum to 1.0, got ${totalRate}`);\n }\n\n for (const pool of this.pools) {\n if (pool.items.length === 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has no items`);\n }\n\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n if (totalWeight <= 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has zero total weight`);\n }\n\n for (const item of pool.items) {\n if (item.weight < 0) {\n throw new Error(`Item \"${item.name}\" weight must be non-negative, got ${item.weight}`);\n }\n }\n\n if (!pool.items.some(i => i.weight > 0)) {\n throw new Error(`Rarity \"${pool.rarity}\" must have at least one item with positive weight`);\n }\n }\n }\n\n getItemDropRate(name: string): number {\n if (this.mode === 'flatRate') {\n return this.flatRateMap.get(name) || 0;\n }\n\n if (this.dropRateCacheScaled.has(name)) {\n return this.fromScaled(this.dropRateCacheScaled.get(name)!);\n }\n\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n if (item.weight === 0) {\n this.dropRateCacheScaled.set(name, 0);\n return 0;\n }\n\n const totalPoolWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const baseRarityRateScaled = this.rarityRatesScaled[pool.rarity];\n const itemWeightScaled = this.toScaled(item.weight);\n const totalWeightScaled = this.toScaled(totalPoolWeight);\n const numeratorScaled = Math.round((itemWeightScaled * baseRarityRateScaled) / GachaEngine.SCALE);\n const rateScaled = Math.round((numeratorScaled * GachaEngine.SCALE) / totalWeightScaled);\n\n this.dropRateCacheScaled.set(name, rateScaled);\n return this.fromScaled(rateScaled);\n }\n }\n\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getCumulativeProbabilityForItem(name: string, rolls: number): number {\n const rate = this.getItemDropRate(name);\n if (rate === 0) return 0;\n if (rate >= 1) return 1;\n\n const cumulativeFailProbability = Math.pow(1 - rate, rolls);\n return 1 - cumulativeFailProbability;\n }\n\n getRollsForTargetProbability(name: string, targetProbability: number): number {\n if (targetProbability <= 0) return 0;\n if (targetProbability >= 1) return 1;\n\n const rate = this.getItemDropRate(name);\n if (rate <= 0) return Infinity;\n return Math.ceil(Math.log(1 - targetProbability) / Math.log(1 - rate));\n }\n\n getRateUpItems(): string[] {\n return this.pools.flatMap(p => p.items.filter(i => i.rateUp).map(i => i.name));\n }\n\n getAllItemDropRates(): { name: string; dropRate: number; rarity: string }[] {\n if (this.mode === 'flatRate') {\n return Array.from(this.flatRateMap.entries()).map(([name, dropRate]) => ({\n name,\n dropRate,\n rarity: 'flatRate',\n }));\n }\n\n return this.pools.flatMap(p =>\n p.items.map(i => ({\n name: i.name,\n dropRate: this.getItemDropRate(i.name),\n rarity: p.rarity,\n }))\n );\n }\n\n roll(count: number = 1): string[] {\n const results: string[] = [];\n for (let i = 0; i < count; i++) {\n if (this.mode === 'flatRate') {\n const rand = Math.random();\n let cumulative = 0;\n for (const [name, rate] of this.flatRateMap.entries()) {\n cumulative += rate;\n if (rand < cumulative) {\n results.push(name);\n break;\n }\n }\n } else {\n const rarity = this.selectRarity();\n const pool = this.pools.find(p => p.rarity === rarity)!;\n const item = this.selectItemFromPool(pool);\n results.push(item.name);\n }\n }\n return results;\n }\n\n private selectRarity(): string {\n const rand = Math.floor(Math.random() * GachaEngine.SCALE);\n let cumulative = 0;\n\n for (const [rarity, scaledRate] of Object.entries(this.rarityRatesScaled)) {\n cumulative += scaledRate;\n if (rand < cumulative) return rarity;\n }\n\n return Object.keys(this.rarityRatesScaled)[0];\n }\n\n private selectItemFromPool(pool: RarityInput): { name: string; weight: number } {\n const items = pool.items.filter(i => i.weight > 0);\n const scaledItems = items.map(i => ({\n ...i,\n scaledWeight: this.toScaled(i.weight),\n }));\n\n const totalScaledWeight = scaledItems.reduce((sum, i) => sum + i.scaledWeight, 0);\n const rand = Math.floor(Math.random() * totalScaledWeight);\n let cumulative = 0;\n\n for (const item of scaledItems) {\n cumulative += item.scaledWeight;\n if (rand < cumulative) {\n return { name: item.name, weight: item.weight };\n }\n }\n\n return items[0]; // Fallback\n }\n\n getDebugInfo(): {\n scale: number;\n rarityRatesScaled: Record<string, number>;\n rarityRatesFloat: Record<string, number>;\n } {\n const rarityRatesFloat: Record<string, number> = {};\n for (const [rarity, scaledRate] of Object.entries(this.rarityRatesScaled)) {\n rarityRatesFloat[rarity] = this.fromScaled(scaledRate);\n }\n\n return {\n scale: GachaEngine.SCALE,\n rarityRatesScaled: { ...this.rarityRatesScaled },\n rarityRatesFloat,\n };\n }\n}\n"],"names":["GachaEngine","config","this","mode","pools","rarityRatesScaled","flatRateMap","Map","dropRateCacheScaled","weightedConfig","scaleRarityRates","rarityRates","validateConfig","Error","_step","_iterator","_createForOfIteratorHelperLoose","done","_step2","_iterator2","value","items","item","weight","name","set","total","Array","from","values","reduce","sum","v","Math","abs","_proto","prototype","scaled","_i","_Object$entries","Object","entries","length","_Object$entries$_i","rarity","rate","toScaled","probability","MAX_SAFE_SCALE","SCALE","round","fromScaled","scaledInt","originalRates","configuredRarities","Set","keys","usedRarities","map","p","missing","filter","r","has","join","totalRate","_iterator3","_step3","pool","i","_step4","_iterator4","some","getItemDropRate","get","_iterator5","_step5","find","totalPoolWeight","baseRarityRateScaled","itemWeightScaled","totalWeightScaled","numeratorScaled","rateScaled","getCumulativeProbabilityForItem","rolls","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","rateUp","getAllItemDropRates","_this","_ref","dropRate","roll","count","_this2","results","_loop","_step6","rand","random","cumulative","_iterator6","_step6$value","push","selectRarity","selectItemFromPool","floor","_i2","_Object$entries2","_Object$entries2$_i","scaledRate","_step7","_this3","scaledItems","_extends","scaledWeight","totalScaledWeight","_iterator7","getDebugInfo","rarityRatesFloat","_i3","_Object$entries3","_Object$entries3$_i","scale","Number","MAX_SAFE_INTEGER"],"mappings":"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"}
package/dist/index.umd.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(t,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports):"function"==typeof define&&define.amd?define(["exports"],r):r((t||self).AllemandiGachaEngine={})}(this,function(t){function r(t,r){(null==r||r>t.length)&&(r=t.length);for(var e=0,n=Array(r);e<r;e++)n[e]=t[e];return n}function e(t,e){var n="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(n)return(n=n.call(t)).next.bind(n);if(Array.isArray(t)||(n=function(t,e){if(t){if("string"==typeof t)return r(t,e);var n={}.toString.call(t).slice(8,-1);return"Object"===n&&t.constructor&&(n=t.constructor.name),"Map"===n||"Set"===n?Array.from(t):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(t,e):void 0}}(t))||e&&t&&"number"==typeof t.length){n&&(t=n);var i=0;return function(){return i>=t.length?{done:!0}:{done:!1,value:t[i++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}t.GachaEngine=/*#__PURE__*/function(){function t(t){var r=t.rarityRates,e=t.pools;this.pools=void 0,this.rarityRates=void 0,this.dropRateCache=new Map,this.pools=e,this.rarityRates=r,this.validateConfig()}var r=t.prototype;return r.validateConfig=function(){var t=new Set(Object.keys(this.rarityRates)),r=new Set(this.pools.map(function(t){return t.rarity})),n=Array.from(r).filter(function(r){return!t.has(r)});if(n.length>0)throw new Error("Missing rarity rates for: "+n.join(", "));for(var i,o=e(this.pools);!(i=o()).done;){var a=i.value;if(0===a.items.length)throw new Error('Rarity "'+a.rarity+'" has no items');if(a.items.reduce(function(t,r){return t+r.weight},0)<=0)throw new Error('Rarity "'+a.rarity+'" has zero total weight')}},r.getItemDropRate=function(t){if(this.dropRateCache.has(t))return this.dropRateCache.get(t);for(var r,n=e(this.pools);!(r=n()).done;){var i=r.value,o=i.items.find(function(r){return r.name===t});if(o){var a=i.items.reduce(function(t,r){return t+r.weight},0),s=o.weight/a*this.rarityRates[i.rarity];return this.dropRateCache.set(t,s),s}}throw new Error('Item "'+t+'" not found')},r.getRarityProbability=function(t){if(!this.rarityRates[t])throw new Error('Rarity "'+t+'" not found');return this.rarityRates[t]},r.getCumulativeProbabilityForItem=function(t,r){var e=this.getItemDropRate(t);return 1-Math.pow(1-e,r)},r.getRollsForTargetProbability=function(t,r){var e=this.getItemDropRate(t);return e<=0?Infinity:Math.ceil(Math.log(1-r)/Math.log(1-e))},r.getRateUpItems=function(){return this.pools.flatMap(function(t){return t.items.filter(function(t){return t.rateUp}).map(function(t){return t.name})})},r.getAllItemDropRates=function(){var t=this;return this.pools.flatMap(function(r){return r.items.map(function(e){return{name:e.name,dropRate:t.getItemDropRate(e.name),rarity:r.rarity}})})},r.roll=function(t){var r=this;void 0===t&&(t=1);for(var e=[],n=function(){var t=r.selectRarity(),n=r.pools.find(function(r){return r.rarity===t}),i=r.selectItemFromPool(n);e.push(i.name)},i=0;i<t;i++)n();return e},r.selectRarity=function(){for(var t=Math.random(),r=0,e=0,n=Object.entries(this.rarityRates);e<n.length;e++){var i=n[e];if(t<=(r+=i[1]))return i[0]}return Object.keys(this.rarityRates)[0]},r.selectItemFromPool=function(t){for(var r,n=t.items.reduce(function(t,r){return t+r.weight},0),i=Math.random()*n,o=0,a=e(t.items);!(r=a()).done;){var s=r.value;if(i<=(o+=s.weight))return s}return t.items[0]},t}()});
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t||self).AllemandiGachaEngine={})}(this,function(t){function e(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,a=Array(e);r<e;r++)a[r]=t[r];return a}function r(t,r){var a="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(a)return(a=a.call(t)).next.bind(a);if(Array.isArray(t)||(a=function(t,r){if(t){if("string"==typeof t)return e(t,r);var a={}.toString.call(t).slice(8,-1);return"Object"===a&&t.constructor&&(a=t.constructor.name),"Map"===a||"Set"===a?Array.from(t):"Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?e(t,r):void 0}}(t))||r&&t&&"number"==typeof t.length){a&&(t=a);var n=0;return function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function a(){return a=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var a in r)({}).hasOwnProperty.call(r,a)&&(t[a]=r[a])}return t},a.apply(null,arguments)}var n,i=/*#__PURE__*/function(){function t(t){if(this.mode=void 0,this.pools=[],this.rarityRatesScaled={},this.flatRateMap=new Map,this.dropRateCacheScaled=new Map,this.mode=t.mode,"weighted"===t.mode){var e=t;this.pools=e.pools,this.rarityRatesScaled=this.scaleRarityRates(e.rarityRates),this.validateConfig(e.rarityRates)}else{if("flatRate"!==t.mode)throw new Error("Unknown gacha mode: "+this.mode);for(var a,n=r(t.pools);!(a=n()).done;)for(var i,o=r(a.value.items);!(i=o()).done;){var s=i.value;if(s.weight<0)throw new Error('FlatRate item "'+s.name+'" must have non-negative weight');this.flatRateMap.set(s.name,s.weight)}var f=Array.from(this.flatRateMap.values()).reduce(function(t,e){return t+e},0);if(Math.abs(f-1)>1e-6)throw new Error("FlatRate item rates must sum to 1.0, but got "+f)}}var e=t.prototype;return e.scaleRarityRates=function(t){for(var e={},r=0,a=Object.entries(t);r<a.length;r++){var n=a[r],i=n[0],o=n[1];if(o<0||o>1)throw new Error('Rarity rate for "'+i+'" must be between 0 and 1, got '+o);e[i]=this.toScaled(o)}return e},e.toScaled=function(e){if(e>t.MAX_SAFE_SCALE/t.SCALE)throw new Error("Probability "+e+" too large for safe integer arithmetic");return Math.round(e*t.SCALE)},e.fromScaled=function(e){return e/t.SCALE},e.validateConfig=function(t){var e=new Set(Object.keys(this.rarityRatesScaled)),a=new Set(this.pools.map(function(t){return t.rarity})),n=Array.from(a).filter(function(t){return!e.has(t)});if(n.length>0)throw new Error("Missing rarity rates for: "+n.join(", "));var i=Object.values(t).reduce(function(t,e){return t+e},0);if(Math.abs(i-1)>1e-10)throw new Error("Rarity rates must sum to 1.0, got "+i);for(var o,s=r(this.pools);!(o=s()).done;){var f=o.value;if(0===f.items.length)throw new Error('Rarity "'+f.rarity+'" has no items');if(f.items.reduce(function(t,e){return t+e.weight},0)<=0)throw new Error('Rarity "'+f.rarity+'" has zero total weight');for(var l,u=r(f.items);!(l=u()).done;){var h=l.value;if(h.weight<0)throw new Error('Item "'+h.name+'" weight must be non-negative, got '+h.weight)}if(!f.items.some(function(t){return t.weight>0}))throw new Error('Rarity "'+f.rarity+'" must have at least one item with positive weight')}},e.getItemDropRate=function(e){if("flatRate"===this.mode)return this.flatRateMap.get(e)||0;if(this.dropRateCacheScaled.has(e))return this.fromScaled(this.dropRateCacheScaled.get(e));for(var a,n=r(this.pools);!(a=n()).done;){var i=a.value,o=i.items.find(function(t){return t.name===e});if(o){if(0===o.weight)return this.dropRateCacheScaled.set(e,0),0;var s=i.items.reduce(function(t,e){return t+e.weight},0),f=this.rarityRatesScaled[i.rarity],l=this.toScaled(o.weight),u=this.toScaled(s),h=Math.round(l*f/t.SCALE),c=Math.round(h*t.SCALE/u);return this.dropRateCacheScaled.set(e,c),this.fromScaled(c)}}throw new Error('Item "'+e+'" not found')},e.getCumulativeProbabilityForItem=function(t,e){var r=this.getItemDropRate(t);return 0===r?0:r>=1?1:1-Math.pow(1-r,e)},e.getRollsForTargetProbability=function(t,e){if(e<=0)return 0;if(e>=1)return 1;var r=this.getItemDropRate(t);return r<=0?Infinity:Math.ceil(Math.log(1-e)/Math.log(1-r))},e.getRateUpItems=function(){return this.pools.flatMap(function(t){return t.items.filter(function(t){return t.rateUp}).map(function(t){return t.name})})},e.getAllItemDropRates=function(){var t=this;return"flatRate"===this.mode?Array.from(this.flatRateMap.entries()).map(function(t){return{name:t[0],dropRate:t[1],rarity:"flatRate"}}):this.pools.flatMap(function(e){return e.items.map(function(r){return{name:r.name,dropRate:t.getItemDropRate(r.name),rarity:e.rarity}})})},e.roll=function(t){var e=this;void 0===t&&(t=1);for(var a=[],n=function(){if("flatRate"===e.mode)for(var t,n=Math.random(),i=0,o=r(e.flatRateMap.entries());!(t=o()).done;){var s=t.value;if(n<(i+=s[1])){a.push(s[0]);break}}else{var f=e.selectRarity(),l=e.pools.find(function(t){return t.rarity===f}),u=e.selectItemFromPool(l);a.push(u.name)}},i=0;i<t;i++)n();return a},e.selectRarity=function(){for(var e=Math.floor(Math.random()*t.SCALE),r=0,a=0,n=Object.entries(this.rarityRatesScaled);a<n.length;a++){var i=n[a];if(e<(r+=i[1]))return i[0]}return Object.keys(this.rarityRatesScaled)[0]},e.selectItemFromPool=function(t){for(var e,n=this,i=t.items.filter(function(t){return t.weight>0}),o=i.map(function(t){return a({},t,{scaledWeight:n.toScaled(t.weight)})}),s=o.reduce(function(t,e){return t+e.scaledWeight},0),f=Math.floor(Math.random()*s),l=0,u=r(o);!(e=u()).done;){var h=e.value;if(f<(l+=h.scaledWeight))return{name:h.name,weight:h.weight}}return i[0]},e.getDebugInfo=function(){for(var e={},r=0,n=Object.entries(this.rarityRatesScaled);r<n.length;r++){var i=n[r];e[i[0]]=this.fromScaled(i[1])}return{scale:t.SCALE,rarityRatesScaled:a({},this.rarityRatesScaled),rarityRatesFloat:e}},t}();n=i,i.SCALE=1e6,i.MAX_SAFE_SCALE=Math.floor(Number.MAX_SAFE_INTEGER/n.SCALE),t.GachaEngine=i});
2
2
  //# sourceMappingURL=index.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/gacha-engine.ts"],"sourcesContent":["import { RarityInput, GachaEngineConfig } from './types';\n\nexport class GachaEngine {\n private pools: RarityInput[];\n private rarityRates: Record<string, number>;\n private dropRateCache = new Map<string, number>();\n\n constructor({ rarityRates, pools }: GachaEngineConfig) {\n this.pools = pools;\n this.rarityRates = rarityRates;\n this.validateConfig();\n }\n\n private validateConfig(): void {\n const configuredRarities = new Set(Object.keys(this.rarityRates));\n const usedRarities = new Set(this.pools.map(p => p.rarity));\n const missingArray = Array.from(usedRarities).filter(r => !configuredRarities.has(r));\n if (missingArray.length > 0) {\n throw new Error(`Missing rarity rates for: ${missingArray.join(', ')}`);\n }\n\n for (const pool of this.pools) {\n if (pool.items.length === 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has no items`);\n }\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n if (totalWeight <= 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has zero total weight`);\n }\n }\n }\n\n getItemDropRate(name: string): number {\n if (this.dropRateCache.has(name)) {\n return this.dropRateCache.get(name)!;\n }\n\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n const totalPoolWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const baseRarityRate = this.rarityRates[pool.rarity];\n const rate = (item.weight / totalPoolWeight) * baseRarityRate;\n this.dropRateCache.set(name, rate);\n return rate;\n }\n }\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getRarityProbability(rarity: string): number {\n if (!this.rarityRates[rarity]) {\n throw new Error(`Rarity \"${rarity}\" not found`);\n }\n return this.rarityRates[rarity];\n }\n\n getCumulativeProbabilityForItem(name: string, rolls: number): number {\n const rate = this.getItemDropRate(name);\n return 1 - Math.pow(1 - rate, rolls);\n }\n\n getRollsForTargetProbability(name: string, targetProbability: number): number {\n const rate = this.getItemDropRate(name);\n if (rate <= 0) return Infinity;\n return Math.ceil(Math.log(1 - targetProbability) / Math.log(1 - rate));\n }\n\n getRateUpItems(): string[] {\n return this.pools.flatMap(p =>\n p.items.filter(i => i.rateUp).map(i => i.name)\n );\n }\n\n getAllItemDropRates(): { name: string; dropRate: number; rarity: string }[] {\n return this.pools.flatMap(p =>\n p.items.map(i => ({\n name: i.name,\n dropRate: this.getItemDropRate(i.name),\n rarity: p.rarity\n }))\n );\n }\n\n roll(count: number = 1): string[] {\n const results: string[] = [];\n for (let i = 0; i < count; i++) {\n const rarity = this.selectRarity();\n const pool = this.pools.find(p => p.rarity === rarity)!;\n const item = this.selectItemFromPool(pool);\n results.push(item.name);\n }\n return results;\n }\n\n private selectRarity(): string {\n const rand = Math.random();\n let cumulative = 0;\n for (const [rarity, rate] of Object.entries(this.rarityRates)) {\n cumulative += rate;\n if (rand <= cumulative) return rarity;\n }\n return Object.keys(this.rarityRates)[0];\n }\n\n private selectItemFromPool(pool: RarityInput): { name: string; weight: number } {\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const rand = Math.random() * totalWeight;\n let cumulative = 0;\n for (const item of pool.items) {\n cumulative += item.weight;\n if (rand <= cumulative) return item;\n }\n return pool.items[0];\n }\n}"],"names":["GachaEngine","_ref","rarityRates","pools","this","dropRateCache","Map","validateConfig","_proto","prototype","configuredRarities","Set","Object","keys","usedRarities","map","p","rarity","missingArray","Array","from","filter","r","has","length","Error","join","_step","_iterator","_createForOfIteratorHelperLoose","done","pool","value","items","reduce","sum","i","weight","getItemDropRate","name","get","_step2","_iterator2","item","find","totalPoolWeight","rate","set","getRarityProbability","getCumulativeProbabilityForItem","rolls","Math","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","rateUp","getAllItemDropRates","_this","dropRate","roll","count","_this2","results","_loop","selectRarity","selectItemFromPool","push","rand","random","cumulative","_i","_Object$entries","entries","_Object$entries$_i","_step3","totalWeight","_iterator3"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.js","sources":["../src/gacha-engine.ts"],"sourcesContent":["import {\n RarityInput,\n GachaEngineConfig,\n WeightedGachaEngineConfig,\n FlatRateGachaEngineConfig,\n} from './types';\n\nexport class GachaEngine {\n private static readonly SCALE = 1_000_000;\n private static readonly MAX_SAFE_SCALE = Math.floor(Number.MAX_SAFE_INTEGER / GachaEngine.SCALE);\n\n private mode: 'weighted' | 'flatRate';\n private pools: RarityInput[] = [];\n private rarityRatesScaled: Record<string, number> = {};\n private flatRateMap: Map<string, number> = new Map();\n private dropRateCacheScaled = new Map<string, number>();\n\n constructor(config: GachaEngineConfig) {\n this.mode = config.mode;\n\n if (config.mode === 'weighted') {\n const weightedConfig = config as WeightedGachaEngineConfig;\n this.pools = weightedConfig.pools;\n this.rarityRatesScaled = this.scaleRarityRates(weightedConfig.rarityRates);\n this.validateConfig(weightedConfig.rarityRates);\n } else if (config.mode === 'flatRate') {\n const flatConfig = config as FlatRateGachaEngineConfig;\n for (const pool of flatConfig.pools) {\n for (const item of pool.items) {\n if (item.weight < 0) {\n throw new Error(`FlatRate item \"${item.name}\" must have non-negative weight`);\n }\n this.flatRateMap.set(item.name, item.weight); // Here, interpreted as direct probability\n }\n }\n const total = Array.from(this.flatRateMap.values()).reduce((sum, v) => sum + v, 0);\n if (Math.abs(total - 1.0) > 1e-6) {\n throw new Error(`FlatRate item rates must sum to 1.0, but got ${total}`);\n }\n } else {\n throw new Error(`Unknown gacha mode: ${this.mode}`);\n }\n }\n\n private scaleRarityRates(rarityRates: Record<string, number>): Record<string, number> {\n const scaled: Record<string, number> = {};\n for (const [rarity, rate] of Object.entries(rarityRates)) {\n if (rate < 0 || rate > 1) {\n throw new Error(`Rarity rate for \"${rarity}\" must be between 0 and 1, got ${rate}`);\n }\n scaled[rarity] = this.toScaled(rate);\n }\n return scaled;\n }\n\n private toScaled(probability: number): number {\n if (probability > GachaEngine.MAX_SAFE_SCALE / GachaEngine.SCALE) {\n throw new Error(`Probability ${probability} too large for safe integer arithmetic`);\n }\n return Math.round(probability * GachaEngine.SCALE);\n }\n\n private fromScaled(scaledInt: number): number {\n return scaledInt / GachaEngine.SCALE;\n }\n\n private validateConfig(originalRates: Record<string, number>): void {\n const configuredRarities = new Set(Object.keys(this.rarityRatesScaled));\n const usedRarities = new Set(this.pools.map(p => p.rarity));\n const missing = Array.from(usedRarities).filter(r => !configuredRarities.has(r));\n\n if (missing.length > 0) {\n throw new Error(`Missing rarity rates for: ${missing.join(', ')}`);\n }\n\n const totalRate = Object.values(originalRates).reduce((sum, rate) => sum + rate, 0);\n if (Math.abs(totalRate - 1.0) > 1e-10) {\n throw new Error(`Rarity rates must sum to 1.0, got ${totalRate}`);\n }\n\n for (const pool of this.pools) {\n if (pool.items.length === 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has no items`);\n }\n\n const totalWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n if (totalWeight <= 0) {\n throw new Error(`Rarity \"${pool.rarity}\" has zero total weight`);\n }\n\n for (const item of pool.items) {\n if (item.weight < 0) {\n throw new Error(`Item \"${item.name}\" weight must be non-negative, got ${item.weight}`);\n }\n }\n\n if (!pool.items.some(i => i.weight > 0)) {\n throw new Error(`Rarity \"${pool.rarity}\" must have at least one item with positive weight`);\n }\n }\n }\n\n getItemDropRate(name: string): number {\n if (this.mode === 'flatRate') {\n return this.flatRateMap.get(name) || 0;\n }\n\n if (this.dropRateCacheScaled.has(name)) {\n return this.fromScaled(this.dropRateCacheScaled.get(name)!);\n }\n\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n if (item.weight === 0) {\n this.dropRateCacheScaled.set(name, 0);\n return 0;\n }\n\n const totalPoolWeight = pool.items.reduce((sum, i) => sum + i.weight, 0);\n const baseRarityRateScaled = this.rarityRatesScaled[pool.rarity];\n const itemWeightScaled = this.toScaled(item.weight);\n const totalWeightScaled = this.toScaled(totalPoolWeight);\n const numeratorScaled = Math.round((itemWeightScaled * baseRarityRateScaled) / GachaEngine.SCALE);\n const rateScaled = Math.round((numeratorScaled * GachaEngine.SCALE) / totalWeightScaled);\n\n this.dropRateCacheScaled.set(name, rateScaled);\n return this.fromScaled(rateScaled);\n }\n }\n\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getCumulativeProbabilityForItem(name: string, rolls: number): number {\n const rate = this.getItemDropRate(name);\n if (rate === 0) return 0;\n if (rate >= 1) return 1;\n\n const cumulativeFailProbability = Math.pow(1 - rate, rolls);\n return 1 - cumulativeFailProbability;\n }\n\n getRollsForTargetProbability(name: string, targetProbability: number): number {\n if (targetProbability <= 0) return 0;\n if (targetProbability >= 1) return 1;\n\n const rate = this.getItemDropRate(name);\n if (rate <= 0) return Infinity;\n return Math.ceil(Math.log(1 - targetProbability) / Math.log(1 - rate));\n }\n\n getRateUpItems(): string[] {\n return this.pools.flatMap(p => p.items.filter(i => i.rateUp).map(i => i.name));\n }\n\n getAllItemDropRates(): { name: string; dropRate: number; rarity: string }[] {\n if (this.mode === 'flatRate') {\n return Array.from(this.flatRateMap.entries()).map(([name, dropRate]) => ({\n name,\n dropRate,\n rarity: 'flatRate',\n }));\n }\n\n return this.pools.flatMap(p =>\n p.items.map(i => ({\n name: i.name,\n dropRate: this.getItemDropRate(i.name),\n rarity: p.rarity,\n }))\n );\n }\n\n roll(count: number = 1): string[] {\n const results: string[] = [];\n for (let i = 0; i < count; i++) {\n if (this.mode === 'flatRate') {\n const rand = Math.random();\n let cumulative = 0;\n for (const [name, rate] of this.flatRateMap.entries()) {\n cumulative += rate;\n if (rand < cumulative) {\n results.push(name);\n break;\n }\n }\n } else {\n const rarity = this.selectRarity();\n const pool = this.pools.find(p => p.rarity === rarity)!;\n const item = this.selectItemFromPool(pool);\n results.push(item.name);\n }\n }\n return results;\n }\n\n private selectRarity(): string {\n const rand = Math.floor(Math.random() * GachaEngine.SCALE);\n let cumulative = 0;\n\n for (const [rarity, scaledRate] of Object.entries(this.rarityRatesScaled)) {\n cumulative += scaledRate;\n if (rand < cumulative) return rarity;\n }\n\n return Object.keys(this.rarityRatesScaled)[0];\n }\n\n private selectItemFromPool(pool: RarityInput): { name: string; weight: number } {\n const items = pool.items.filter(i => i.weight > 0);\n const scaledItems = items.map(i => ({\n ...i,\n scaledWeight: this.toScaled(i.weight),\n }));\n\n const totalScaledWeight = scaledItems.reduce((sum, i) => sum + i.scaledWeight, 0);\n const rand = Math.floor(Math.random() * totalScaledWeight);\n let cumulative = 0;\n\n for (const item of scaledItems) {\n cumulative += item.scaledWeight;\n if (rand < cumulative) {\n return { name: item.name, weight: item.weight };\n }\n }\n\n return items[0]; // Fallback\n }\n\n getDebugInfo(): {\n scale: number;\n rarityRatesScaled: Record<string, number>;\n rarityRatesFloat: Record<string, number>;\n } {\n const rarityRatesFloat: Record<string, number> = {};\n for (const [rarity, scaledRate] of Object.entries(this.rarityRatesScaled)) {\n rarityRatesFloat[rarity] = this.fromScaled(scaledRate);\n }\n\n return {\n scale: GachaEngine.SCALE,\n rarityRatesScaled: { ...this.rarityRatesScaled },\n rarityRatesFloat,\n };\n }\n}\n"],"names":["GachaEngine","config","this","mode","pools","rarityRatesScaled","flatRateMap","Map","dropRateCacheScaled","weightedConfig","scaleRarityRates","rarityRates","validateConfig","Error","_step","_iterator","_createForOfIteratorHelperLoose","done","_step2","_iterator2","value","items","item","weight","name","set","total","Array","from","values","reduce","sum","v","Math","abs","_proto","prototype","scaled","_i","_Object$entries","Object","entries","length","_Object$entries$_i","rarity","rate","toScaled","probability","MAX_SAFE_SCALE","SCALE","round","fromScaled","scaledInt","originalRates","configuredRarities","Set","keys","usedRarities","map","p","missing","filter","r","has","join","totalRate","_iterator3","_step3","pool","i","_step4","_iterator4","some","getItemDropRate","get","_iterator5","_step5","find","totalPoolWeight","baseRarityRateScaled","itemWeightScaled","totalWeightScaled","numeratorScaled","rateScaled","getCumulativeProbabilityForItem","rolls","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","rateUp","getAllItemDropRates","_this","_ref","dropRate","roll","count","_this2","results","_loop","_step6","rand","random","cumulative","_iterator6","_step6$value","push","selectRarity","selectItemFromPool","floor","_i2","_Object$entries2","_Object$entries2$_i","scaledRate","_step7","_this3","scaledItems","_extends","scaledWeight","totalScaledWeight","_iterator7","getDebugInfo","rarityRatesFloat","_i3","_Object$entries3","_Object$entries3$_i","scale","Number","MAX_SAFE_INTEGER"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,13 +1,21 @@
1
- export interface RarityInput {
2
- rarity: string;
3
- items: GachaItem[];
4
- }
5
1
  export interface GachaItem {
6
2
  name: string;
7
3
  weight: number;
8
4
  rateUp?: boolean;
9
5
  }
10
- export interface GachaEngineConfig {
6
+ export interface RarityInput {
7
+ rarity: string;
8
+ items: GachaItem[];
9
+ }
10
+ /** Weighted mode requires explicit rarityRates */
11
+ export interface WeightedGachaEngineConfig {
12
+ mode: 'weighted';
11
13
  rarityRates: Record<string, number>;
12
14
  pools: RarityInput[];
13
15
  }
16
+ /** Flat rate mode does NOT use rarityRates */
17
+ export interface FlatRateGachaEngineConfig {
18
+ mode: 'flatRate';
19
+ pools: RarityInput[];
20
+ }
21
+ export type GachaEngineConfig = WeightedGachaEngineConfig | FlatRateGachaEngineConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allemandi/gacha-engine",
3
- "version": "0.2.2",
3
+ "version": "0.3.3",
4
4
  "description": "Practical, type-safe toolkit for simulating and understanding gacha rates and rate-ups.",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.module.js",