Practical, type-safe toolkit for simulating and understanding gacha rates and rate-ups.
Supports"weighted"and"flatRate"modes for different gacha strategies.
Works in Node.js and browsers β supports ESM, CommonJS, and UMD.
- β¨ Features
- π οΈ Installation
- π Quick Usage Examples
- π API
- π§ͺ Tests
- π Related Projects
- π€ Contributing
- π² Roll simulation β Perform gacha rolls with weighted or flat-rate logic
- π Probability analysis β Drop rates, cumulative probabilities, target probabilities
- π Multi-rarity support β Flexible rarity-based or item-based probability distributions
- β‘ Performance optimized β Efficient with cached calculations
- π‘οΈ Type-safe β Written in TypeScript with strict configuration validation
# Yarn
yarn add @allemandi/gacha-engine
# NPM
npm install @allemandi/gacha-engineESM (Weighted Mode)
import { GachaEngine } from '@allemandi/gacha-engine';
const pools = [
{
rarity: 'SSR',
items: [
{ name: 'Super Hobo', weight: 0.8, rateUp: true },
{ name: 'Broke King', weight: 0.4 },
{ name: 'Cardboard Hero', weight: 0.4 }
]
},
{
rarity: 'SR',
items: [
{ name: 'Cold Salaryman', weight: 1.5, rateUp: true },
{ name: 'Numb Artist', weight: 1.8 },
{ name: 'Crying Cook', weight: 1.8 }
]
},
{
rarity: 'R',
items: [
{ name: 'Regular Joe', weight: 5.0 },
{ name: 'Normal Person', weight: 5.0 }
]
}
];
const rarityRates = {
SSR: 0.01,
SR: 0.05,
R: 0.94
};
const engine = new GachaEngine({ mode: 'weighted', pools, rarityRates });
console.log('10 rolls:', engine.roll(10).join(', '));
const rate = engine.getItemDropRate('Super Hobo');
console.log('Drop rate for Super Hobo:', (rate * 100) + '%');
// ~0.4% β (0.8 / 1.6) * 0.01 = 0.005 β 0.5%
const cumulative = engine.getCumulativeProbabilityForItem('Super Hobo', 300);
console.log('Probability in 300 rolls:', (cumulative * 100) + '%');
// ~77.7%
console.log('Rolls for 50% chance:', engine.getRollsForTargetProbability('Super Hobo', 0.5));
// 139
console.log('Rate-up items:', engine.getRateUpItems().join(', '));
// Super Hobo, Cold SalarymanCommonJS (Flat Rate Mode)
const { GachaEngine } = require('@allemandi/gacha-engine');
const pools = [
{
rarity: 'SSR',
items: [
{ name: 'Superior Rat', weight: 0.003, rateUp: true },
{ name: 'Dumpster King', weight: 0.002 }
]
},
{
rarity: 'SR',
items: [
{ name: 'Sleepy Chef', weight: 0.015 }
]
},
{
rarity: 'R',
items: [
{ name: 'Unknown Student', weight: 0.1 }
]
}
];
const engine = new GachaEngine({ mode: 'flatRate', pools });
console.log('Roll x5:', engine.roll(5).join(', '));
const dropRate = engine.getItemDropRate('Superior Rat');
console.log('Drop rate for Superior Rat:', (dropRate * 100) + '%');
// 0.3%
const cumulative = engine.getCumulativeProbabilityForItem('Superior Rat', 500);
console.log('Chance after 500 rolls:', (cumulative * 100).toFixed(1) + '%');
// ~78.5%
const rollsFor50 = engine.getRollsForTargetProbability('Superior Rat', 0.5);
console.log('Rolls for 50% chance:', rollsFor50);
// ~231
console.log('Rate-up items:', engine.getRateUpItems().join(', '));
// Superior RatUMD (Browser, Weighted Mode)
<script src="https://unpkg.com/@allemandi/gacha-engine"></script>
<script>
const { GachaEngine } = AllemandiGachaEngine;
const engine = new GachaEngine({
mode: 'weighted',
rarityRates: {
SSR: 0.02,
SR: 0.08,
R: 0.90
},
pools: [
{
rarity: 'SSR',
items: [
{ name: 'Trash Wizard', weight: 1.0 },
{ name: 'Park Master', weight: 1.0, rateUp: true }
]
},
{
rarity: 'SR',
items: [
{ name: 'Street Sweeper', weight: 2.0 },
{ name: 'Bench Philosopher', weight: 1.0 }
]
},
{
rarity: 'R',
items: [
{ name: 'Bus Stop Ghost', weight: 5.0 }
]
}
]
});
const rate = engine.getItemDropRate('Park Master');
const rolls = engine.getRollsForTargetProbability('Park Master', 0.75);
const cumulative = engine.getCumulativeProbabilityForItem('Park Master', 200);
console.log('1x Roll:', engine.roll());
console.log('Drop rate for Park Master:', (rate * 100).toFixed(2) + '%');
// 1.0 / 2.0 * 0.02 = 0.01 β 1.00%
console.log('Cumulative 200 rolls:', (cumulative * 100).toFixed(1) + '%');
// ~86.6%
console.log('Rolls for 75% chance:', rolls);
// ~138
console.log('Rate-up items:', engine.getRateUpItems().join(', '));
// Park Master
console.log('All items:', engine.getAllItemDropRates().map(i => i.name));
// ["Trash Wizard", "Park Master", "Street Sweeper", "Bench Philosopher", "Bus Stop Ghost"]
</script>new GachaEngine(config: GachaEngineConfig)
Creates a new GachaEngine instance with validation.
Config Options:
- Weighted Mode
{
mode: 'weighted'; // (default)
rarityRates: Record<string, number>; // Required: must sum to 1.0
pools: Array<{
rarity: string; // Must match a key in `rarityRates`
items: Array<{
name: string;
weight: number;
rateUp?: boolean;
}>
}>
}- Flat Rate Mode
{
mode: 'flatRate';
pools: Array<{
rarity: string; // Used only for categorization
items: Array<{
name: string;
weight: number; // Interpreted as direct probability (must sum to 1.0 across all items)
rateUp?: boolean;
}>
}>
}roll(count?: number): string[]
- Simulate gacha rolls and returns item names
count: Number of rolls to perform (default: 1)- Returns array of item names
getItemDropRate(name: string): number
- Returns the effective drop rate for a specific item
- In weighted mode:
- Computed as
dropRate = (item.weight / totalPoolWeight) Γ rarityBaseRate
- Computed as
- In flat rate mode:
- `Returns the item's defined probability.
- Throws if the item does not exist.
- In weighted mode:
getRarityProbability(rarity: string): number
- Returns the base probability for a given rarity tier
- Only in "weighted" mode.
- Throws in flatRate mode.
getCumulativeProbabilityForItem(name: string, rolls: number): number
- Calculates probability of getting the item at least once in N rolls
- Uses formula:
1 - (1 - dropRate)^rolls
getRollsForTargetProbability(name: string, targetProbability: number): number
- Calculates the minimum number of rolls needed to reach a specific probability of pulling a given item.
- Returns
Infinityif item has zero drop rate - Returns 1 if target probability β₯ 1.0
getRateUpItems(): string[]
- Returns names of all items marked with
rateUp: true
getAllItemDropRates(): Array<{name: string, dropRate: number, rarity: string}>
- Returns a list of all items with:
- name: Item name
- dropRate: Calculated drop probability
- rarity: Associated rarity (or "flatRate" in flat mode)
Available in the GitHub repo only.
# Run the test suite with Vitest
yarn test
# or
npm testCheck out these related projects that might interest you:
- Fast, type-safe utilities for vector embedding comparison and search.
- Node.js CLI tool for local text classification using word embeddings.
If you have ideas, improvements, or new features:
- Fork the project
- Create your feature branch (git checkout -b feature/amazing-feature)
- Commit your changes (git commit -m 'Add some amazing feature')
- Push to the branch (git push origin feature/amazing-feature)
- Open a Pull Request