@allemandi/gacha-engine 0.1.1 → 0.2.1
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 +78 -43
- 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,38 +45,48 @@ 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 }, // Rate-up: 0.8% of SSR pool
|
|
49
|
+
{ name: 'Broke King', weight: 0.4 }, // Standard: 0.4% of SSR pool
|
|
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 }, // Rate-up: 1.5% of SR pool
|
|
58
|
+
{ name: 'Numb Artist', weight: 1.8 }, // Standard 4-star rates
|
|
59
|
+
{ name: 'Crying Cook', weight: 1.8 },
|
|
60
|
+
{ name: 'Lonely Cat', weight: 1.8 }
|
|
56
61
|
]
|
|
57
62
|
}
|
|
58
63
|
];
|
|
59
64
|
|
|
60
65
|
const rarityRates = {
|
|
61
|
-
SSR: 0.
|
|
62
|
-
SR: 0.3
|
|
66
|
+
SSR: 0.01, // 1% chance for SSR (5-star)
|
|
67
|
+
SR: 0.03, // 3% chance for SR (4-star)
|
|
63
68
|
};
|
|
64
69
|
|
|
65
70
|
const engine = new GachaEngine({ pools, rarityRates });
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
// Perform actual rolls
|
|
73
|
+
const results = engine.roll(10);
|
|
74
|
+
console.log(`10 rolls result: ${results.join(', ')}`);
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
// Analyze probabilities
|
|
77
|
+
const dropRate = engine.getItemDropRate('Super Hobo');
|
|
78
|
+
console.log(`Drop rate for Super Hobo: ${(dropRate * 100).toFixed(3)}%`);
|
|
79
|
+
// Output: ~0.4% (0.8/2.0 * 0.01)
|
|
72
80
|
|
|
73
|
-
const
|
|
74
|
-
console.log(`
|
|
81
|
+
const cumulativeProb = engine.getCumulativeProbabilityForItem('Super Hobo', 300);
|
|
82
|
+
console.log(`Probability of getting Super Hobo in 300 rolls: ${(cumulativeProb * 100).toFixed(1)}%`);
|
|
83
|
+
|
|
84
|
+
const rollsNeeded = engine.getRollsForTargetProbability('Super Hobo', 0.5);
|
|
85
|
+
console.log(`Rolls needed for 50% chance: ${rollsNeeded}`);
|
|
75
86
|
|
|
76
87
|
const rateUpItems = engine.getRateUpItems();
|
|
77
88
|
console.log(`Current rate-up items: ${rateUpItems.join(', ')}`);
|
|
89
|
+
// Output: "Super Hobo, Cold Salaryman"
|
|
78
90
|
```
|
|
79
91
|
|
|
80
92
|
**CommonJS**
|
|
@@ -84,56 +96,79 @@ const { GachaEngine } = require('@allemandi/gacha-engine');
|
|
|
84
96
|
|
|
85
97
|
**UMD**
|
|
86
98
|
```html
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
<script src="https://unpkg.com/@allemandi/gacha-engine"></script>
|
|
100
|
+
<script>
|
|
101
|
+
const engine = new AllemandiGachaEngine.GachaEngine({
|
|
102
|
+
rarityRates: { 'SSR': 0.01 },
|
|
103
|
+
pools: [
|
|
104
|
+
{
|
|
105
|
+
rarity: 'SSR',
|
|
106
|
+
items: [
|
|
107
|
+
{ name: 'Park Master', weight: 0.7, rateUp: true },
|
|
108
|
+
{ name: 'Trash Titan', weight: 0.3 }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
console.log('Single roll:', engine.roll());
|
|
115
|
+
// Single roll: ['Park Master'] or ['Trash Titan']
|
|
116
|
+
</script>
|
|
101
117
|
```
|
|
102
118
|
|
|
103
119
|
## 📘 API
|
|
104
|
-
`new GachaEngine(config: GachaEngineConfig)`
|
|
105
120
|
|
|
106
|
-
|
|
121
|
+
### Constructor
|
|
122
|
+
`new GachaEngine(config: GachaEngineConfig)`
|
|
107
123
|
|
|
108
|
-
|
|
124
|
+
Creates a new GachaEngine instance with validation.
|
|
109
125
|
|
|
110
|
-
|
|
126
|
+
**Config Properties:**
|
|
127
|
+
- `rarityRates` **(required)**: Object mapping rarity names to their base probabilities (must sum to ≤ 1.0)
|
|
128
|
+
- `pools` **(required)**: Array of rarity pools, each containing:
|
|
129
|
+
- `rarity`: String identifier matching a key in `rarityRates`
|
|
130
|
+
- `items`: Array of items with:
|
|
131
|
+
- `name`: Unique item identifier
|
|
132
|
+
- `weight`: Relative weight within the rarity pool (higher = more likely)
|
|
133
|
+
- `rateUp?`: Optional boolean flag for rate-up items
|
|
111
134
|
|
|
112
135
|
### Methods
|
|
136
|
+
|
|
137
|
+
#### Rolling
|
|
138
|
+
`roll(count?: number): string[]`
|
|
139
|
+
- Simulate gacha rolls and returns item names
|
|
140
|
+
- `count`: Number of rolls to perform (default: 1)
|
|
141
|
+
- Returns array of item names
|
|
142
|
+
|
|
143
|
+
#### Analysis
|
|
113
144
|
`getItemDropRate(name: string): number`
|
|
114
|
-
- Returns the effective drop rate
|
|
145
|
+
- Returns the effective drop rate (0-1) for a specific item
|
|
146
|
+
- Calculated as: `(item.weight / pool.totalWeight) × rarity.baseRate`
|
|
115
147
|
|
|
116
148
|
`getRarityProbability(rarity: string): number`
|
|
117
|
-
- Returns the base probability
|
|
149
|
+
- Returns the base probability for a rarity tier
|
|
118
150
|
|
|
119
151
|
`getCumulativeProbabilityForItem(name: string, rolls: number): number`
|
|
120
|
-
-
|
|
152
|
+
- Calculates probability of getting the item at least once in N rolls
|
|
153
|
+
- Uses formula: `1 - (1 - dropRate)^rolls`
|
|
121
154
|
|
|
122
155
|
`getRollsForTargetProbability(name: string, targetProbability: number): number`
|
|
123
|
-
- Returns
|
|
156
|
+
- Returns minimum rolls needed to reach target probability for an item
|
|
157
|
+
- Returns `Infinity` if item has zero drop rate
|
|
124
158
|
|
|
159
|
+
#### Utility
|
|
125
160
|
`getRateUpItems(): string[]`
|
|
126
|
-
- Returns
|
|
161
|
+
- Returns names of all items marked with `rateUp: true`
|
|
127
162
|
|
|
128
|
-
`getAllItemDropRates(): {
|
|
129
|
-
- Returns
|
|
163
|
+
`getAllItemDropRates(): Array<{name: string, dropRate: number, rarity: string}>`
|
|
164
|
+
- Returns complete list of all items with their calculated drop rates and rarities
|
|
130
165
|
|
|
131
166
|
## 🧪 Tests
|
|
132
167
|
|
|
133
168
|
> Available in the GitHub repo only.
|
|
134
169
|
|
|
135
170
|
```bash
|
|
136
|
-
# Run the test suite with
|
|
171
|
+
# Run the test suite with Vitest
|
|
137
172
|
yarn test
|
|
138
173
|
# or
|
|
139
174
|
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=[].concat(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 missing = [...usedRarities].filter(r => !configuredRarities.has(r));\n if (missing.length > 0) {\n throw new Error(`Missing rarity rates for: ${missing.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","missing","concat","filter","r","has","length","Error","join","_iterator","_step","_createForOfIteratorHelperLoose","done","pool","value","items","reduce","sum","i","weight","getItemDropRate","name","get","_iterator2","_step2","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=[].concat(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 missing = [...usedRarities].filter(r => !configuredRarities.has(r));\n if (missing.length > 0) {\n throw new Error(`Missing rarity rates for: ${missing.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","missing","concat","filter","r","has","length","Error","join","_iterator","_step","_createForOfIteratorHelperLoose","done","pool","value","items","reduce","sum","i","weight","getItemDropRate","name","get","_iterator2","_step2","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=[].concat(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 missing = [...usedRarities].filter(r => !configuredRarities.has(r));\n if (missing.length > 0) {\n throw new Error(`Missing rarity rates for: ${missing.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","missing","concat","filter","r","has","length","Error","join","_iterator","_step","_createForOfIteratorHelperLoose","done","pool","value","items","reduce","sum","i","weight","getItemDropRate","name","get","_iterator2","_step2","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