Mines Game Responses
This guide explains how to read the scenario field returned by placeBet for a mines-style game, and how to use the loadConfig data to display cashout multipliers.
Overview
Mines is a multi-step wager game. The player places an initial bet with a chosen mine count, then reveals tiles on a grid. Each safe tile increases the cashout multiplier. Hitting a mine loses everything. The player can cash out at any point between picks.
The game uses wager stake features (wagerStakeFeatures). Each pick is a separate feature where the entry's win is the absolute cashout multiplier relative to the original stake. This avoids compounding rounding errors across many picks.
Sending a Pick Position
Every placeBet call for a mines game must include a pickPosition in additionalData. This tells the engine which tile the player selected:
// Initial bet — select mine count via featureToBuy + first tile pick
await placeBet({
backendURL,
token,
stake: 100,
featureToBuy: 'mines_10',
additionalData: { pickPosition: 7 },
});
// Continuation — subsequent tile picks
await placeBet({
backendURL,
token,
playerChoiceIndex: 0,
additionalData: { pickPosition: 12 },
});WARNING
Omitting pickPosition will return an error. Every bet in a mines game requires a tile selection.
The Board Array
The engine's game layer enriches each placeBet response with a board array representing the full grid state. Each element is one of:
| Value | Meaning |
|---|---|
null | Unknown — tile has not been revealed |
'picked' | Safe — player picked this tile and survived |
'bomb' | Mine — either the player hit it, or it was revealed on game over |
interface IMinesScenario {
board: (null | 'bomb' | 'picked')[]; // Length = grid size (e.g. 25 for 5x5)
currentMultiplier: number; // Current cashout multiplier
mineCount: number; // Mines on this board
}Reading the Board
const response = await placeBet({
backendURL, token, stake: 100,
featureToBuy: 'mines_10',
additionalData: { pickPosition: 7 },
});
if (response.success) {
const { scenario, engineData } = response.result.result;
const { board, currentMultiplier, mineCount } = scenario as IMinesScenario;
// board[7] is 'picked' (safe) or 'bomb' (mine hit)
if (board[7] === 'picked') {
console.log(`Safe! Cashout: ${currentMultiplier}x`);
// engineData.canCollect === true — collect or continue
} else {
console.log('Boom!');
// All mine positions are revealed in the board
}
}Game Over Reveal
When the round ends (mine hit or auto-cashout on final safe tile), all mine positions are revealed in the board array. Tiles the player never picked remain null unless they contain a mine, in which case they become 'bomb'.
// After hitting a mine, the board might look like:
// [null, 'picked', null, 'bomb', 'picked', null, null, 'bomb', null, ...]
// ↑ safe ↑ mine ↑ safe ↑ the hitPlayer Actions After a Safe Pick
- Collect — cash out at the current multiplier
- Continue — reveal another tile for a higher multiplier
// Player wants to continue
const next = await placeBet({
backendURL,
token,
playerChoiceIndex: 0,
additionalData: { pickPosition: 12 },
});
if (next.success) {
const { board, currentMultiplier } = next.result.result.scenario as IMinesScenario;
if (board[12] === 'picked') {
console.log(`New cashout: ${currentMultiplier}x`);
} else {
console.log('Mine hit — round over');
}
}Loading Game Config
The loadConfig response provides grid size, mine count range, and multiplier tables:
const config = configResponse.result.config;
config.gameType; // 'mines'
config.gridSize; // Total tiles (e.g. 25 for 5x5)
config.minMines; // Minimum mine count (e.g. 1)
config.maxMines; // Maximum mine count (e.g. 24)Mine Count Selection via Buy Features
Every mine count 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';
// Player selects 10 mines — first pick at position 3
const response = await placeBet({
backendURL,
token,
stake: 100,
featureToBuy: 'mines_10',
additionalData: { pickPosition: 3 },
});featureToBuy is required on the opening bet; subsequent picks within the same round are plain placeBet calls (the engine tracks the active feature).
Multi-Step Game Flow
Mines is a multi-step wager stake game. The typical flow:
- Initial bet —
placeBetwithpickPositionandfeatureToBuyfor mine count (required) - Pick loop — each
placeBetwithplayerChoiceIndex: 0and newpickPosition - End — player collects, hits a mine, or reveals all safe tiles (auto-cashout)
placeBet (pick 1) → [safe] → placeBet (pick 2) → [safe] → ... → collect
→ [mine] → round ends (loss, mines revealed)engineData.canCollect is true while the player has an active cashout. engineData.inProgress becomes false when the player hits a mine or reveals all safe tiles.
How the Math Works
The cashout multiplier at pick k is: targetRTP / P(survive k picks)
Where P(survive k picks) = product of (safeTiles - i) / (gridSize - i) for i = 0 to k-1.
The house edge is taken once on the first pick. After that, each step's survive probability matches the fair odds exactly. This means the RTP is the same regardless of when the player collects.
Provably Fair Verification
A PF mines round commits to every entry draw (one per pick) and to the seeded mine-reveal shuffle. Use pfVerify to have the engine replay the round and confirm both. The bomb-reveal shuffle is seeded from the PF commitment itself — (serverSeedHash, clientSeed) — so the verifier reproduces the same bomb layout without any extra inputs.
Request
Replay every pick — including the opening one — by passing each step's pickPosition in actions, in order. Continuation steps' playerChoiceIndex only matters when the engine surfaced a playerChoice (e.g. risk pick mid-round); the common case is playerChoiceIndex: 0.
import { pfVerify } from '@hizi.io/engine-sdk';
const reply = await pfVerify({
backendURL,
token,
serverSeed: pf.revealedServerSeed,
clientSeed: pf.clientSeed,
stake: 100,
featureToBuy: 'mines_10', // every mines round opens on a buy feature
actions: [
{ pickPosition: 7 }, // opening pick
{ pickPosition: 12 }, // continuation
{ pickPosition: 18 }, // continuation that hit a mine — round ends here
],
});Reading the response
steps[i].scenario is an IMinesScenario for pick i, including the cumulative board and (on the terminal step) the revealed mines. Compare against the round's recorded scenarios:
rngData— array-equal against the live round'spf.rngData. Each pick contributes oneintrow (the weighted entry draw).steps[i].scenario.board— element-wise against the live board at each step; terminal-step bomb positions match exactly.steps[steps.length - 1].totalWin(multiplier) — equal to the live round's terminaltotalWin.
if (!reply.success) return; // reply.error has the failure
const { rngData, steps } = reply.result;
const rngOk = rngData.every((row, i) => deepEqual(row, pf.rngData[i]));
const terminal = steps[steps.length - 1];
const liveBoard = lastLiveResult.scenario.board;
const boardOk = liveBoard.every((tile, i) => tile === terminal.scenario.board[i]);
const cashoutOk = terminal.totalWin === lastLiveResult.totalWin;