Skip to content

Over/Under Game Responses

This guide explains how to read the scenario field returned by placeBet for an Over/Under game, and how to use the loadConfig data to display odds and multipliers.

Overview

Over/Under is a single-roll wager game. A virtual 100-sided die (values 1–100) is rolled, and the player bets whether the result will be 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). Lower risk means smaller rewards; higher risk offers bigger wins.

Each bet is a buy feature — the player selects their bet and places it in a single placeBet call. No multi-step flow, 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 their bet (over_75, under_30, etc.) and 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 — no collect step, no playerChoice, no follow-up calls. One request in, one result out.

Feature Naming

Buy features follow the pattern {direction}_{threshold}:

FeatureBetWin condition
under_1Under 1roll ≤ 1
over_1Over 1roll > 1
under_50Under 50roll ≤ 50
over_50Over 50roll > 50
under_99Under 99roll ≤ 99
over_99Over 99roll > 99

All 198 bets (under_1 through under_99, over_1 through over_99) are available 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 odds

Using 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 house edge can be applied in two ways, controlled by the rtpMode setting in the builder:

Adjust Multipliers (default)

Natural win probabilities, multiplier reduced to hit target RTP. The house edge is visible in the payout.

P(win) × multiplier = target RTP

BetP(win)Multiplier (98% RTP)EV
Under 5050%1.96x0.98
Over 5050%1.96x0.98
Under 9898%1.0x0.98
Over 982%49.0x0.98

Adjust Weights

Fair multiplier (1/P), win probability reduced to hit target RTP. The house edge is hidden in the win rate.

adjusted P(win) × fair multiplier = target RTP

BetMultiplierAdjusted P(win)EV
Under 502.0x49%0.98
Over 502.0x49%0.98
Under 981.0204x96.04%0.98
Over 9850.0x1.96%0.98

Every bet has the same expected value regardless of threshold or RTP mode — the player trades risk for reward.

Multiplier rounding

When multiplier rounding is enabled, multipliers are snapped 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, weights are adjusted to maintain the target RTP exactly.

Feature Structure

Over/Under uses standalone buy features — there is no basegame. Every placeBet must include a featureToBuy; a call without one will be rejected 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 = 0

Key points:

  • Every bet is a buy feature triggered by featureToBuy.
  • No wager features — each feature resolves to a final win amount directly.
  • 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 placeBet call, 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 fixes the roll, the threshold/direction (from featureToBuy) decides 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

One-shot — steps has one entry.

  • rngData — one int row (the entry draw). Array-equal against the live pf.rngData.
  • steps[0].scenario.roll — equals the live roll value.
  • steps[0].totalWin — 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