78 lines
2.7 KiB
JavaScript
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 };
|
|
}
|