@allemandi/gacha-engine 0.1.1 → 0.2.2

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
@@ -19,9 +19,11 @@
19
19
 
20
20
 
21
21
  ## ✨ Features
22
- - 🔍 Determine how many rolls are needed to reach a target probability for an item
23
- - 📐 Estimate cumulative probabilities over multiple rolls
24
- - Lightweight and fast rarity probabilities
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
25
27
 
26
28
  ## 🛠️ Installation
27
29
  ```bash
@@ -43,97 +45,161 @@ const pools = [
43
45
  {
44
46
  rarity: 'SSR',
45
47
  items: [
46
- { name: 'Ultra Sword', probability: 0.01, rateUp: true },
47
- { name: 'Magic Wand', probability: 0.02 },
48
- { name: 'Useless SSR Loot', probability: 0.97 }
48
+ { name: 'Super Hobo', weight: 0.8, rateUp: true },
49
+ { name: 'Broke King', weight: 0.4 },
50
+ { name: 'Cardboard Hero', weight: 0.4 },
51
+ { name: 'Newspaper Warmer', weight: 0.4 }
49
52
  ]
50
53
  },
51
54
  {
52
- rarity: 'SR',
55
+ rarity: 'SR',
53
56
  items: [
54
- { name: 'Steel Shield', probability: 0.1 },
55
- { name: 'Healing Potion', probability: 0.2 }
57
+ { name: 'Cold Salaryman', weight: 1.5, rateUp: true },
58
+ { name: 'Numb Artist', weight: 1.8 },
59
+ { name: 'Crying Cook', weight: 1.8 },
60
+ { name: 'Lonely Cat', weight: 1.8 }
61
+ ]
62
+ },
63
+ {
64
+ rarity: 'R',
65
+ items: [
66
+ { name: 'Regular Joe', weight: 5.0 },
67
+ { name: 'Normal Person', weight: 5.0 }
56
68
  ]
57
69
  }
58
70
  ];
59
71
 
60
72
  const rarityRates = {
61
- SSR: 0.05,
62
- SR: 0.3,
73
+ SSR: 0.01, // 1% chance for SSR
74
+ SR: 0.05, // 5% chance for SR
75
+ R: 0.94 // 94% chance for R
63
76
  };
64
77
 
65
78
  const engine = new GachaEngine({ pools, rarityRates });
66
79
 
67
- const dropRate = engine.getItemDropRate('Ultra Sword');
68
- console.log(`Drop rate for Ultra Sword: ${dropRate}`);
80
+ // Perform actual rolls
81
+ const results = engine.roll(10);
82
+ console.log(`10 rolls result: ${results.join(', ')}`);
69
83
 
70
- const cumulativeProb = engine.getCumulativeProbabilityForItem('Ultra Sword', 10);
71
- console.log(`Probability of getting Ultra Sword in 10 rolls: ${cumulativeProb}`);
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)
72
88
 
73
- const rollsNeeded = engine.getRollsForTargetProbability('Ultra Sword', 0.9);
74
- console.log(`Rolls needed for 90% chance: ${rollsNeeded}`);
89
+ const cumulativeProb = engine.getCumulativeProbabilityForItem('Super Hobo', 300);
90
+ console.log(`Probability of getting Super Hobo in 300 rolls: ${(cumulativeProb * 100).toFixed(1)}%`);
91
+
92
+ const rollsNeeded = engine.getRollsForTargetProbability('Super Hobo', 0.5);
93
+ console.log(`Rolls needed for 50% chance: ${rollsNeeded}`);
75
94
 
76
95
  const rateUpItems = engine.getRateUpItems();
77
96
  console.log(`Current rate-up items: ${rateUpItems.join(', ')}`);
97
+ // Output: "Super Hobo, Cold Salaryman"
78
98
  ```
79
99
 
80
100
  **CommonJS**
81
101
  ```js
82
102
  const { GachaEngine } = require('@allemandi/gacha-engine');
103
+
104
+ // Same configuration as above
105
+ const engine = new GachaEngine({ pools, rarityRates });
106
+ console.log('Single roll:', engine.roll());
83
107
  ```
84
108
 
85
- **UMD**
109
+ **UMD (Browser)**
86
110
  ```html
87
- <script src="https://unpkg.com/@allemandi/gacha-engine"></script>
88
- <script>
89
- const engine = new window.AllemandiGachaEngine.GachaEngine({
90
- pools: [
91
- {
92
- rarity: '5★',
93
- items: [{ name: 'Rate Up Character', probability: 0.008 }]
94
- }
95
- ]
96
- });
97
-
98
- console.log('Rate up:', engine.getItemDropRate('Rate Up Character'));
99
- // Rate up: 0.008
100
- </script>
111
+ <script src="https://unpkg.com/@allemandi/gacha-engine"></script>
112
+ <script>
113
+ // Access the GachaEngine class
114
+ const { GachaEngine } = window.AllemandiGachaEngine;
115
+
116
+ const engine = new GachaEngine({
117
+ rarityRates: {
118
+ SSR: 0.01,
119
+ SR: 0.05,
120
+ R: 0.94
121
+ },
122
+ pools: [
123
+ {
124
+ rarity: 'SSR',
125
+ items: [
126
+ { name: 'Park Master', weight: 0.7, rateUp: true },
127
+ { name: 'Trash Titan', weight: 0.3 }
128
+ ]
129
+ },
130
+ {
131
+ rarity: 'SR',
132
+ items: [
133
+ { name: 'Street Sweeper', weight: 1.0 }
134
+ ]
135
+ },
136
+ {
137
+ rarity: 'R',
138
+ items: [
139
+ { name: 'Regular Person', weight: 1.0 }
140
+ ]
141
+ }
142
+ ]
143
+ });
144
+
145
+ console.log('Single roll:', engine.roll());
146
+ console.log('Drop rate for Park Master:', engine.getItemDropRate('Park Master'));
147
+ </script>
101
148
  ```
102
149
 
103
150
  ## 📘 API
104
- `new GachaEngine(config: GachaEngineConfig)`
105
151
 
106
- Creates a new GachaEngine instance.
152
+ ### Constructor
153
+ `new GachaEngine(config: GachaEngineConfig)`
107
154
 
108
- - `config.pools`: Array of item pools, each with a rarity and list of items (each with name, probability, optional `rateUp` flag).
155
+ Creates a new GachaEngine instance with validation.
109
156
 
110
- - `config.rarityRates`: Optional object mapping rarities to base probabilities.
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
111
165
 
112
166
  ### Methods
167
+
168
+ #### Rolling
169
+ `roll(count?: number): string[]`
170
+ - Simulate gacha rolls and returns item names
171
+ - `count`: Number of rolls to perform (default: 1)
172
+ - Returns array of item names
173
+
174
+ #### Analysis
113
175
  `getItemDropRate(name: string): number`
114
- - Returns the effective drop rate of an item by name.
176
+ - Returns the effective drop rate (0-1) for a specific item
177
+ - Calculated as: `(item.weight / pool.totalWeight) × rarity.baseRate`
115
178
 
116
179
  `getRarityProbability(rarity: string): number`
117
- - Returns the base probability assigned to a rarity pool.
180
+ - Returns the base probability for a rarity tier
118
181
 
119
182
  `getCumulativeProbabilityForItem(name: string, rolls: number): number`
120
- - Returns the probability of obtaining the specified item at least once within the given number of rolls.
183
+ - Calculates probability of getting the item at least once in N rolls
184
+ - Uses formula: `1 - (1 - dropRate)^rolls`
121
185
 
122
186
  `getRollsForTargetProbability(name: string, targetProbability: number): number`
123
- - Returns the minimum number of rolls needed to reach or exceed the target probability for the specified item.
187
+ - Returns minimum rolls needed to reach target probability for an item
188
+ - Returns `Infinity` if item has zero drop rate
124
189
 
190
+ #### Utility
125
191
  `getRateUpItems(): string[]`
126
- - Returns a list of all item names currently marked as rate-up.
192
+ - Returns names of all items marked with `rateUp: true`
127
193
 
128
- `getAllItemDropRates(): { name: string; dropRate: number; rarity: string }[]`
129
- - Returns an array of all items with their calculated drop rates and rarities.
194
+ `getAllItemDropRates(): Array<{name: string, dropRate: number, rarity: string}>`
195
+ - Returns complete list of all items with their calculated drop rates and rarities
130
196
 
131
197
  ## 🧪 Tests
132
198
 
133
199
  > Available in the GitHub repo only.
134
200
 
135
201
  ```bash
136
- # Run the test suite with Jest
202
+ # Run the test suite with Vitest
137
203
  yarn test
138
204
  # or
139
205
  npm test
@@ -2,7 +2,9 @@ import { GachaEngineConfig } from './types';
2
2
  export declare class GachaEngine {
3
3
  private pools;
4
4
  private rarityRates;
5
+ private dropRateCache;
5
6
  constructor({ rarityRates, pools }: GachaEngineConfig);
7
+ private validateConfig;
6
8
  getItemDropRate(name: string): number;
7
9
  getRarityProbability(rarity: string): number;
8
10
  getCumulativeProbabilityForItem(name: string, rolls: number): number;
@@ -13,4 +15,7 @@ export declare class GachaEngine {
13
15
  dropRate: number;
14
16
  rarity: string;
15
17
  }[];
18
+ roll(count?: number): string[];
19
+ private selectRarity;
20
+ private selectItemFromPool;
16
21
  }
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}exports.GachaEngine=/*#__PURE__*/function(){function r(t){var r=t.rarityRates,e=void 0===r?{}:r,n=t.pools;this.pools=void 0,this.rarityRates=void 0,this.pools=n,this.rarityRates=e}var e=r.prototype;return e.getItemDropRate=function(r){for(var e,n=function(r){var e="undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(e)return(e=e.call(r)).next.bind(e);if(Array.isArray(r)||(e=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=e);var n=0;return function(){return n>=r.length?{done:!0}:{done:!1,value:r[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.")}(this.pools);!(e=n()).done;){var o=e.value,i=o.items.find(function(t){return t.name===r});if(i){var a,u=o.items.reduce(function(t,r){return t+r.probability},0),l=null!=(a=this.rarityRates[o.rarity])?a:u;return i.probability/u*l}}throw new Error('Item "'+r+'" not found')},e.getRarityProbability=function(t){var r,e=this.pools.find(function(r){return r.rarity===t});if(!e)throw new Error('Rarity "'+t+'" not found');var n=e.items.reduce(function(t,r){return t+r.probability},0);return null!=(r=this.rarityRates[t])?r:n},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}})})},r}();
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}();
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';\nexport class GachaEngine {\n private pools: RarityInput[];\n private rarityRates: Record<string, number>;\n\n constructor({ rarityRates = {}, pools }: GachaEngineConfig) {\n this.pools = pools;\n this.rarityRates = rarityRates;\n }\n\n getItemDropRate(name: string): number {\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n const totalPoolProb = pool.items.reduce((sum, i) => sum + i.probability, 0);\n const baseRarityRate = this.rarityRates[pool.rarity] ?? totalPoolProb;\n return (item.probability / totalPoolProb) * baseRarityRate;\n }\n }\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getRarityProbability(rarity: string): number {\n const pool = this.pools.find(p => p.rarity === rarity);\n if (!pool) throw new Error(`Rarity \"${rarity}\" not found`);\n\n const totalProb = pool.items.reduce((sum, i) => sum + i.probability, 0);\n const baseRate = this.rarityRates[rarity] ?? totalProb;\n return baseRate;\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"],"names":["GachaEngine","_ref","_ref$rarityRates","rarityRates","pools","this","_proto","prototype","getItemDropRate","name","_step","_iterator","_createForOfIteratorHelperLoose","done","pool","value","item","items","find","i","_this$rarityRates$poo","totalPoolProb","reduce","sum","probability","baseRarityRate","rarity","Error","getRarityProbability","_this$rarityRates$rar","p","totalProb","getCumulativeProbabilityForItem","rolls","rate","Math","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","filter","rateUp","map","getAllItemDropRates","_this","dropRate"],"mappings":"uIACwB,WAIpB,SAAAA,EAAAC,GAA0DC,IAAAA,EAAAD,EAA5CE,YAAAA,OAAW,IAAAD,EAAG,CAAE,EAAAA,EAAEE,EAAKH,EAALG,WAHxBA,WAAK,EAAAC,KACLF,iBAAW,EAGfE,KAAKD,MAAQA,EACbC,KAAKF,YAAcA,CACvB,CAAC,IAAAG,EAAAN,EAAAO,UAgDA,OAhDAD,EAEDE,gBAAA,SAAgBC,GACZ,IAAA,IAA6BC,EAA7BC,2pBAAAC,CAAmBP,KAAKD,SAAKM,EAAAC,KAAAE,MAAE,CAAA,IAApBC,EAAIJ,EAAAK,MACLC,EAAOF,EAAKG,MAAMC,KAAK,SAAAC,GAAK,OAAAA,EAAEV,OAASA,CAAI,GACjD,GAAIO,EAAM,CAAA,IAAAI,EACAC,EAAgBP,EAAKG,MAAMK,OAAO,SAACC,EAAKJ,GAAC,OAAKI,EAAMJ,EAAEK,WAAW,EAAE,GACnEC,EAA8CL,OAAhCA,EAAGf,KAAKF,YAAYW,EAAKY,SAAON,EAAIC,EACxD,OAAQL,EAAKQ,YAAcH,EAAiBI,CAChD,CACJ,CACA,MAAU,IAAAE,MAAK,SAAUlB,EAAI,cACjC,EAACH,EAEDsB,qBAAA,SAAqBF,GAAc,IAAAG,EACzBf,EAAOT,KAAKD,MAAMc,KAAK,SAAAY,GAAC,OAAIA,EAAEJ,SAAWA,CAAM,GACrD,IAAKZ,EAAM,MAAU,IAAAa,MAAK,WAAYD,EAAM,eAE5C,IAAMK,EAAYjB,EAAKG,MAAMK,OAAO,SAACC,EAAKJ,GAAM,OAAAI,EAAMJ,EAAEK,WAAW,EAAE,GAErE,OADyCK,OAA3BA,EAAGxB,KAAKF,YAAYuB,IAAOG,EAAIE,CAEjD,EAACzB,EAED0B,gCAAA,SAAgCvB,EAAcwB,GAC1C,IAAMC,EAAO7B,KAAKG,gBAAgBC,GAClC,OAAQ,EAAG0B,KAAKC,IAAI,EAAIF,EAAMD,EAClC,EAAC3B,EAED+B,6BAAA,SAA6B5B,EAAc6B,GACvC,IAAMJ,EAAO7B,KAAKG,gBAAgBC,GAClC,OAAIyB,GAAQ,EAAUK,SACfJ,KAAKK,KAAKL,KAAKM,IAAI,EAAIH,GAAqBH,KAAKM,IAAI,EAAIP,GACpE,EAAC5B,EAEDoC,eAAA,WACI,OAAWrC,KAACD,MAAMuC,QAAQ,SAAAb,GACtB,OAAAA,EAAEb,MAAM2B,OAAO,SAAAzB,GAAC,OAAIA,EAAE0B,MAAM,GAAEC,IAAI,SAAA3B,GAAC,OAAIA,EAAEV,IAAI,EAAC,EAEtD,EAACH,EAEDyC,oBAAA,WAAmB,IAAAC,EAAA3C,KACf,OAAOA,KAAKD,MAAMuC,QAAQ,SAAAb,GACtB,OAAAA,EAAEb,MAAM6B,IAAI,SAAA3B,GAAM,MAAA,CACdV,KAAMU,EAAEV,KACRwC,SAAUD,EAAKxC,gBAAgBW,EAAEV,MACjCiB,OAAQI,EAAEJ,OACb,EAAE,EAEX,EAAC1B,CAAA,CAvDmB"}
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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"}
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export { GachaEngine } from "./gacha-engine";
1
+ export * from './gacha-engine';
2
+ export * from './types';
@@ -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}var r=/*#__PURE__*/function(){function r(t){var r=t.rarityRates,e=void 0===r?{}:r,n=t.pools;this.pools=void 0,this.rarityRates=void 0,this.pools=n,this.rarityRates=e}var e=r.prototype;return e.getItemDropRate=function(r){for(var e,n=function(r){var e="undefined"!=typeof Symbol&&r[Symbol.iterator]||r["@@iterator"];if(e)return(e=e.call(r)).next.bind(e);if(Array.isArray(r)||(e=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=e);var n=0;return function(){return n>=r.length?{done:!0}:{done:!1,value:r[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.")}(this.pools);!(e=n()).done;){var o=e.value,i=o.items.find(function(t){return t.name===r});if(i){var a,u=o.items.reduce(function(t,r){return t+r.probability},0),l=null!=(a=this.rarityRates[o.rarity])?a:u;return i.probability/u*l}}throw new Error('Item "'+r+'" not found')},e.getRarityProbability=function(t){var r,e=this.pools.find(function(r){return r.rarity===t});if(!e)throw new Error('Rarity "'+t+'" not found');var n=e.items.reduce(function(t,r){return t+r.probability},0);return null!=(r=this.rarityRates[t])?r:n},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}})})},r}();export{r as GachaEngine};
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};
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';\nexport class GachaEngine {\n private pools: RarityInput[];\n private rarityRates: Record<string, number>;\n\n constructor({ rarityRates = {}, pools }: GachaEngineConfig) {\n this.pools = pools;\n this.rarityRates = rarityRates;\n }\n\n getItemDropRate(name: string): number {\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n const totalPoolProb = pool.items.reduce((sum, i) => sum + i.probability, 0);\n const baseRarityRate = this.rarityRates[pool.rarity] ?? totalPoolProb;\n return (item.probability / totalPoolProb) * baseRarityRate;\n }\n }\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getRarityProbability(rarity: string): number {\n const pool = this.pools.find(p => p.rarity === rarity);\n if (!pool) throw new Error(`Rarity \"${rarity}\" not found`);\n\n const totalProb = pool.items.reduce((sum, i) => sum + i.probability, 0);\n const baseRate = this.rarityRates[rarity] ?? totalProb;\n return baseRate;\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"],"names":["GachaEngine","_ref","_ref$rarityRates","rarityRates","pools","this","_proto","prototype","getItemDropRate","name","_step","_iterator","_createForOfIteratorHelperLoose","done","pool","value","item","items","find","i","_this$rarityRates$poo","totalPoolProb","reduce","sum","probability","baseRarityRate","rarity","Error","getRarityProbability","_this$rarityRates$rar","p","totalProb","getCumulativeProbabilityForItem","rolls","rate","Math","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","filter","rateUp","map","getAllItemDropRates","_this","dropRate"],"mappings":"sGACa,IAAAA,eAAW,WAIpB,SAAAA,EAAAC,GAA0DC,IAAAA,EAAAD,EAA5CE,YAAAA,OAAW,IAAAD,EAAG,CAAE,EAAAA,EAAEE,EAAKH,EAALG,WAHxBA,WAAK,EAAAC,KACLF,iBAAW,EAGfE,KAAKD,MAAQA,EACbC,KAAKF,YAAcA,CACvB,CAAC,IAAAG,EAAAN,EAAAO,UAgDA,OAhDAD,EAEDE,gBAAA,SAAgBC,GACZ,IAAA,IAA6BC,EAA7BC,2pBAAAC,CAAmBP,KAAKD,SAAKM,EAAAC,KAAAE,MAAE,CAAA,IAApBC,EAAIJ,EAAAK,MACLC,EAAOF,EAAKG,MAAMC,KAAK,SAAAC,GAAK,OAAAA,EAAEV,OAASA,CAAI,GACjD,GAAIO,EAAM,CAAA,IAAAI,EACAC,EAAgBP,EAAKG,MAAMK,OAAO,SAACC,EAAKJ,GAAC,OAAKI,EAAMJ,EAAEK,WAAW,EAAE,GACnEC,EAA8CL,OAAhCA,EAAGf,KAAKF,YAAYW,EAAKY,SAAON,EAAIC,EACxD,OAAQL,EAAKQ,YAAcH,EAAiBI,CAChD,CACJ,CACA,MAAU,IAAAE,MAAK,SAAUlB,EAAI,cACjC,EAACH,EAEDsB,qBAAA,SAAqBF,GAAc,IAAAG,EACzBf,EAAOT,KAAKD,MAAMc,KAAK,SAAAY,GAAC,OAAIA,EAAEJ,SAAWA,CAAM,GACrD,IAAKZ,EAAM,MAAU,IAAAa,MAAK,WAAYD,EAAM,eAE5C,IAAMK,EAAYjB,EAAKG,MAAMK,OAAO,SAACC,EAAKJ,GAAM,OAAAI,EAAMJ,EAAEK,WAAW,EAAE,GAErE,OADyCK,OAA3BA,EAAGxB,KAAKF,YAAYuB,IAAOG,EAAIE,CAEjD,EAACzB,EAED0B,gCAAA,SAAgCvB,EAAcwB,GAC1C,IAAMC,EAAO7B,KAAKG,gBAAgBC,GAClC,OAAQ,EAAG0B,KAAKC,IAAI,EAAIF,EAAMD,EAClC,EAAC3B,EAED+B,6BAAA,SAA6B5B,EAAc6B,GACvC,IAAMJ,EAAO7B,KAAKG,gBAAgBC,GAClC,OAAIyB,GAAQ,EAAUK,SACfJ,KAAKK,KAAKL,KAAKM,IAAI,EAAIH,GAAqBH,KAAKM,IAAI,EAAIP,GACpE,EAAC5B,EAEDoC,eAAA,WACI,OAAWrC,KAACD,MAAMuC,QAAQ,SAAAb,GACtB,OAAAA,EAAEb,MAAM2B,OAAO,SAAAzB,GAAC,OAAIA,EAAE0B,MAAM,GAAEC,IAAI,SAAA3B,GAAC,OAAIA,EAAEV,IAAI,EAAC,EAEtD,EAACH,EAEDyC,oBAAA,WAAmB,IAAAC,EAAA3C,KACf,OAAOA,KAAKD,MAAMuC,QAAQ,SAAAb,GACtB,OAAAA,EAAEb,MAAM6B,IAAI,SAAA3B,GAAM,MAAA,CACdV,KAAMU,EAAEV,KACRwC,SAAUD,EAAKxC,gBAAgBW,EAAEV,MACjCiB,OAAQI,EAAEJ,OACb,EAAE,EAEX,EAAC1B,CAAA,CAvDmB"}
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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"}
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}t.GachaEngine=/*#__PURE__*/function(){function t(t){var r=t.rarityRates,e=void 0===r?{}:r,n=t.pools;this.pools=void 0,this.rarityRates=void 0,this.pools=n,this.rarityRates=e}var e=t.prototype;return e.getItemDropRate=function(t){for(var e,n=function(t){var e="undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(e)return(e=e.call(t)).next.bind(e);if(Array.isArray(t)||(e=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=e);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.")}(this.pools);!(e=n()).done;){var o=e.value,i=o.items.find(function(r){return r.name===t});if(i){var a,u=o.items.reduce(function(t,r){return t+r.probability},0),l=null!=(a=this.rarityRates[o.rarity])?a:u;return i.probability/u*l}}throw new Error('Item "'+t+'" not found')},e.getRarityProbability=function(t){var r,e=this.pools.find(function(r){return r.rarity===t});if(!e)throw new Error('Rarity "'+t+'" not found');var n=e.items.reduce(function(t,r){return t+r.probability},0);return null!=(r=this.rarityRates[t])?r:n},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}})})},t}()});
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}()});
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';\nexport class GachaEngine {\n private pools: RarityInput[];\n private rarityRates: Record<string, number>;\n\n constructor({ rarityRates = {}, pools }: GachaEngineConfig) {\n this.pools = pools;\n this.rarityRates = rarityRates;\n }\n\n getItemDropRate(name: string): number {\n for (const pool of this.pools) {\n const item = pool.items.find(i => i.name === name);\n if (item) {\n const totalPoolProb = pool.items.reduce((sum, i) => sum + i.probability, 0);\n const baseRarityRate = this.rarityRates[pool.rarity] ?? totalPoolProb;\n return (item.probability / totalPoolProb) * baseRarityRate;\n }\n }\n throw new Error(`Item \"${name}\" not found`);\n }\n\n getRarityProbability(rarity: string): number {\n const pool = this.pools.find(p => p.rarity === rarity);\n if (!pool) throw new Error(`Rarity \"${rarity}\" not found`);\n\n const totalProb = pool.items.reduce((sum, i) => sum + i.probability, 0);\n const baseRate = this.rarityRates[rarity] ?? totalProb;\n return baseRate;\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"],"names":["GachaEngine","_ref","_ref$rarityRates","rarityRates","pools","this","_proto","prototype","getItemDropRate","name","_step","_iterator","_createForOfIteratorHelperLoose","done","pool","value","item","items","find","i","_this$rarityRates$poo","totalPoolProb","reduce","sum","probability","baseRarityRate","rarity","Error","getRarityProbability","_this$rarityRates$rar","p","totalProb","getCumulativeProbabilityForItem","rolls","rate","Math","pow","getRollsForTargetProbability","targetProbability","Infinity","ceil","log","getRateUpItems","flatMap","filter","rateUp","map","getAllItemDropRates","_this","dropRate"],"mappings":"+WACwB,WAIpB,SAAAA,EAAAC,GAA0DC,IAAAA,EAAAD,EAA5CE,YAAAA,OAAW,IAAAD,EAAG,CAAE,EAAAA,EAAEE,EAAKH,EAALG,WAHxBA,WAAK,EAAAC,KACLF,iBAAW,EAGfE,KAAKD,MAAQA,EACbC,KAAKF,YAAcA,CACvB,CAAC,IAAAG,EAAAN,EAAAO,UAgDA,OAhDAD,EAEDE,gBAAA,SAAgBC,GACZ,IAAA,IAA6BC,EAA7BC,2pBAAAC,CAAmBP,KAAKD,SAAKM,EAAAC,KAAAE,MAAE,CAAA,IAApBC,EAAIJ,EAAAK,MACLC,EAAOF,EAAKG,MAAMC,KAAK,SAAAC,GAAK,OAAAA,EAAEV,OAASA,CAAI,GACjD,GAAIO,EAAM,CAAA,IAAAI,EACAC,EAAgBP,EAAKG,MAAMK,OAAO,SAACC,EAAKJ,GAAC,OAAKI,EAAMJ,EAAEK,WAAW,EAAE,GACnEC,EAA8CL,OAAhCA,EAAGf,KAAKF,YAAYW,EAAKY,SAAON,EAAIC,EACxD,OAAQL,EAAKQ,YAAcH,EAAiBI,CAChD,CACJ,CACA,MAAU,IAAAE,MAAK,SAAUlB,EAAI,cACjC,EAACH,EAEDsB,qBAAA,SAAqBF,GAAc,IAAAG,EACzBf,EAAOT,KAAKD,MAAMc,KAAK,SAAAY,GAAC,OAAIA,EAAEJ,SAAWA,CAAM,GACrD,IAAKZ,EAAM,MAAU,IAAAa,MAAK,WAAYD,EAAM,eAE5C,IAAMK,EAAYjB,EAAKG,MAAMK,OAAO,SAACC,EAAKJ,GAAM,OAAAI,EAAMJ,EAAEK,WAAW,EAAE,GAErE,OADyCK,OAA3BA,EAAGxB,KAAKF,YAAYuB,IAAOG,EAAIE,CAEjD,EAACzB,EAED0B,gCAAA,SAAgCvB,EAAcwB,GAC1C,IAAMC,EAAO7B,KAAKG,gBAAgBC,GAClC,OAAQ,EAAG0B,KAAKC,IAAI,EAAIF,EAAMD,EAClC,EAAC3B,EAED+B,6BAAA,SAA6B5B,EAAc6B,GACvC,IAAMJ,EAAO7B,KAAKG,gBAAgBC,GAClC,OAAIyB,GAAQ,EAAUK,SACfJ,KAAKK,KAAKL,KAAKM,IAAI,EAAIH,GAAqBH,KAAKM,IAAI,EAAIP,GACpE,EAAC5B,EAEDoC,eAAA,WACI,OAAWrC,KAACD,MAAMuC,QAAQ,SAAAb,GACtB,OAAAA,EAAEb,MAAM2B,OAAO,SAAAzB,GAAC,OAAIA,EAAE0B,MAAM,GAAEC,IAAI,SAAA3B,GAAC,OAAIA,EAAEV,IAAI,EAAC,EAEtD,EAACH,EAEDyC,oBAAA,WAAmB,IAAAC,EAAA3C,KACf,OAAOA,KAAKD,MAAMuC,QAAQ,SAAAb,GACtB,OAAAA,EAAEb,MAAM6B,IAAI,SAAA3B,GAAM,MAAA,CACdV,KAAMU,EAAEV,KACRwC,SAAUD,EAAKxC,gBAAgBW,EAAEV,MACjCiB,OAAQI,EAAEJ,OACb,EAAE,EAEX,EAAC1B,CAAA,CAvDmB"}
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":"6iCAEwB,WAKpB,SAAAA,EAAAC,GAAqD,IAAvCC,EAAWD,EAAXC,YAAaC,EAAKF,EAALE,MAAKC,KAJxBD,WAAK,EAAAC,KACLF,iBAAW,EAAAE,KACXC,cAAgB,IAAIC,IAGxBF,KAAKD,MAAQA,EACbC,KAAKF,YAAcA,EACnBE,KAAKG,gBACT,CAAC,IAAAC,EAAAR,EAAAS,UAuGA,OAvGAD,EAEOD,eAAA,WACJ,IAAMG,EAAqB,IAAIC,IAAIC,OAAOC,KAAKT,KAAKF,cAC9CY,EAAe,IAAIH,IAAIP,KAAKD,MAAMY,IAAI,SAAAC,GAAK,OAAAA,EAAEC,MAAM,IACnDC,EAAeC,MAAMC,KAAKN,GAAcO,OAAO,SAAAC,GAAC,OAAKZ,EAAmBa,IAAID,EAAE,GACpF,GAAIJ,EAAaM,OAAS,EACtB,MAAU,IAAAC,MAAK,6BAA8BP,EAAaQ,KAAK,OAGnE,IAAA,IAA6BC,EAA7BC,EAAAC,EAAmBzB,KAAKD,SAAKwB,EAAAC,KAAAE,MAAE,CAAA,IAApBC,EAAIJ,EAAAK,MACX,GAA0B,IAAtBD,EAAKE,MAAMT,OACX,UAAUC,MAAiBM,WAAAA,EAAKd,OAAsB,kBAG1D,GADoBc,EAAKE,MAAMC,OAAO,SAACC,EAAKC,GAAM,OAAAD,EAAMC,EAAEC,MAAM,EAAE,IAC/C,EACf,MAAM,IAAIZ,iBAAiBM,EAAKd,OAAM,0BAE9C,CACJ,EAACT,EAED8B,gBAAA,SAAgBC,GACZ,GAAInC,KAAKC,cAAckB,IAAIgB,GACvB,OAAWnC,KAACC,cAAcmC,IAAID,GAGlC,IAAA,IAA6BE,EAA7BC,EAAAb,EAAmBzB,KAAKD,SAAKsC,EAAAC,KAAAZ,MAAE,CAAA,IAApBC,EAAIU,EAAAT,MACLW,EAAOZ,EAAKE,MAAMW,KAAK,SAAAR,GAAK,OAAAA,EAAEG,OAASA,CAAI,GACjD,GAAII,EAAM,CACN,IAAME,EAAkBd,EAAKE,MAAMC,OAAO,SAACC,EAAKC,GAAC,OAAKD,EAAMC,EAAEC,MAAM,EAAE,GAEhES,EAAQH,EAAKN,OAASQ,EADLzC,KAAKF,YAAY6B,EAAKd,QAG7C,OADAb,KAAKC,cAAc0C,IAAIR,EAAMO,GACtBA,CACX,CACJ,CACA,MAAU,IAAArB,MAAK,SAAUc,EAAiB,cAC9C,EAAC/B,EAEDwC,qBAAA,SAAqB/B,GACjB,IAAKb,KAAKF,YAAYe,GAClB,MAAM,IAAIQ,iBAAiBR,EAAM,eAErC,OAAOb,KAAKF,YAAYe,EAC5B,EAACT,EAEDyC,gCAAA,SAAgCV,EAAcW,GAC1C,IAAMJ,EAAO1C,KAAKkC,gBAAgBC,GAClC,OAAO,EAAIY,KAAKC,IAAI,EAAIN,EAAMI,EAClC,EAAC1C,EAED6C,6BAAA,SAA6Bd,EAAce,GACvC,IAAMR,EAAO1C,KAAKkC,gBAAgBC,GAClC,OAAIO,GAAQ,EAAUS,SACfJ,KAAKK,KAAKL,KAAKM,IAAI,EAAIH,GAAqBH,KAAKM,IAAI,EAAIX,GACpE,EAACtC,EAEDkD,eAAA,WACI,OAAOtD,KAAKD,MAAMwD,QAAQ,SAAA3C,GAAC,OACvBA,EAAEiB,MAAMZ,OAAO,SAAAe,GAAK,OAAAA,EAAEwB,MAAM,GAAE7C,IAAI,SAAAqB,GAAK,OAAAA,EAAEG,IAAI,EAAC,EAEtD,EAAC/B,EAEDqD,oBAAA,WAAmBC,IAAAA,EACf1D,KAAA,YAAYD,MAAMwD,QAAQ,SAAA3C,GACtB,OAAAA,EAAEiB,MAAMlB,IAAI,SAAAqB,GAAM,MAAA,CACdG,KAAMH,EAAEG,KACRwB,SAAUD,EAAKxB,gBAAgBF,EAAEG,MACjCtB,OAAQD,EAAEC,OACb,EAAE,EAEX,EAACT,EAEDwD,KAAA,SAAKC,GAAiBC,IAAAA,gBAAjBD,IAAAA,EAAgB,GAEjB,IADA,IAAME,EAAoB,GAAGC,EAAA,WAEzB,IAAMnD,EAASiD,EAAKG,eACdtC,EAAOmC,EAAK/D,MAAMyC,KAAK,SAAA5B,GAAC,OAAIA,EAAEC,SAAWA,CAAM,GAC/C0B,EAAOuB,EAAKI,mBAAmBvC,GACrCoC,EAAQI,KAAK5B,EAAKJ,KACtB,EALSH,EAAI,EAAGA,EAAI6B,EAAO7B,IAAGgC,IAM9B,OAAOD,CACX,EAAC3D,EAEO6D,aAAA,WAGJ,IAFA,IAAMG,EAAOrB,KAAKsB,SACdC,EAAa,EACjBC,EAAAC,EAAAA,EAA6BhE,OAAOiE,QAAQzE,KAAKF,aAAYyE,EAAAC,EAAApD,OAAAmD,IAAE,CAA1D,IAAAG,EAAAF,EAAAD,GAED,GAAIH,IADJE,GADoBI,EAAA,IAEI,OAFVA,EAAA,EAGlB,CACA,OAAOlE,OAAOC,KAAKT,KAAKF,aAAa,EACzC,EAACM,EAEO8D,mBAAA,SAAmBvC,GAIvB,IAHA,IAG6BgD,EAHvBC,EAAcjD,EAAKE,MAAMC,OAAO,SAACC,EAAKC,GAAC,OAAKD,EAAMC,EAAEC,MAAM,EAAE,GAC5DmC,EAAOrB,KAAKsB,SAAWO,EACzBN,EAAa,EACjBO,EAAApD,EAAmBE,EAAKE,SAAK8C,EAAAE,KAAAnD,MAAE,CAAA,IAApBa,EAAIoC,EAAA/C,MAEX,GAAIwC,IADJE,GAAc/B,EAAKN,QACK,OAAOM,CACnC,CACA,OAAOZ,EAAKE,MAAM,EACtB,EAACjC,CAAA,CAhHmB"}
package/dist/types.d.ts CHANGED
@@ -4,10 +4,10 @@ export interface RarityInput {
4
4
  }
5
5
  export interface GachaItem {
6
6
  name: string;
7
- probability: number;
7
+ weight: number;
8
8
  rateUp?: boolean;
9
9
  }
10
10
  export interface GachaEngineConfig {
11
- rarityRates?: Record<string, number>;
11
+ rarityRates: Record<string, number>;
12
12
  pools: RarityInput[];
13
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allemandi/gacha-engine",
3
- "version": "0.1.1",
3
+ "version": "0.2.2",
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",