@allemandi/gacha-engine
Version:
Practical, type-safe toolkit for simulating and understanding gacha rates and rate-ups.
3 lines (2 loc) • 6.1 kB
JavaScript
function t(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,a=Array(e);r<e;r++)a[r]=t[r];return a}function e(e,r){var a="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(a)return(a=a.call(e)).next.bind(a);if(Array.isArray(e)||(a=function(e,r){if(e){if("string"==typeof e)return t(e,r);var a={}.toString.call(e).slice(8,-1);return"Object"===a&&e.constructor&&(a=e.constructor.name),"Map"===a||"Set"===a?Array.from(e):"Arguments"===a||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(a)?t(e,r):void 0}}(e))||r&&e&&"number"==typeof e.length){a&&(e=a);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[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.")}function r(){return r=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var a in r)({}).hasOwnProperty.call(r,a)&&(t[a]=r[a])}return t},r.apply(null,arguments)}var a,n=/*#__PURE__*/function(){function t(t){if(this.mode=void 0,this.pools=[],this.rarityRatesScaled={},this.flatRateMap=new Map,this.dropRateCacheScaled=new Map,this.flatRateRateUpItems=[],this.mode=t.mode,"weighted"===t.mode){var r=t;this.pools=r.pools,this.rarityRatesScaled=this.scaleRarityRates(r.rarityRates),this.validateConfig(r.rarityRates)}else{if("flatRate"!==t.mode)throw new Error("Unknown gacha mode: "+this.mode);var a=t;this.pools=a.pools;for(var n,i=e(a.pools);!(n=i()).done;)for(var o,s=e(n.value.items);!(o=s()).done;){var l=o.value;if(l.weight<0)throw new Error('FlatRate item "'+l.name+'" must have non-negative weight');this.flatRateMap.set(l.name,l.weight),l.rateUp&&this.flatRateRateUpItems.push(l.name)}var u=Array.from(this.flatRateMap.values()).reduce(function(t,e){return t+e},0);if(Math.abs(u-1)>1e-6)throw new Error("FlatRate item rates must sum to 1.0, but got "+u)}}var a=t.prototype;return a.scaleRarityRates=function(t){for(var e={},r=0,a=Object.entries(t);r<a.length;r++){var n=a[r],i=n[0],o=n[1];if(o<0||o>1)throw new Error('Rarity rate for "'+i+'" must be between 0 and 1, got '+o);e[i]=this.toScaled(o)}return e},a.toScaled=function(e){if(e>t.MAX_SAFE_SCALE/t.SCALE)throw new Error("Probability "+e+" too large for safe integer arithmetic");return Math.round(e*t.SCALE)},a.fromScaled=function(e){return e/t.SCALE},a.validateConfig=function(t){var r=new Set(Object.keys(this.rarityRatesScaled)),a=new Set(this.pools.map(function(t){return t.rarity})),n=Array.from(a).filter(function(t){return!r.has(t)});if(n.length>0)throw new Error("Missing rarity rates for: "+n.join(", "));var i=Object.values(t).reduce(function(t,e){return t+e},0);if(Math.abs(i-1)>1e-10)throw new Error("Rarity rates must sum to 1.0, got "+i);for(var o,s=e(this.pools);!(o=s()).done;){var l=o.value;if(0===l.items.length)throw new Error('Rarity "'+l.rarity+'" has no items');if(l.items.reduce(function(t,e){return t+e.weight},0)<=0)throw new Error('Rarity "'+l.rarity+'" has zero total weight');for(var u,h=e(l.items);!(u=h()).done;){var f=u.value;if(f.weight<0)throw new Error('Item "'+f.name+'" weight must be non-negative, got '+f.weight)}if(!l.items.some(function(t){return t.weight>0}))throw new Error('Rarity "'+l.rarity+'" must have at least one item with positive weight')}},a.getItemDropRate=function(r){if("flatRate"===this.mode)return this.flatRateMap.get(r)||0;if(this.dropRateCacheScaled.has(r))return this.fromScaled(this.dropRateCacheScaled.get(r));for(var a,n=e(this.pools);!(a=n()).done;){var i=a.value,o=i.items.find(function(t){return t.name===r});if(o){if(0===o.weight)return this.dropRateCacheScaled.set(r,0),0;var s=i.items.reduce(function(t,e){return t+e.weight},0),l=this.rarityRatesScaled[i.rarity],u=this.toScaled(o.weight),h=this.toScaled(s),f=Math.round(u*l/t.SCALE),c=Math.round(f*t.SCALE/h);return this.dropRateCacheScaled.set(r,c),this.fromScaled(c)}}throw new Error('Item "'+r+'" not found')},a.getCumulativeProbabilityForItem=function(t,e){var r=this.getItemDropRate(t);return 0===r?0:r>=1?1:1-Math.pow(1-r,e)},a.getRollsForTargetProbability=function(t,e){if(e<=0)return 0;if(e>=1)return 1;var r=this.getItemDropRate(t);return r<=0?Infinity:Math.ceil(Math.log(1-e)/Math.log(1-r))},a.getRateUpItems=function(){return"weighted"===this.mode?this.pools.flatMap(function(t){return t.items.filter(function(t){return t.rateUp}).map(function(t){return t.name})}):this.flatRateRateUpItems},a.getAllItemDropRates=function(){var t=this;return"flatRate"===this.mode?Array.from(this.flatRateMap.entries()).map(function(t){return{name:t[0],dropRate:t[1],rarity:"flatRate"}}):this.pools.flatMap(function(e){return e.items.map(function(r){return{name:r.name,dropRate:t.getItemDropRate(r.name),rarity:e.rarity}})})},a.roll=function(t){var r=this;void 0===t&&(t=1);for(var a=[],n=function(){if("flatRate"===r.mode)for(var t,n=Math.random(),i=0,o=e(r.flatRateMap.entries());!(t=o()).done;){var s=t.value;if(n<(i+=s[1])){a.push(s[0]);break}}else{var l=r.selectRarity(),u=r.pools.find(function(t){return t.rarity===l}),h=r.selectItemFromPool(u);a.push(h.name)}},i=0;i<t;i++)n();return a},a.selectRarity=function(){for(var e=Math.floor(Math.random()*t.SCALE),r=0,a=0,n=Object.entries(this.rarityRatesScaled);a<n.length;a++){var i=n[a];if(e<(r+=i[1]))return i[0]}return Object.keys(this.rarityRatesScaled)[0]},a.selectItemFromPool=function(t){for(var a,n=this,i=t.items.filter(function(t){return t.weight>0}),o=i.map(function(t){return r({},t,{scaledWeight:n.toScaled(t.weight)})}),s=o.reduce(function(t,e){return t+e.scaledWeight},0),l=Math.floor(Math.random()*s),u=0,h=e(o);!(a=h()).done;){var f=a.value;if(l<(u+=f.scaledWeight))return{name:f.name,weight:f.weight}}return i[0]},a.getDebugInfo=function(){for(var e={},a=0,n=Object.entries(this.rarityRatesScaled);a<n.length;a++){var i=n[a];e[i[0]]=this.fromScaled(i[1])}return{scale:t.SCALE,rarityRatesScaled:r({},this.rarityRatesScaled),rarityRatesFloat:e}},t}();a=n,n.SCALE=1e6,n.MAX_SAFE_SCALE=Math.floor(Number.MAX_SAFE_INTEGER/a.SCALE);export{n as GachaEngine};
//# sourceMappingURL=index.module.js.map