{"id":46782056,"url":"https://github.com/allemandi/gacha-engine","last_synced_at":"2026-03-10T00:33:12.524Z","repository":{"id":301026787,"uuid":"1007365780","full_name":"allemandi/gacha-engine","owner":"allemandi","description":"Practical, type-safe toolkit for simulating and understanding gacha rates and rate-ups.","archived":false,"fork":false,"pushed_at":"2025-12-04T22:01:11.000Z","size":269,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-08T03:57:08.547Z","etag":null,"topics":["drop-rate","gacha","gacha-engine","gacha-simulator","game-probability","gaming","lootbox","probability","random-drop","rate-up","rolls","simulation","type-safe","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@allemandi/gacha-engine","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/allemandi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-23T22:09:32.000Z","updated_at":"2025-12-04T22:00:13.000Z","dependencies_parsed_at":"2025-06-24T20:15:21.914Z","dependency_job_id":"51d545ae-bee5-44dc-a4a7-7a1b8f192809","html_url":"https://github.com/allemandi/gacha-engine","commit_stats":null,"previous_names":["allemandi/gacha-engine"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/allemandi/gacha-engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allemandi%2Fgacha-engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allemandi%2Fgacha-engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allemandi%2Fgacha-engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allemandi%2Fgacha-engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/allemandi","download_url":"https://codeload.github.com/allemandi/gacha-engine/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/allemandi%2Fgacha-engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30318458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T20:05:46.299Z","status":"ssl_error","status_checked_at":"2026-03-09T19:57:04.425Z","response_time":61,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["drop-rate","gacha","gacha-engine","gacha-simulator","game-probability","gaming","lootbox","probability","random-drop","rate-up","rolls","simulation","type-safe","typescript"],"created_at":"2026-03-10T00:33:11.974Z","updated_at":"2026-03-10T00:33:12.513Z","avatar_url":"https://github.com/allemandi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 📖 @allemandi/gacha-engine\n\n[![NPM Version](https://img.shields.io/npm/v/@allemandi/gacha-engine)](https://www.npmjs.com/package/@allemandi/gacha-engine)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/allemandi/gacha-engine/blob/main/LICENSE)\n\n\u003e **Practical, type-safe toolkit for simulating and understanding gacha rates and rate-ups.**  \n\u003e Supports `\"weighted\"` and `\"flatRate\"` modes for different gacha strategies.  \n\u003e Works in Node.js and browsers – supports ESM, CommonJS, and UMD.\n\n---\n\n\u003c!-- omit from toc --\u003e\n## 🔖 Table of Contents\n\n- [✨ Features](#-features)\n- [🛠️ Installation](#️-installation)\n- [🚀 Quick Usage Examples](#-quick-usage-examples)\n- [📘 API](#-api)\n- [🧪 Tests](#-tests)\n- [🔗 Related Projects](#-related-projects)\n- [🤝 Contributing](#-contributing)\n\n---\n\n## ✨ Features\n\n- 🎲 **Roll simulation** – Perform gacha rolls with weighted or flat-rate logic  \n- 🔍 **Probability analysis** – Drop rates, cumulative probabilities, target probabilities  \n- 📐 **Multi-rarity support** – Flexible rarity-based or item-based probability distributions  \n- ⚡ **Performance optimized** – Efficient with cached calculations  \n- 🛡️ **Type-safe** – Written in TypeScript with strict configuration validation\n\n---\n\n## 🛠️ Installation\n```bash\n# Yarn\nyarn add @allemandi/gacha-engine\n\n# NPM\nnpm install @allemandi/gacha-engine\n```\n\n\n## 🚀 Quick Usage Examples\n\n**ESM (Weighted Mode)**\n```js\nimport { GachaEngine } from '@allemandi/gacha-engine';\n\nconst pools = [\n  {\n    rarity: 'SSR',\n    items: [\n      { name: 'Super Hobo', weight: 0.8, rateUp: true },\n      { name: 'Broke King', weight: 0.4 },\n      { name: 'Cardboard Hero', weight: 0.4 }\n    ]\n  },\n  {\n    rarity: 'SR',\n    items: [\n      { name: 'Cold Salaryman', weight: 1.5, rateUp: true },\n      { name: 'Numb Artist', weight: 1.8 },\n      { name: 'Crying Cook', weight: 1.8 }\n    ]\n  },\n  {\n    rarity: 'R',\n    items: [\n      { name: 'Regular Joe', weight: 5.0 },\n      { name: 'Normal Person', weight: 5.0 }\n    ]\n  }\n];\n\nconst rarityRates = {\n  SSR: 0.01,\n  SR: 0.05,\n  R: 0.94\n};\n\nconst engine = new GachaEngine({ mode: 'weighted', pools, rarityRates });\n\nconsole.log('10 rolls:', engine.roll(10).join(', '));\n\nconst rate = engine.getItemDropRate('Super Hobo');\nconsole.log('Drop rate for Super Hobo:', (rate * 100) + '%');\n// ~0.4% → (0.8 / 1.6) * 0.01 = 0.005 → 0.5%\n\nconst cumulative = engine.getCumulativeProbabilityForItem('Super Hobo', 300);\nconsole.log('Probability in 300 rolls:', (cumulative * 100) + '%');\n// ~77.7%\n\nconsole.log('Rolls for 50% chance:', engine.getRollsForTargetProbability('Super Hobo', 0.5));\n// 139\n\nconsole.log('Rate-up items:', engine.getRateUpItems().join(', '));\n// Super Hobo, Cold Salaryman\n```\n\n**CommonJS (Flat Rate Mode)**\n```js\nconst { GachaEngine } = require('@allemandi/gacha-engine');\n\nconst pools = [\n  {\n    rarity: 'SSR',\n    items: [\n      { name: 'Superior Rat', weight: 0.003, rateUp: true },\n      { name: 'Dumpster King', weight: 0.002 }\n    ]\n  },\n  {\n    rarity: 'SR',\n    items: [\n      { name: 'Sleepy Chef', weight: 0.015 }\n    ]\n  },\n  {\n    rarity: 'R',\n    items: [\n      { name: 'Unknown Student', weight: 0.1 }\n    ]\n  }\n];\n\nconst engine = new GachaEngine({ mode: 'flatRate', pools });\n\nconsole.log('Roll x5:', engine.roll(5).join(', '));\n\nconst dropRate = engine.getItemDropRate('Superior Rat');\nconsole.log('Drop rate for Superior Rat:', (dropRate * 100) + '%');\n// 0.3%\n\nconst cumulative = engine.getCumulativeProbabilityForItem('Superior Rat', 500);\nconsole.log('Chance after 500 rolls:', (cumulative * 100).toFixed(1) + '%');\n// ~78.5%\n\nconst rollsFor50 = engine.getRollsForTargetProbability('Superior Rat', 0.5);\nconsole.log('Rolls for 50% chance:', rollsFor50);\n// ~231\n\nconsole.log('Rate-up items:', engine.getRateUpItems().join(', '));\n// Superior Rat\n```\n\n**UMD (Browser, Weighted Mode)**\n```html\n\u003cscript src=\"https://unpkg.com/@allemandi/gacha-engine\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const { GachaEngine } = AllemandiGachaEngine;\n\n  const engine = new GachaEngine({\n    mode: 'weighted',\n    rarityRates: {\n      SSR: 0.02,\n      SR: 0.08,\n      R: 0.90\n    },\n    pools: [\n      {\n        rarity: 'SSR',\n        items: [\n          { name: 'Trash Wizard', weight: 1.0 },\n          { name: 'Park Master', weight: 1.0, rateUp: true }\n        ]\n      },\n      {\n        rarity: 'SR',\n        items: [\n          { name: 'Street Sweeper', weight: 2.0 },\n          { name: 'Bench Philosopher', weight: 1.0 }\n        ]\n      },\n      {\n        rarity: 'R',\n        items: [\n          { name: 'Bus Stop Ghost', weight: 5.0 }\n        ]\n      }\n    ]\n  });\n\n  const rate = engine.getItemDropRate('Park Master');\n  const rolls = engine.getRollsForTargetProbability('Park Master', 0.75);\n  const cumulative = engine.getCumulativeProbabilityForItem('Park Master', 200);\n\n  console.log('1x Roll:', engine.roll());\n  console.log('Drop rate for Park Master:', (rate * 100).toFixed(2) + '%');\n  // 1.0 / 2.0 * 0.02 = 0.01 → 1.00%\n\n  console.log('Cumulative 200 rolls:', (cumulative * 100).toFixed(1) + '%');\n  // ~86.6%\n\n  console.log('Rolls for 75% chance:', rolls);\n  // ~138\n\n  console.log('Rate-up items:', engine.getRateUpItems().join(', '));\n  // Park Master\n\n  console.log('All items:', engine.getAllItemDropRates().map(i =\u003e i.name));\n  // [\"Trash Wizard\", \"Park Master\", \"Street Sweeper\", \"Bench Philosopher\", \"Bus Stop Ghost\"]\n\u003c/script\u003e\n```\n\n## 📘 API\n\n### Constructor\n`new GachaEngine(config: GachaEngineConfig)`\n\nCreates a new GachaEngine instance with validation.\n\n**Config Options:**\n\n- Weighted Mode \n```ts\n{\n  mode: 'weighted'; // (default)\n  rarityRates: Record\u003cstring, number\u003e; // Required: must sum to 1.0\n  pools: Array\u003c{\n    rarity: string; // Must match a key in `rarityRates`\n    items: Array\u003c{\n      name: string;\n      weight: number;\n      rateUp?: boolean;\n    }\u003e\n  }\u003e\n}\n```\n- Flat Rate Mode\n\n```ts\n{\n  mode: 'flatRate';\n  pools: Array\u003c{\n    rarity: string; // Used only for categorization\n    items: Array\u003c{\n      name: string;\n      weight: number; // Interpreted as direct probability (must sum to 1.0 across all items)\n      rateUp?: boolean;\n    }\u003e\n  }\u003e\n}\n```\n\n### Methods\n\n#### Rolling\n`roll(count?: number): string[]`\n- Simulate gacha rolls and returns item names\n- `count`: Number of rolls to perform (default: 1)\n- Returns array of item names\n\n#### Analysis\n`getItemDropRate(name: string): number`\n- Returns the effective drop rate for a specific item\n  - In weighted mode:\n    - Computed as `dropRate = (item.weight / totalPoolWeight) × rarityBaseRate`\n  - In flat rate mode:\n    - `Returns the item's defined probability.\n  - Throws if the item does not exist.\n\n`getRarityProbability(rarity: string): number`\n- Returns the base probability for a given rarity tier\n  - Only in \"weighted\" mode.\n  - Throws in flatRate mode.\n\n`getCumulativeProbabilityForItem(name: string, rolls: number): number`\n- Calculates probability of getting the item at least once in N rolls\n- Uses formula: `1 - (1 - dropRate)^rolls`\n\n`getRollsForTargetProbability(name: string, targetProbability: number): number`\n- Calculates the minimum number of rolls needed to reach a specific probability of pulling a given item.\n- Returns `Infinity` if item has zero drop rate\n- Returns 1 if target probability ≥ 1.0\n\n#### Utility\n`getRateUpItems(): string[]`\n- Returns names of all items marked with `rateUp: true`\n\n`getAllItemDropRates(): Array\u003c{name: string, dropRate: number, rarity: string}\u003e`\n- Returns a list of all items with:\n  - name: Item name\n  - dropRate: Calculated drop probability\n  - rarity: Associated rarity (or \"flatRate\" in flat mode)\n\n## 🧪 Tests\n\n\u003e Available in the GitHub repo only.\n\n```bash\n# Run the test suite with Vitest\nyarn test\n# or\nnpm test\n```\n\n## 🔗 Related Projects\nCheck out these related projects that might interest you:\n\n**[@allemandi/embed-utils](https://github.com/allemandi/embed-utils)**\n- Fast, type-safe utilities for vector embedding comparison and search.\n\n**[Embed Classify CLI](https://github.com/allemandi/embed-classify-cli)**\n- Node.js CLI tool for local text classification using word embeddings.\n\n## 🤝 Contributing\nIf you have ideas, improvements, or new features:\n\n1. Fork the project\n2. Create your feature branch (git checkout -b feature/amazing-feature)\n3. Commit your changes (git commit -m 'Add some amazing feature')\n4. Push to the branch (git push origin feature/amazing-feature)\n5. Open a Pull Request","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallemandi%2Fgacha-engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fallemandi%2Fgacha-engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fallemandi%2Fgacha-engine/lists"}