@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 +110 -44
- package/dist/gacha-engine.d.ts +5 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.module.js +1 -1
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
## ✨ Features
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
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: '
|
|
47
|
-
{ name: '
|
|
48
|
-
{ name: '
|
|
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: '
|
|
55
|
-
{ name: '
|
|
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.
|
|
62
|
-
SR: 0.
|
|
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
|
-
|
|
68
|
-
|
|
80
|
+
// Perform actual rolls
|
|
81
|
+
const results = engine.roll(10);
|
|
82
|
+
console.log(`10 rolls result: ${results.join(', ')}`);
|
|
69
83
|
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
74
|
-
console.log(`
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
152
|
+
### Constructor
|
|
153
|
+
`new GachaEngine(config: GachaEngineConfig)`
|
|
107
154
|
|
|
108
|
-
|
|
155
|
+
Creates a new GachaEngine instance with validation.
|
|
109
156
|
|
|
110
|
-
|
|
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
|
|
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
|
|
180
|
+
- Returns the base probability for a rarity tier
|
|
118
181
|
|
|
119
182
|
`getCumulativeProbabilityForItem(name: string, rolls: number): number`
|
|
120
|
-
-
|
|
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
|
|
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
|
|
192
|
+
- Returns names of all items marked with `rateUp: true`
|
|
127
193
|
|
|
128
|
-
`getAllItemDropRates(): {
|
|
129
|
-
- Returns
|
|
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
|
|
202
|
+
# Run the test suite with Vitest
|
|
137
203
|
yarn test
|
|
138
204
|
# or
|
|
139
205
|
npm test
|
package/dist/gacha-engine.d.ts
CHANGED
|
@@ -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}
|
|
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
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
|
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
|
|
1
|
+
export * from './gacha-engine';
|
|
2
|
+
export * from './types';
|
package/dist/index.module.js
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}
|
|
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
|
package/dist/index.module.js.map
CHANGED
|
@@ -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
|
|
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}
|
|
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
|
package/dist/index.umd.js.map
CHANGED
|
@@ -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
|
|
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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"}
|
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
|
-
|
|
7
|
+
weight: number;
|
|
8
8
|
rateUp?: boolean;
|
|
9
9
|
}
|
|
10
10
|
export interface GachaEngineConfig {
|
|
11
|
-
rarityRates
|
|
11
|
+
rarityRates: Record<string, number>;
|
|
12
12
|
pools: RarityInput[];
|
|
13
13
|
}
|
package/package.json
CHANGED