Files
idle_clicker/public/js/engine/formulas.js
T

62 lines
2.6 KiB
JavaScript

// Pure math helpers. No state, no DOM.
export function clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
/** Price of the next item when `owned` items are already owned. */
export function unitCost(base, growth, owned) {
return base * growth ** owned;
}
/** Total price of buying `count` more items (geometric series, rounded up). */
export function bulkCost(base, growth, owned, count) {
if (count <= 0) return 0;
return Math.ceil((unitCost(base, growth, owned) * (growth ** count - 1)) / (growth - 1));
}
/** How many items `budget` can pay for, robust against float error. */
export function maxAffordable(base, growth, owned, budget) {
const first = unitCost(base, growth, owned);
if (Number.isNaN(budget) || budget < first) return 0;
let n = Math.floor(Math.log((budget * (growth - 1)) / first + 1) / Math.log(growth));
while (n > 0 && bulkCost(base, growth, owned, n) > budget) n--;
while (bulkCost(base, growth, owned, n + 1) <= budget) n++;
return Math.max(n, 0);
}
/**
* Raw-material price per unit of material, before market noise. Expensive at
* first, then cheaper relative to revenue as the operation scales
* (economies of scale): it falls from `startUnitPrice` towards `floorUnitPrice`.
*/
export function materialUnitPrice(mm, totalProduced) {
const scale = 1 / Math.sqrt(1 + totalProduced / mm.scale);
return mm.floorUnitPrice + (mm.startUnitPrice - mm.floorUnitPrice) * scale;
}
/**
* What one purchase of material looks like. The lot grows with the operation
* (a share of everything produced so far, or `batchSecondsOfProduction` of
* current output), in doubling steps so the price stays readable.
* `factor` is the market's current noise, `discount` comes from upgrades.
*/
export function materialBatch(mm, totalProduced, rate, factor, discount) {
const basis = Math.max(totalProduced * mm.batchShareOfTotal, rate * mm.batchSecondsOfProduction);
const doublings = Math.max(0, Math.floor(Math.log2(Math.max(1, basis / mm.minBatch))));
const size = mm.minBatch * 2 ** doublings;
const cost = Math.max(1, Math.ceil(size * materialUnitPrice(mm, totalProduced) * factor * discount));
return { size, cost };
}
/**
* Units customers want to buy per second at `price`.
* Demand is maxed at or below the fair price and drops steeply above it, so
* extra production is only worth selling at a higher price while supply is
* short; to sell more at the fair price, the player needs more demand.
*/
export function demandPerSecond(market, price, multiplier) {
const overpriced = Math.max(1, price / market.fairPrice);
return (market.baseDemand * multiplier) / overpriced ** market.elasticity;
}