Appearance
Mines Game Responses
This guide explains how to read the scenario field that placeBet returns for a mines-style game. It also explains 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. The player then reveals tiles on a grid. Each safe tile increases the cashout multiplier. If the player hits a mine, the player loses the entire stake. The player can cash out at any point between picks.
The game uses wager stake features (wagerStakeFeatures). Each pick is a separate feature. 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:
typescript
// 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
The engine returns an error if a call omits pickPosition. Every bet in a mines game requires a tile selection.
The Board Array
The engine's game layer adds a board array to each placeBet response. This array shows the state of the full grid. Each element is one of the following:
| Value | Meaning |
|---|---|
null | Unknown. The engine has not revealed the tile. |
'picked' | Safe. The player picked this tile and survived. |
'bomb' | Mine. The player hit this tile, or the engine reveals it when the round ends. |
typescript
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
typescript
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 (a mine hit, or auto-cashout on the final safe tile), the engine reveals all mine positions in the board array. Tiles that the player never picked remain null. If an unpicked tile contains a mine, it becomes 'bomb'.
typescript
// 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.
typescript
// 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:
typescript
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 call must include a featureToBuy. If a call omits it, the engine returns PARAMETERMISSING ("This game requires a buy feature. Provide featureToBuy.").
typescript
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 },
});The opening bet requires featureToBuy. Subsequent picks in 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 the mine count (required). - Pick loop: each
placeBetcall withplayerChoiceIndex: 0and a newpickPosition. - End: the 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 engine takes the house edge once, on the first pick. After that, each step's survive probability matches the fair odds exactly. This means the RTP stays the same, regardless of when the player collects.
Provably Fair Verification
A Provably Fair (PF) mines round commits to every entry draw (one per pick) and to the seeded mine-reveal shuffle. Use pfVerify to make the engine replay the round and confirm both commitments. The engine seeds the bomb-reveal shuffle from the PF commitment itself, (serverSeedHash, clientSeed). The verifier reproduces the same bomb layout without extra inputs.
Request
Replay every pick, including the opening one. Pass each step's pickPosition in actions, in order. The playerChoiceIndex of a continuation step only matters when the engine surfaces a playerChoice (for example, a risk pick mid-round). In the common case, use playerChoiceIndex: 0.
typescript
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: compare it array-by-array against the live round'spf.rngData. Each pick contributes oneintrow (the weighted entry draw).steps[i].scenario.board: compare it element-by-element against the live board at each step. Terminal-step bomb positions must match exactly.steps[steps.length - 1].totalWin(multiplier): compare it to the live round's terminaltotalWin.
typescript
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;