Appearance
Over/Under Game Responses
This guide explains how to read the scenario field that placeBet returns for an Over/Under game. It also explains how to use the loadConfig data to display odds and multipliers.
Overview
Over/Under is a single-roll wager game. The engine rolls a virtual 100-sided die (values 1–100). The player bets whether the result is over or under a chosen threshold.
- Under T: wins if the roll is ≤ T. Win chance = T/100.
- Over T: wins if the roll is > T. Win chance = (100 − T)/100.
The payout multiplier scales inversely with win probability: multiplier = RTP / P(win). RTP (Return to Player) is the average percentage of all stakes that the game pays back to players. Lower risk means smaller rewards. Higher risk means bigger wins.
Each bet is a buy feature (a bet that the player buys directly, in one step). The player selects a bet and places it in a single placeBet call. The game has no multi-step flow. It has no collect step.
The Over/Under Scenario
When placeBet returns an Over/Under result, gameResult.scenario contains:
typescript
interface IOverUnderScenario {
roll: number; // Die result: 1–100
threshold: number; // The chosen threshold
betType: string; // "under" or "over"
won: boolean; // Whether the bet was correct
multiplier?: number; // Win multiplier - present on wins
}Game Flow
Single-Step: Place Bet and Roll
Each bet is a buy feature. The player picks a bet (over_75, under_30, etc.). The player calls placeBet with featureToBuy:
typescript
const response = await placeBet({
backendURL,
token,
stake: 100,
featureToBuy: 'over_75',
});
if (response.success) {
const { scenario, totalWin } = response.result.result;
const result = scenario as IOverUnderScenario;
console.log(`Rolled: ${result.roll}`);
if (result.won) {
console.log(`Win! ${result.multiplier}x → payout ${totalWin}`);
} else {
console.log(`Loss`);
}
}TIP
The game resolves instantly. It has no collect step, no playerChoice, and no follow-up calls. One request produces one result.
Feature Naming
Buy features follow the pattern {direction}_{threshold}:
| Feature | Bet | Win condition |
|---|---|---|
under_1 | Under 1 | roll ≤ 1 |
over_1 | Over 1 | roll > 1 |
under_50 | Under 50 | roll ≤ 50 |
over_50 | Over 50 | roll > 50 |
under_99 | Under 99 | roll ≤ 99 |
over_99 | Over 99 | roll > 99 |
The game provides 198 bets (under_1 through under_99, over_1 through over_99) as buy features.
Loading Game Config
The loadConfig response provides the odds table:
typescript
const config = configResponse.result.config;
config.diceMax; // 100 (die sides)
config.odds; // Array of threshold oddsUsing the Odds Table
typescript
interface IOverUnderOdds {
threshold: number; // 1–99
pUnder: number; // P(roll <= threshold)
pOver: number; // P(roll > threshold)
underMultiplier: number; // Payout for correct "under" bet
overMultiplier: number; // Payout for correct "over" bet
}
const odds = config.odds as IOverUnderOdds[];
// Show odds for threshold 75
const t75 = odds[74]; // 0-indexed: threshold 75 is index 74
console.log(`Under 75: ${(t75.pUnder * 100).toFixed(0)}% → ${t75.underMultiplier}x`);
console.log(`Over 75: ${(t75.pOver * 100).toFixed(0)}% → ${t75.overMultiplier}x`);How RTP Works
The rtpMode setting in the builder controls the house edge in two ways:
Adjust Multipliers (default)
This mode keeps natural win probabilities. It reduces the multiplier to reach the target RTP. The house edge is visible in the payout.
P(win) × multiplier = target RTP
| Bet | P(win) | Multiplier (98% RTP) | EV |
|---|---|---|---|
| Under 50 | 50% | 1.96x | 0.98 |
| Over 50 | 50% | 1.96x | 0.98 |
| Under 98 | 98% | 1.0x | 0.98 |
| Over 98 | 2% | 49.0x | 0.98 |
Adjust Weights
This mode uses a fair multiplier (1/P). It reduces the win probability to reach the target RTP. The house edge stays hidden in the win rate.
adjusted P(win) × fair multiplier = target RTP
| Bet | Multiplier | Adjusted P(win) | EV |
|---|---|---|---|
| Under 50 | 2.0x | 49% | 0.98 |
| Over 50 | 2.0x | 49% | 0.98 |
| Under 98 | 1.0204x | 96.04% | 0.98 |
| Over 98 | 50.0x | 1.96% | 0.98 |
Every bet has the same expected value, regardless of threshold or RTP mode. The player trades risk for reward.
Multiplier rounding
If you enable multiplier rounding, the engine snaps multipliers to clean numbers (e.g. 1x, 1.5x, 2x). In "Adjust Multipliers" mode, this may cause the actual RTP to deviate slightly from the target. In "Adjust Weights" mode, the engine adjusts weights to maintain the target RTP exactly.
Feature Structure
Over/Under uses standalone buy features. It has no basegame. Every placeBet call must include a featureToBuy. The engine rejects a call without one with PARAMETERMISSING ("This game requires a buy feature. Provide featureToBuy.").
under_50 (buy feature, resolves instantly)
├─ win entries (rolls 1–50): win = multiplier
└─ lose entries (rolls 51–100): win = 0
over_75 (buy feature, resolves instantly)
├─ win entries (rolls 76–100): win = multiplier
└─ lose entries (rolls 1–75): win = 0Key points:
- Every bet is a buy feature that
featureToBuytriggers. - No wager features. Each feature resolves directly to a final win amount.
- Win entries show specific roll values from the winning range for visual variety.
- Lose entries show specific roll values from the losing range.
- Single round. One
placeBetcall gives an immediate result.
Complete Example
typescript
import { login, loadConfig, placeBet } from '@hizi.io/engine-sdk';
interface IOverUnderScenario {
roll: number;
threshold: number;
betType: string;
won: boolean;
multiplier?: number;
}
// After login and loadConfig...
const odds = config.odds;
// Player wants "Over 75" - show them the odds first
const t75 = odds[74];
console.log(`Over 75: ${(t75.pOver * 100).toFixed(0)}% chance → ${t75.overMultiplier}x`);
// Place bet and roll in one call
const response = await placeBet({
backendURL,
token,
stake: 100,
featureToBuy: 'over_75',
});
if (response.success) {
const result = response.result.result.scenario as IOverUnderScenario;
const totalWin = response.result.result.totalWin;
if (result.won) {
console.log(`Rolled ${result.roll} - Win! ${result.multiplier}x → payout ${totalWin}`);
} else {
console.log(`Rolled ${result.roll} - Loss`);
}
}Provably Fair Verification
Over/Under is one-shot. A single entry draw (the random number the engine generates for the round) fixes the roll. The threshold and direction (from featureToBuy) decide the payout. Use pfVerify to replay the draw.
Request
typescript
import { pfVerify } from '@hizi.io/engine-sdk';
const reply = await pfVerify({
backendURL,
token,
serverSeed: pf.revealedServerSeed,
clientSeed: pf.clientSeed,
stake: 100,
featureToBuy: 'over_50', // the round's threshold + direction package
});Reading the response
This is one-shot. steps has one entry.
rngData: oneintrow (the entry draw). Compare it to the livepf.rngData. The arrays must be equal.steps[0].scenario.roll: this value equals the live roll value.steps[0].totalWin: this value equals the live multiplier.
typescript
if (!reply.success) return;
const { rngData, steps } = reply.result;
const rollOk = steps[0].scenario.roll === liveScenario.roll;
const winOk = steps[0].totalWin === liveResult.totalWin;Next Steps
- Response Handling:
IGameResultstructure andengineDatafields. - Hi/Lo Responses: a multi-step card guessing game with cashout.
- Keno Responses: a number selection game with instant resolution.