Frogger Game Responses
This guide explains how to read the scenario field returned by placeBet for a frogger-style (chicken-run / crash-walk) game, and how to use the loadConfig data to display cashout multipliers.
Overview
Frogger is a multi-step wager game. The player places an initial bet at a chosen risk level, then progresses through steps — at each step they can cash out or continue. Surviving a step multiplies their cashout value; dying loses everything. Each risk level has its own wager multiplier and cashout progression.
The game uses wager stake features (wagerStakeFeatures) — each step is a separate feature where the entry's win is the absolute cashout multiplier. The player's choice to continue triggers the next step's feature automatically.
The Frogger Scenario
When placeBet returns a frogger result, gameResult.scenario contains:
interface IFroggerScenario {
step: number; // Current step index (0 = initial bet)
risk: string; // Risk level name: "low" | "medium" | "high" | "extreme"
alive: boolean; // Whether the player survived this step
cashoutMultiplier: number; // Cashout value if the player collects here
}Step 0 — Initial Bet
The first placeBet call places the initial bet. It must include a featureToBuy selecting the risk level (see Risk Selection via Buy Features). The result tells you whether the player survived entry:
const response = await placeBet({ backendURL, token, stake, featureToBuy: `risk_${selectedRisk}` });
if (response.success) {
const { scenario, engineData, totalWin } = response.result.result;
const frogger = scenario as IFroggerScenario;
if (frogger.alive) {
// Player survived — show cashout offer
console.log(`Cashout available: ${frogger.cashoutMultiplier}x`);
// engineData.inProgress === true — player must wager or collect
} else {
// Instant death — player loses their bet
console.log('Died on entry');
// engineData.inProgress === false — round is over
}
}Subsequent Steps — Wager or Collect
After surviving, the game is in progress (engineData.inProgress === true). The player chooses to:
- Collect — cash out at the current multiplier
- Continue — wager their current cashout for a higher multiplier
// Player wants to continue — place a wager bet
const wagerResponse = await placeBet({
backendURL,
token,
stake, // Same stake as the original bet
});
if (wagerResponse.success) {
const { scenario, engineData, totalWin } = wagerResponse.result.result;
const frogger = scenario as IFroggerScenario;
if (frogger.alive) {
// Survived — new cashout multiplier available
console.log(`Step ${frogger.step}: ${frogger.cashoutMultiplier}x`);
if (!engineData.inProgress) {
// Final step — auto-cashout
console.log(`Auto-cashout: ${totalWin}`);
}
} else {
// Died — everything is lost
console.log(`Busted at step ${frogger.step}`);
// engineData.inProgress === false
}
}To collect instead of continuing, use the collect/cashout mechanism rather than calling placeBet again.
Collect Response — deathStep and maxSteps
When the player collects, the response's scenario is enriched with two extra fields on top of the usual { risk, step, alive }:
interface IFroggerCollectScenario {
risk: string;
step: number; // Step the player cashed out on
alive: boolean;
/**
* The step the player *would have* busted on had they kept going —
* `null` if the simulated run would have survived to the final step.
*/
deathStep: number | null;
/** Maximum steps configured for this frogger table. */
maxSteps: number;
}deathStep is purely cosmetic / informational — a "what would have happened" readout for the UI (e.g. "You cashed out at step 3. You would have busted on step 5!"). It is:
- Simulated with
Math.random(), not certified RNG. - Not part of certified gameplay — it does not affect the payout or audit log.
nullwhen the simulated run would have reached the final step without busting.
maxSteps is the total number of progression steps configured for the table, useful for rendering "cashed out at N of M" style displays.
const collectResponse = await collect({ backendURL, token });
if (collectResponse.success) {
const { scenario, totalWin } = collectResponse.result.result;
const frogger = scenario as IFroggerCollectScenario;
if (frogger.deathStep === null) {
showMessage(`Cashed out at step ${frogger.step}/${frogger.maxSteps} — you would have survived!`);
} else {
showMessage(`Cashed out at step ${frogger.step}/${frogger.maxSteps} — you would have busted on step ${frogger.deathStep}.`);
}
}Note: deathStep and maxSteps are only present on the collect response. The placeBet scenario during normal play contains { risk, step, alive, cashoutMultiplier }.
Cashout Multipliers from loadConfig
The loadConfig response includes the full cashout progression for each risk level. Use this to render the step ladder in your UI before the player starts.
const configResponse = await loadConfig({ backendURL, token });
if (configResponse.success) {
const lc = configResponse.result.config;
// lc.maxSteps — maximum number of progression steps
// lc.risks — array of { name, steps, multipliers }
// where multipliers is the cashout values for that risk per step
// (index 0 is always 0, index N is the cashout multiplier at step N)
const risks = lc.risks as Array<{ name: string; steps: number; multipliers: number[] }>;
// risks[0] → { name: "low", steps: 10, multipliers: [0, 1.25, 1.5, 2, 2.5, 3, ...] }
// risks[3] → { name: "extreme", steps: 5, multipliers: [0, 3, 9, 27, 81, 243] }
}Rendering the Step Ladder
function renderStepLadder(risk: { name: string; multipliers: number[] }) {
// multipliers[0] = 0 (step 0 has no cashout)
// multipliers[1] = cashout after surviving step 0
// multipliers[N] = cashout after surviving step N-1
for (let step = 1; step < risk.multipliers.length; step++) {
renderStep(step, `${risk.multipliers[step]}x`);
}
}How RTP Works in Frogger
The house edge is taken once, on entry
The target RTP (e.g. 95%) is applied entirely at step 0 — the initial bet. After that, every wager step is a fair game.
Here's a concrete example with a 2x wager multiplier and 95% target RTP:
| Step | Cashout | Survive chance | Cumulative RTP |
|---|---|---|---|
| 0 (entry) | — | 47.5% | — |
| 1 | 2x | 50% | 95% |
| 2 | 4x | 50% | 95% |
| 3 | 8x | 50% | 95% |
| 4 | 16x | 50% | 95% |
- Step 0 survive chance = 95% ÷ 2.0 = 47.5% (slightly below the "fair" 50%)
- Steps 1+ survive chance = 50% (exactly fair — a coin flip for double-or-nothing)
The 5% house edge comes from step 0 being slightly harder than fair. Once the player survives entry, every subsequent wager is neutral — the player isn't fighting the house anymore, just the odds.
Why it's the same RTP at every cashout point
No matter which step the player decides to cash out at, their expected return on the original bet is always 95%:
- Cash out at step 1: 47.5% × 2x = 0.95x
- Cash out at step 2: 47.5% × 50% × 4x = 0.95x
- Cash out at step 5: 47.5% × 50%⁴ × 32x = 0.95x
This means players can't gain an advantage by always cashing out early or always going deep — the game is balanced at every step.
With multiplier rounding
When multiplier rounding is enabled, the cashout values at each step are rounded to clean numbers (e.g. 2x, 4x, 8x instead of 1.97x, 3.88x, 7.65x). Two things are adjusted automatically to maintain the target RTP:
- Survive weights at each step may vary slightly from the "fair" rate.
- Per-step win values are absolute cashout multipliers (e.g. 1.25, 1.5, 2.0), not relative ratios. This avoids compounding rounding errors across many steps. The engine uses
wagerStakeFeaturesmode — each entry's win IS the cashout multiplier at that step, applied directly to the stake.
Risk Selection via Buy Features
Every risk level is a buy feature — there is no default basegame. Every opening placeBet must include a featureToBuy; a call without one will be rejected with PARAMETERMISSING ("This game requires a buy feature. Provide featureToBuy.").
import { placeBet } from '@hizi.io/engine-sdk';
// Buy features are named like "risk_low", "risk_medium", "risk_high", "risk_extreme"
const featureId = `risk_${selectedRisk}`;
const response = await placeBet({
backendURL,
token,
stake,
featureToBuy: featureId,
});featureToBuy is required only on the opening bet; the wager-stake continuations (step 1, step 2, …) are plain placeBet calls — the engine tracks the active feature across the round.
Multi-Step Game Flow
Frogger is a multi-step wager game. The typical flow:
- Initial bet —
placeBetwithfeatureToBuyfor risk selection (required) - Check result — if
alive, show cashout offer; if dead, round is over - Player decides — collect (end round) or continue (call
placeBetagain) - Repeat until the player collects, dies, or reaches the final step (auto-cashout)
placeBet (step 0) → alive? → placeBet (step 1) → alive? → ... → collect or bust
→ dead? → round overengineData.inProgress is true while the player has an active cashout to wager or collect. It becomes false when the player dies, collects, or hits the auto-cashout at the final step.
Complete Example
import { login, loadConfig, placeBet } from '@hizi.io/engine-sdk';
interface IFroggerScenario {
step: number;
risk: string;
alive: boolean;
cashoutMultiplier: number;
}
// After login and loadConfig...
const config = configResponse.result.config;
const risks = config.risks as Array<{ name: string; steps: number; multipliers: number[] }>;
// Player selects "high" risk
const selectedRisk = 'high';
const featureId = `risk_${selectedRisk}`;
// Show the step ladder
const risk = risks.find(r => r.name === selectedRisk)!;
for (let i = 1; i < risk.multipliers.length; i++) {
renderStep(i, `${risk.multipliers[i]}x`);
}
// Place initial bet with buy feature
let response = await placeBet({
backendURL,
token,
stake,
featureToBuy: featureId,
});
// Game loop
while (response.success) {
const { scenario, engineData, totalWin } = response.result.result;
const frogger = scenario as IFroggerScenario;
if (!frogger.alive) {
// Player died
showBust(frogger.step);
break;
}
if (!engineData.inProgress) {
// Auto-cashout at final step
showWin(totalWin * stake);
break;
}
// Show cashout offer and wait for player decision
const playerChoice = await showCashoutOffer(frogger.cashoutMultiplier);
if (playerChoice === 'collect') {
// Player collects — end the round
showWin(totalWin * stake);
break;
}
// Player continues — place next wager
response = await placeBet({ backendURL, token, stake });
}Provably Fair Verification
Frogger sits on top of the fixed-odds pipeline: each hop is a weighted entry draw, the wager-stake cascade is deterministic from the entries, and there are no mid-round player choices the seeds don't already imply. Use pfVerify to replay every hop from the committed seeds.
Request
import { pfVerify } from '@hizi.io/engine-sdk';
const reply = await pfVerify({
backendURL,
token,
serverSeed: pf.revealedServerSeed,
clientSeed: pf.clientSeed,
stake: 100,
featureToBuy: 'frogger_medium', // the round's risk tier
});No actions[] — each hop chains automatically from the previous step's nextFeature; the verify path runs the same cascade.
Reading the response
rngData— oneintrow per hop (each entry draw). Array-equal against the livepf.rngData.steps[i]— theIGameResultafter hopi, includingscenario.currentMultiplier. Compare against the live continuation chain.steps[steps.length - 1].totalWin— the final cashout multiplier the round terminated at (or0if the frog died).
The collect-time deathStep shown by the live UI is not part of pfVerify's output — it's Math.random()-driven cosmetic enrichment in frogger.collect, not PF-derived.
Next Steps
- Response Handling —
IGameResultstructure andengineDatafields. - Plinko Responses — Reading plinko game responses.
- Buy Features — How buy features work.