@open-rgs/gamble
The interesting number is the edge, not the payout.
Double-or-nothing at exactly one half returns everything staked. A game that offers it returns more than its base RTP to any player who uses it, and less to one who does not, which is a strange thing to ship by accident. Every function here takes the chance explicitly and reports what the round is worth.
One step, priced
import { stepReturn, stepEdge, fairChance } from "@open-rgs/gamble";
stepReturn({ chance: 0.5, on: 2 }); // 1.00, fair
stepEdge({ chance: 0.49, on: 2 }); // 0.02, two percent to the house
fairChance(2); // 0.5
fairChance(1.5, 0.5); // 0.5, a half gamble that keeps half on a loss
fairChance(4); // 0.25, a quadruple A step that returns more than 1 pays the player to take it. That is a mistake rather than a feature, and it is easier to see as a number than in a report three weeks later.
A ladder changes variance, not RTP
Climbing a fair ladder returns exactly what it staked however far it goes, so the feature moves the shape of the distribution and leaves its mean alone. With an edge, the return decays geometrically: five rungs at two percent keeps about 90 percent of the stake.
import { gambleLadder, ladderReturn, survivalChance } from "@open-rgs/gamble";
const STEP = { chance: 0.49, on: 2 };
ladderReturn(STEP, 5); // 0.904, per unit staked
survivalChance(STEP, 5); // 0.028, what the player is actually betting against
const round = gambleLadder(win, STEP, (stake, steps) => wantsToContinue(stake, steps), host.rng_next, {
maxSteps: 5,
maxStake: 10_000,
});
round.stake; // 0 when busted
round.history; // the stake after each step, for the replay The policy argument is the player in a real round and whatever you want to price against in a simulation: "always climb" is the worst case, and it is one line.
Every gamble needs a limit of its own
maxSteps and maxStake end the round. Without them the max win is unbounded and the engine's payout cap becomes the only thing that stops it, which is a payout rule standing in for a game rule. The round says which one ended it, so a report can tell a bust from a cap.
Collect or risk
The other shape: a prize that grows while the player declines to collect. Same arithmetic, different presentation, and worth its own function because the growth is additive, so the edge per step changes as the prize climbs.
import { collectOrRisk } from "@open-rgs/gamble";
collectOrRisk(prize, 5, 0.8, (p, steps) => steps < 3, host.rng_next, { maxSteps: 10 });