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

78 lines
2.7 KiB
JavaScript

// The gamble (a "quantum computer" in the first theme): chips are a second
// currency, hard to buy (the price rises with every chip owned) and hard to
// grow. Stake chips on a cycle: win and the stake doubles, hit a jackpot and it
// is multiplied, lose it, or crash and lose more than you bet. Reliability
// upgrades (paid in funds) tilt the odds. Chips give a diminishing demand bonus.
import { clamp } from './formulas.js';
const MAX_EXPONENT = 200;
export function chipCost(state, ctx) {
const g = ctx.rules.gamble;
return Math.ceil(g.baseChipCost * g.chipCostGrowth ** Math.min(state.chips, MAX_EXPONENT));
}
export function reliabilityCost(state, ctx) {
const r = ctx.rules.gamble.reliability;
return Math.ceil(r.baseCost * r.costGrowth ** state.reliabilityLevel);
}
/** Probabilities of each outcome of one cycle. They always sum to 1. */
export function odds(state, ctx, mods) {
const g = ctx.rules.gamble;
const crash = g.crashChance;
const jackpot = g.jackpotChance;
const reliability = g.baseWin + state.reliabilityLevel * g.reliability.perLevel + mods.reliability;
const win = clamp(reliability, 0.05, g.maxWin - crash - jackpot);
return { crash, jackpot, win, lose: 1 - crash - jackpot - win };
}
export function buyChip(state, ctx) {
const cost = chipCost(state, ctx);
if (state.funds < cost || state.chips >= ctx.rules.gamble.maxChips) return false;
state.funds -= cost;
state.chips += 1;
return true;
}
export function buyReliability(state, ctx) {
if (state.reliabilityLevel >= ctx.rules.gamble.reliability.maxLevel) return false;
const cost = reliabilityCost(state, ctx);
if (state.funds < cost) return false;
state.funds -= cost;
state.reliabilityLevel += 1;
return true;
}
/**
* Stake `stake` chips on one cycle. Returns { outcome, stake, delta } or null
* when it cannot be played (no chips, nothing staked, still cooling down).
*/
export function gamble(state, ctx, stake, mods, rng = Math.random) {
const g = ctx.rules.gamble;
const bet = Math.min(Math.floor(stake), state.chips);
if (!(bet >= 1) || state.gambleCooldown > 0) return null;
const p = odds(state, ctx, mods);
const roll = rng();
let outcome = 'lose';
let delta = -bet;
if (roll < p.crash) {
outcome = 'crash';
delta = -Math.min(state.chips, Math.ceil(bet * g.crashMultiplier));
} else if (roll < p.crash + p.jackpot) {
outcome = 'jackpot';
delta = bet * g.jackpotMultiplier;
} else if (roll < p.crash + p.jackpot + p.win) {
outcome = 'win';
delta = bet;
}
state.chips = clamp(state.chips + delta, 0, g.maxChips);
state.gambleCooldown = g.cooldownSeconds;
if (delta > 0) state.stats.gambleWins += 1;
else state.stats.gambleLosses += 1;
return { outcome, stake: bet, delta };
}