Frosted Market Bonanza — Developer Guide
Frosted Market Bonanza — Developer Guide
This document is based on the actual implementation in the game code and config. It does not rely on assumptions or external marketing text. When the code does not make a rule clear, this guide explicitly says: "Not clearly identifiable from the current code."
1. Game Overview
Frosted Market Bonanza is implemented as a cluster/cascade slot game in a 6-column × 5-row grid.
- Grid size: 6 columns, 5 rows
- Win system: cluster-based, not a classic payline system
- The game uses symbol clusters and cascade refill logic
- The base game may trigger a free-spin feature through scatter counts
- The free-spin game has bomb multipliers and additional scatter retrigger logic
- There is no standard wild symbol in the current implementation
Imagine the game as a 5-row board that is filled with symbols. The backend groups matching symbols by position, checks whether a cluster is large enough, pays out based on the cluster size, removes winning symbols, refills the grid, and repeats until no more winning clusters remain.
The implementation shows this clearly in games/frostedmarketbonanza/src/logic.ts:
minClusterSize = 8groupSymbolPositions(...)is used to count symbols by position- winning symbols are excluded from
scatterandmultiplierSymbol - cluster wins are added together into
cascadeWin - if the board has no wins and no nudge trigger, the engine may apply symbol conversion
Main symbols
From the pay table and symbolsMap in games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json:
H1,H2,H3,H4: high-value symbolsL1,L2,L3,L4,L5: low-value symbolsSC: scatter symbolBN: multiplier bomb symbolSTACK1,STACK2: stack replacement symbols used during symbol resolution
The game's symbolsMap also maps raw display symbols like Pic1, Pic2, Pic3, Pic4, A, K, Q, J, 10, Scatter, and BOMB into the internal game values such as H1, H2, L1, SC, and BN.
Special symbols
Scatter
SC is the scatter symbol.
The config in games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json defines:
freeSpinsTrigger.minScatters = 4freeSpinsTrigger.initialCount = 10freeSpinsRetrigger.minScatters = 3freeSpinsRetrigger.additionalSpins = 5
When the board reaches the free-spin trigger threshold, the game sets nextFeature = 'freespins' and prepares a free-spin feature.
Multiplier bomb
BN is the multiplier symbol in free games. The code checks configSymbols.multiplierSymbol and stores multiplier values on bomb positions as symbolName_x such as BN_2, BN_5, etc. The multiplier is then applied to the free-spin win total.
Wild behavior
There is no wild symbol in the current code. The pay table and symbol map do not define a wild symbol, and the cluster detector deliberately skips scatter and multiplierSymbol from cluster wins.
This means the implementation does not have a standard wild substitute rule in Frosted Market Bonanza.
Scatter behavior
Scatter logic is implemented in games/frostedmarketbonanza/src/logic.ts:
lastScCountis counted fromsymbolCounts[configSymbols.scatter]?.countscatterPayis calculated frommath.payTable[SC][lastScCount - 1]and then multiplied bystakeValue- scatter triggers are checked after the cascade loop ends
- base game trigger threshold is
4scatters - retrigger threshold in free games is
3scatters
Bonus / free-spin behavior
The game supports free spins through freespins feature handling.
- Base game triggers free spins at
4scatters - Free-spin award:
10spins initially - Retrigger: if a free spin finishes with
3or more scatters, the code adds5more spins viapendingFeature - Free-spin results are stored in
featureResults[0]
Multipliers
Multipliers are implemented as bomb multipliers in free games.
- Bomb weights are defined for
BOMB_SET1_WeightsandBOMB_SET2_Weights - Selectable values include things like
2x,3x,4x,5x,8x,10x,12x,15x,20x,25x,50x,100x - The game sums bomb values across the free-spin cascade and multiplies the total free-spin win by the total bomb multiplier
Other important mechanics
- Nudge feature: on non-winning base-game spins with exactly 3 scatters, the code may nudge a limited number of reels and optionally add a scatter
- Symbol conversion: on blank/no-win base spins, the engine may convert low symbols to high symbols and re-run the spin
- Stack symbol replacement:
STACK1andSTACK2are resolved before evaluation and replaced with weighted high/low symbols - Buy bonus mode:
buyBonuschoosesBB_Triggermath and can place scatter symbols on the base spin before evaluation - Ante mode:
antechanges the game type and base bet toanteBaseBet = 25
2. Important Slot/Game Terminology
This is the terminology that matters for this game.
Reel
A reel is one vertical strip of symbols. In the actual layout, the game has 6 columns, meaning 6 reel positions in the board. Each column is generated from a reel strip in the JSON math file.
Row
A row is one horizontal band in the 5-row board. The board is 5 rows high.
Symbol
A symbol is a value such as H1, L2, or SC. In the platform, raw display values such as Pic1 or Scatter are mapped to internal symbols.
Cluster
A cluster is a group of matching symbol positions that touch and reach the minimum size. For Frosted Market Bonanza, the minimum cluster size is 8.
Cascade
A cascade is one evaluation pass on the board. After a winning cluster is removed, the board is refilled and evaluated again. The code calls cascadeRefillReelView(...) and continues while there are wins.
Payline
Not used in this game's current implementation. The code does not evaluate paylines. It evaluates clusters.
Way
Not used as a separate pay system here. The game does not use standard ways-of-win logic.
Win
A win is represented as spinWin inside a cascade entry, and also as totalWin on the final SpinResult. The code sums all cluster wins and scatter wins into the final result.
Wild
Not clearly identifiable from the current code. There is no WILD symbol defined in this game's config or pay table.
Scatter
The SC symbol. In this game, scatters count toward feature trigger thresholds and also contribute a scatter pay when enough are present.
Multiplier
A free-spin multiplier is represented by BN bomb values such as 2x, 8x, 25x, 100x. These can be applied to the total free-spin win.
Free Spin
A free spin is a spin executed in feature mode, represented by gameType = 'FG', ANTE_FG, or BB. Free spins are tracked by pendingFeature and featureResults.
Feature
A feature is a special round or state triggered by a symbol condition. In this game, the relevant feature states are free spins, nudge, and symbol conversion.
Bet
The player bet is passed in as betAmount and then translated into a stake value for the game. The code uses stakeValue = totalBet / baseBet.
Stake
The stake is the normalized bet value used in the pay calculation. For example, if totalBet = 20 and baseBet = 20, then stakeValue = 1.
Win amount
The final win is computed from cluster payouts and scatter payouts. The cluster formula is winAmount = payTable[symbol][sizeIndex] * stakeValue.
RTP
Not clearly identifiable from the current code. There is no explicit RTP field exposed in the math config or game code as a directly readable value.
Volatility
Not clearly identifiable from the current code. The code exposes weights, payouts, and feature triggers, but no explicit volatility label or formula.
3. Repository / Code Structure
The game lives in games/frostedmarketbonanza.
| File / Folder | Purpose | Important format | Notes |
|---|---|---|---|
| games/frostedmarketbonanza/src/plugin.ts | Game entry point for the platform | plugin | Loads the math JSON, exposes spin() and feature() |
| games/frostedmarketbonanza/src/engine.ts | Game execution orchestrator | engine | Chooses game mode, creates reels, runs base spin and optional conversion rerun |
| games/frostedmarketbonanza/src/logic.ts | Core game logic and win evaluation | logic | Evaluates clusters, scatter triggers, free spins, nudge, cascade refill |
| games/frostedmarketbonanza/src/interfaces.ts | Shared request/result contracts | types | Defines GameSpinInput, SpinResult, feature structs |
| games/frostedmarketbonanza/src/symbol-conversion.ts | Passive conversion feature | helper | Converts low symbols to high symbols under certain conditions |
| games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json | Math and configuration | JSON | Contains reel strips, weights, pay table, free-spin settings |
| games/frostedmarketbonanza/project.json | Nx game registration | config | Registers the project and cert tasks |
| tools/game-tools/src/executors/simulate/analyzers/games/frosted-market-bonanza.analyzer.ts | Simulation stats | analyzer | Counts cluster wins, scatter wins, bomb multipliers, nudge triggers |
Why these files matter
- plugin.ts is the public entry point seen by the platform.
- engine.ts decides which reel set and math mode to use and handles a conversion rerun.
- logic.ts contains the true round evaluation logic.
- interfaces.ts defines the data structures used across the request and response pipeline.
- frosted-market-bonanza-R3.json stores the actual rules, reel strips, and weights.
Important files and responsibilities
games/frostedmarketbonanza/src/plugin.ts
This file loads the JSON math file and exposes the platform-level spin() API.
- Input is a platform
ISpinInput - The function extracts
betAmount,isBuyBonus,extraData,gameMode,devMode, andrgsMaxWin - It converts the input into the internal
GameSpinInput - It returns
ISpinOutput<SpinResult>
games/frostedmarketbonanza/src/engine.ts
This is the orchestration layer.
- Chooses regular reels vs
ANTEvsBB_Trigger - Generates the initial reel view
- Calls
logic.spin() - If the spin is a non-winning base or ante spin without a nudge, it may apply
applySymbolConversion()and run the spin again
games/frostedmarketbonanza/src/logic.ts
This is the heart of the game.
- Runs cluster detection
- Calculates cluster payouts
- Handles cascade refill
- Checks scatter triggers
- Evaluates nudge feature
- Manages free-spin flow and bomb multipliers
- Applies win cap logic
games/frostedmarketbonanza/src/symbol-conversion.ts
This file defines the passive conversion feature.
- Chooses a low symbol and a high symbol from configured weights
- Counts how many appear
- Triggers only if enough low/high symbols exist and the trigger roll says
Yes - Replaces the chosen low symbol with the chosen high symbol
4. High-Level Game Architecture
The actual round flow is roughly:
Request
↓
Platform plugin entry (spin / feature)
↓
Engine selects math and reel strips
↓
Logic evaluates spin / free-spin state
↓
Cluster detection and scatter checks
↓
Feature evaluation (nudge, conversion, free spins)
↓
Win cap and total win calculation
↓
Response object
flowchart TD
A[Incoming spin request] --> B[FrostedMarketBonanzaPlugin.spin]
B --> C[FrostedMarketBonanzaEngine.executeSpin]
C --> D[generateReelView]
D --> E[FrostedMarketBonanzaLogic.spin]
E --> F[Cluster detection + cascade refill]
F --> G[Scatter check]
G --> H{Feature trigger?}
H -->|Yes| I[Free spins / nudge / symbol conversion]
H -->|No| J[Final win cap + response]
I --> K[Update pendingFeature / featureResults]
K --> J
J --> L[ISpinOutput<SpinResult>]
For feature rounds, the flow is:
flowchart TD
A[previousSpinResult] --> B[FrostedMarketBonanzaPlugin.feature]
B --> C[FrostedMarketBonanzaEngine.executeFeature]
C --> D[FrostedMarketBonanzaLogic.runFeature]
D --> E[select free-spin reel set]
E --> F[spinFreeGame]
F --> G[cluster detection + bomb multipliers]
G --> H[update featureResults + pendingFeature]
H --> I[apply win cap]
I --> J[return updated SpinResult]
5. Request Flow
This is the real request path in the current code.
- The platform calls
FrostedMarketBonanzaPlugin.spin()in games/frostedmarketbonanza/src/plugin.ts. plugin.spin()loads the math config from the JSON file for the selectedgameMode.- It reads input fields from the SDK request, including
betAmount,isBuyBonus,devMode,extraData, andrgsMaxWin. - It builds the internal
GameSpinInputobject and passes it intoengine.executeSpin(). engine.executeSpin()selects the reel set from the JSON based onbuyBonus,ante, or default base game mode.- It calls
generateReelView(...)to create the board layout. logic.spin()runs the round evaluation.- Inside
logic.spin():- determines game type (
BG,ANTE,FG,BB_Trigger, etc.) - calculates
stakeValue = totalBet / baseBet - resolves stack symbols (
STACK1,STACK2) - groups symbols by position
- checks clusters with minimum size 8
- calculates wins from pay table
- handles cascade refill
- evaluates scatter trigger and free-spin state
- applies feature logic like nudge and symbol conversion
- determines game type (
- If the spin qualifies for the passive symbol conversion flow, the engine may run
applySymbolConversion()and a secondlogic.spin()call on the converted grid. - Finally, the result is returned as a
SpinResultand wrapped intoISpinOutput.
Important path details
- The game entry is games/frostedmarketbonanza/src/plugin.ts
- Math loading occurs in
onLoad()andgetMath() - Data shape is defined in games/frostedmarketbonanza/src/interfaces.ts
- Actual win calculation occurs in games/frostedmarketbonanza/src/logic.ts
6. Request Object
The public request enters as the SDK ISpinInput, but the actual game-specific request object inside the game is GameSpinInput in games/frostedmarketbonanza/src/interfaces.ts.
| Field | Type | Required? | Meaning | Used in |
|---|---|---|---|---|
betAmount | number | Yes | Total bet value passed into the game | FrostedMarketBonanzaPlugin.spin(), logic.spin() |
ante | boolean | No | Whether the round is using ante mode | engine.executeSpin(), logic.spin() |
buyBonus | boolean | No | Whether the round is a buy-bonus trigger spin | engine.executeSpin(), logic.spin() |
combination | number[] | No | Optional forced symbol sequence used in dev mode | generateReelView / debug tests |
devMode | boolean | No | Allows debug overrides such as forced features | plugin.ts, engine.ts |
maxWin | number | No | External max-win cap value from platform | applyWinCap() |
forceNudgeFeature | string | No | Dev-only override for nudge decision | logic.spin() |
forceNudgeReels | number | No | Dev-only override for number of nudged reels | logic.spin() |
forceSymbolConversion | boolean | No | Dev-only override for passive symbol conversion | applySymbolConversion() |
forceBonusMultiplier | number | No | Dev-only override for free-spin bomb multiplier | logic.spin() / runFeature() |
Related platform request fields
The platform request also contains fields such as:
gameModeisBuyBonusextraDatargsMaxWindevMode
These are not defined in the custom GameSpinInput interface, but they are used by the plugin before it builds the internal game input.
Feature request object
For free-spin continuation, the feature request is built as GameFeatureInput in games/frostedmarketbonanza/src/interfaces.ts:
previousSpinResultisBuyBonuscombinationselectedReelSetdevModemaxWinforceBonusMultiplier
7. Response Object
The public response is an ISpinOutput<SpinResult> from the platform, and the actual game payload is the SpinResult object from games/frostedmarketbonanza/src/interfaces.ts.
| Field | Type | Meaning | Where it is populated |
|---|---|---|---|
cascadeData | CascadeData[] | Per-cascade board information | logic.spin() |
bet | number | Original bet | logic.spin() |
totalWin | number | Total win after cluster + scatter + feature adjustments | logic.spin() and runFeature() |
baseWin | number | Base round win before feature adjustments | logic.spin() |
nextFeature | `string | null` | Feature name if trigger is pending |
pendingFeature | number | Remaining feature count or pending free spins | logic.spin(), runFeature() |
featureResults | FeatureResult[] | Free-spin feature state | logic.spin(), runFeature() |
active | boolean | Whether the game is still active | set by logic.spin() and runFeature() |
multiplier | number | Total bomb multiplier across the result | logic.spin() |
firstReelMultiplierValue | number | First free-spin bomb multiplier | set in logic.spin() |
newCascadeMultiplierValue | number | Added multiplier on later cascades | set in logic.spin() |
lineWin | number | The total cluster win value | set as spinResult.totalWin before scatter addition |
scatterPay | number | Scatter payout only | logic.spin() |
maxCapReached | boolean | Whether the configured max win cap triggered | applyWinCap() |
maxWin | number | Effective max-win cap value | applyWinCap() |
ante | boolean | Ante mode persisted for the feature round | logic.spin() |
reelModifiers | ReelModifier[] | Modifiers such as nudge or symbol conversion | logic.spin() |
Important response distinction
lineWin: set tototalWinbefore scatter addition, so it represents the cluster win total in this implementationscatterPay: scatter pay onlytotalWin: cluster wins + scatter pay + feature totals, then capped if neededfeatureResults[0].totalWin: cumulative free-spin total including trigger win on the first feature resultpendingFeature: remaining spins to play in feature mode
8. Game State
The main state is the SpinResult object in games/frostedmarketbonanza/src/interfaces.ts.
What is stored?
The result contains:
- current board state (
cascadeData[]) - total win and base win
- next feature to trigger
- pending free-spin count
- feature result metadata
- reel modifiers from nudge / conversion
- multiplier information
- ante mode status
Where is it created?
logic.spin()creates the initialSpinResultlogic.runFeature()updates the sameSpinResultobject to continue free-spin rounds
Where is it modified?
- cluster payouts are added within the cascade loop in
logic.spin() - scatter triggers update
nextFeatureandpendingFeature - nudge adds to
reelModifiers - symbol conversion adds to
reelModifiersand rewritescascadeData - free spins update
featureResults[0]and decrementpendingFeature
Is it persisted?
The code does not persist the round state to a database in this game project. Instead, it keeps the state in-memory as previousSpinResult and passes it to the feature call.
In practice:
plugin.spin()returns the state to the callerplugin.feature()receivespreviousSpinResult- the feature call continues from the previous result
Free-spin state representation
Free-spin state is represented by:
nextFeature = 'freespins'on triggerpendingFeaturestores the number of spins still to be processedfeatureResults[0]stores the aggregate feature statefeatureResults[0].currentCounttracks how many free spins have runfeatureResults[0].totalWintracks cumulative free-spin total
Cascade state representation
Each entry in cascadeData includes:
reelViewspinWinscatterCountscatterPositionsscatterPayblustSymbolsmultiplierPositionsbombTotalMultiplier
Feature state transition
flowchart TD
A[Initial spin] --> B[Cluster win or scatter trigger]
B --> C{Trigger free spins?}
C -->|No| D[Round complete]
C -->|Yes| E[Set nextFeature = freespins]
E --> F[Create featureResults[0]]
F --> G[Execute feature spin]
G --> H{pendingFeature > 0?}
H -->|Yes| I[Continue feature]
H -->|No| J[Close feature]
I --> G
J --> K[Final result]
9. Math File — Most Important Section
The math engine is in games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json, and the rules are executed in games/frostedmarketbonanza/src/logic.ts.
Function: FrostedMarketBonanzaLogic.spin()
Purpose
This is the main round evaluator. It decides the board, cluster wins, cascades, scatter trigger, and the feature state for a base game or feature game.
Input
totalBet— total bet amountmath— full JSON game configurationreelView— current boardisFreeGame— free-spin flagbuyBonus— buy-bonus flagante— ante flagbombWeights— free-spin bomb weightsmaxWin— external max win capforceNudgeFeature— dev overrideforceNudgeReels— dev overrideforceBonusMultiplier— dev override
Process
- choose game type (
BG,ANTE,FG,BB_Trigger,ANTE_FG,BB) - determine base bet and
stakeValue - resolve stack symbols
- count symbol positions in the grid
- detect clusters for non-
SCand non-BNsymbols with minimum size8 - calculate cluster wins from
math.payTable - apply multiplier bombs during free games
- refill the board and continue cascade loop
- evaluate scatter trigger and free-spin feature trigger
- apply win cap using
applyWinCap() - clean bomb suffixes from reel values before returning
Output
Returns a SpinResult with totalWin, scatterPay, nextFeature, pendingFeature, featureResults, and the board state.
Used by
FrostedMarketBonanzaEngine.executeSpin()FrostedMarketBonanzaLogic.runFeature()callsspinFreeGame()which calls this method
Example
If a base spin has stakeValue = 1 and a cluster of H2 with size 8, then the pay table says H2 pays 50 at size 8, so the cluster win is 50.
Function: FrostedMarketBonanzaLogic.runFeature()
Purpose
This method executes the free-spin feature and updates the feature state.
Input
previousSpinResultmathbetAmountisBuyBonusselectedReelSetmaxWinforceBonusMultiplier
Process
- reads whether the round is ante-based from
previousSpinResult.ante - selects a free-spin reel set using
selectReelSet() - picks bomb weights based on the selected reel set
- calls
spinFreeGame() - appends the free-spin result to
featureResults[0].spinResults - adds the result to
featureResults[0].totalWin - handles retrigger logic with
pendingFeature - applies max-win cap to the cumulative feature total
- closes the feature when
pendingFeature <= 0or cap is reached
Output
Returns the updated SpinResult with the free-spin state and new total win.
Used by
FrostedMarketBonanzaEngine.executeFeature()
Example
Base spin triggers 10 free spins. The first free spin wins 200; a later free spin adds 500. The feature result total becomes 700 and the pendingFeature counter reduces from 10 to 9 after the spin.
Function: FrostedMarketBonanzaLogic.spinFreeGame()
Purpose
Creates the board for a free-spin round and delegates to the regular spin evaluator in feature mode.
Input
totalBetmathreelStripsbuyBonusantebombWeightsmaxWinforceBonusMultipliercombination
Process
- generates a reel view from the selected free-spin reel strips
- calls the regular
spin()method withisFreeGame: true
Output
One free-spin result.
Used by
runFeature()
Function: applySymbolConversion()
Purpose
Runs the base-game passive symbol conversion feature.
Input
reelViewmathByGameTypeforceSymbolConversion(dev only)
Process
- choose one low symbol and one high symbol from weighted arrays
- count occurrences on the board
- if there are fewer than 8 total relevant symbols, do nothing
- roll a trigger using
TriggerWeights - if triggered, replace every selected low symbol with the selected high symbol
- record original positions and board state in
reelModifiers
Output
Returns a new reel view plus SymbolConversionResult.
Used by
FrostedMarketBonanzaEngine.executeSpin()
Example
If L1 and H1 are selected and the board has L1 in 8 places, then the conversion can replace all L1 cells with H1. The code only does this when the trigger roll passes and the board qualifies.
10. Math Calculation Examples
Cluster win formula
The game uses cluster wins, not paylines.
The exact formula in code is:
const winAmount = multiply(symbolPay, stakeValue) ?? 0;
Where:
symbolPaycomes frommath.payTable[cluster.symbol][payIndex]payIndexis set byMath.min(cluster.size - 1, payTable.length - 1)stakeValue = totalBet / baseBet
Example:
- total bet =
20 - baseBet =
20 stakeValue = 1- cluster symbol =
H2 - cluster size =
8 - pay table for
H2at size 8 =50
Then:
winAmount = 50 × 1 = 50
Scatter payout formula
The code has:
const scPayouts = math.payTable[configSymbols.scatter];
scatterPay = multiply(scPayouts?.[lastScCount - 1] ?? 0, stakeValue);
From the JSON pay table:
SCvalues are[0, 0, 0, 60, 100, 2000, ...]
Examples:
4scatters ->60 × stakeValue5scatters ->100 × stakeValue6scatters ->2000 × stakeValue
Free-spin multiplier example
If the free-spin bomb weights produce 5x and 3x, the game sums them:
- total bomb multiplier =
5 + 3 = 8 - if free-spin win total is
200, then multiplied total =200 × 8 = 1600
This is exactly how the code does it:
if (totalBombMultiplier > 0 && freeSpinResult.totalWin > 0) {
freeSpinResult.totalWin = multiply(freeSpinResult.totalWin, totalBombMultiplier);
}
Max win cap example
The game uses applyWinCap() with configCapMultiplier and rgsCap:
- base game
maxWinMultiplier = 10000 - base game
maxWinBbMultiplier = 10000 - config cap uses
totalBet × 10000
Example:
- bet =
20 - max multiplier =
10000 - config cap =
20 × 10000 = 200000
If calculated win exceeds this value, the result is capped.
11. Symbol Generation
The actual symbol generation is largely driven by:
- reel strips in the JSON math file
- stack replacement logic in games/frostedmarketbonanza/src/logic.ts
- weighted random selection from the SDK helpers
Reel strips
The BG, ANTE, FG, BB, and BB_Trigger sections in the math JSON define the reel strips. The base board is 6 columns × 5 rows, and each column contains a list of symbols.
Symbol weights
This game uses weighted symbol selection for:
STACK1_WeightsSTACK2_WeightsBOMB_SET1_WeightsBOMB_SET2_WeightsReelSelectionWeightsNudgeFeature.TriggerWeightsSymbolConversionFeature.TriggerWeights
Example from the config:
STACK1_Weights:H1 10,H2 25,H3 25,H4 25STACK2_Weights:L1 15,L2 15,L3 20,L4 20,L5 18
This means the stack replacement logic chooses from a weighted set rather than a uniform random choice.
Weighted random selection example
The code uses selectWeightedRandom(...) from the SDK.
For STACK1_Weights:
H1weight =10H2weight =25H3weight =25H4weight =25
Approximate distribution:
H1≈10 / 85 ≈ 11.8%H2≈29.4%H3≈29.4%H4≈29.4%
Exact probabilities are derived from the actual weighted arrays in the JSON, not from a separate abstraction.
Special symbol generation
SCappears in reel strips and can be added by the nudge featureBNis used as the bomb multiplier symbol inside free gamesSTACK1andSTACK2are converted into high/low symbols before cluster evaluation
Symbol conversion weights
The conversion logic uses:
HighSymbolWeights:H1 10,H2 25,H3 25,H4 25LowSymbolWeights:L1 15,L2 15,L3 20,L4 20,L5 18TriggerWeights:Yes 1,No 49
This means the conversion trigger is weighted to be rare: roughly 1 in 50 if not forced by dev mode.
12. Win Evaluation
Win evaluation is cluster-based.
What counts as a win?
The game groups symbol positions with groupSymbolPositions(...) and checks them in the while (proceedNextCascade) loop.
It ignores:
SC(scatter)BN(multiplier symbol)
It then evaluates each other symbol cluster:
if (count >= minClusterSize) {
clusters.push({ symbol, positions, size: count });
}
with:
const minClusterSize = 8;
How many matching symbols are required?
At minimum, 8 positions of the same symbol are needed to form a winning cluster.
Are wins evaluated left-to-right?
No. This is not a payline or ways engine. The code groups positions by symbol and then evaluates cluster size, so the win logic is by symbol cluster, not left-to-right payline logic.
Can multiple wins happen simultaneously?
Yes. The code loops over all clusters and sums all cluster wins into cascadeWin.
for (const cluster of clusters) {
...
cascadeWin = add(cascadeWin, winAmount);
}
Wild and scatter rules
- Wild: not defined in the current code
- Scatter: counted separately and not treated as a cluster-winning symbol
- Multiplier bomb: not treated as cluster-paying symbol
Example
Suppose the board has 8 H2 symbols in a connected cluster and the stake value is 1.
H2pay table at size 8 =50- cluster win =
50
If there are also 4 scatters, the scatter pay is 60 and the total is 110 before any free-spin multiplier logic is applied.
13. Features
Feature: Nudge
Trigger
The game checks for nudge only when:
- not in free game
- game type is
BGorANTE - no cluster wins existed so far in the cascade loop
- scatter count is
3on the last cascade
What happens
The code selects a number of reels based on NudgeFeature.NumReelsWeights and optionally adds a scatter to one chosen reel. It then creates a NudgeFeatureModifier.
Relevant code
Implementation details
TriggerWeights:withScatter 37,no 64,withoutScatter 37NumReelsWeights:1 -> 100,2 -> 5,3 -> 1
This means the nudge is a weighted, rare, reel-shifting event that can optionally add a scatter to the result.
Feature: Symbol Conversion
Trigger
This is a passive base-game conversion feature. It is run only for base or ante spins when:
lineWin === 0- no nudge feature triggered
- the board qualifies by low/high symbol counts
What happens
- picks one low symbol and one high symbol from the weighted sets
- counts them on the board
- if the total count is at least 8, triggers a weighted
Yes/Noroll - if triggered, converts each low symbol to the chosen high symbol
- re-runs the spin on the converted board
Relevant code
Important note
The code comment in games/frostedmarketbonanza/src/symbol-conversion.ts describes this as passive base-game logic. The current implementation also re-runs the spin from engine.ts. There is a known bug in the conversion branch that can drop the feature trigger state when a converted board creates a free-spin trigger. This is documented in docs/superpowers/handoff/2026-09-21-frosted-market-bonanza-freespin-trigger-bug.md. The current source is the truth for behavior, including the bug.
Feature: Free Spins
Trigger
4 scatters on a base game trigger 10 free spins.
What happens
nextFeature = 'freespins'pendingFeature = 10featureResults[0]is created- in the feature loop, each free-spin result is added to the total feature result
How it ends
- when
pendingFeature <= 0 - or when max win cap is reached
Feature: Bomb multiplier in free spins
Trigger
The bomb multiplier is present in free games when a free-spin board contains BN symbols.
What happens
- the game picks from
BOMB_SET1_WeightsorBOMB_SET2_Weights - values are stored as amounts like
2x,5x,25x,100x - all multipliers in the final free-spin cascade are added into
totalBombMultiplier - the free-spin total is multiplied by that total
14. Free Spins / Bonus Game
This game has a clear free-spin feature.
| Comparison | Normal Spin | Free Spin |
|---|---|---|
| Game type | BG, ANTE, BB_Trigger | FG, ANTE_FG, BB |
| Scatter trigger | 4 scatters | 3 scatters retrigger |
| Initial spins | not applicable | 10 spins |
| Bonus multiplier | not present | BN bomb multipliers can appear |
| Net win cap | config max win cap per base game | same cap by config but applied to cumulative feature total |
| State storage | SpinResult only | featureResults[0] + pendingFeature |
Trigger logic
In base game, the trigger is:
if (lastScCount >= fsTriggerCount) {
spinResult.nextFeature = 'freespins';
...
if (isFreeGame) {
spinResult.pendingFeature = features.freeSpinsRetrigger.additionalSpins;
} else {
spinResult.pendingFeature = features.freeSpinsTrigger.initialCount;
}
}
with:
fsTriggerCount = features.freeSpinsTrigger.minScatterswhen not free gamefsTriggerCount = features.freeSpinsRetrigger.minScatterswhen free game
Free-spin state
The feature is stored in:
previousSpinResult.featureResults[0]previousSpinResult.pendingFeaturepreviousSpinResult.nextFeature
The game decrements pendingFeature after each free-spin result.
Free-spin math change
The free-spin game uses separate reel strips and different ReelSelectionWeights, BOMB_SET weights, and different RallyNonRallyCascadeWeights arrays. The code also allows bomb multipliers to change the total free-spin win.
End condition
The free-spin feature ends when:
pendingFeature <= 0, or- the max-win cap was reached
At that point, featureResults[0].closed = true.
15. Cascade / Respin / Special Round Flow
This game does not use a traditional respin mechanic. It uses a cascade loop.
The actual pattern is:
- evaluate current board
- find clusters
- if cluster wins exist, remove winning symbols
- cascade refill occurs with
cascadeRefillReelView() - continue evaluating until no winning cluster remains
- after the cascade loop ends, check scatters and feature triggers
flowchart TD
A[Initial reel view] --> B[Group symbols]
B --> C[Find clusters >= 8]
C --> D{Any cluster win?}
D -->|No| E[Check scatter trigger]
D -->|Yes| F[Add cluster win to cascade]
F --> G[Remove winning symbols]
G --> H[Refill board]
H --> B
E --> I[Feature trigger or round end]
This matches the implementation in logic.spin().
16. Complete End-to-End Example
Here is a simplified example based on the actual math and implementation.
Request
{
"betAmount": 20,
"gameMode": "R3",
"devMode": false,
"extraData": {}
}
This is the request sent to the platform plugin.
Step 1: plugin builds input
FrostedMarketBonanzaPlugin.spin() reads betAmount = 20 and gameMode = R3, loads the JSON math, and prepares the internal GameSpinInput.
Step 2: engine creates board
engine.executeSpin() checks the base game and picks math.BG.ReelSet_1.
Then it calls generateReelView() with:
- rows =
5 - columns =
6
This creates a 6 × 5 reel view.
Step 3: logic evaluates spin
logic.spin() determines:
gameType = 'BG'baseBet = 20stakeValue = 20 / 20 = 1
It resolves stack symbols and then groups symbols by position. Suppose the board contains 8 H2 symbols and 4 SC symbols.
Step 4: cluster evaluation
The cluster detector sees H2 count = 8 and SC count = 4.
The game ignores SC for cluster win purposes, but it does count cluster wins for H2.
From the pay table:
H2pay at size 8 =50- cluster win =
50 × 1 = 50
Step 5: scatter evaluation
Scatter trigger check sees lastScCount = 4 and the trigger threshold is 4.
The code sets:
nextFeature = 'freespins'pendingFeature = 10scatterPay = 60 × 1 = 60
Step 6: total win calculation
- cluster win =
50 - scatter win =
60 - total win =
110
The result is then stored in the SpinResult, and the game is marked as active because feature is pending.
Step 7: free-spin state created
The featureResults[0] entry is created with:
featureType = 'freespins'totalCount = 10closed = false
Step 8: response
The platform returns a response similar to:
{
"totalWin": 110,
"featureTriggered": true,
"gameData": {
"nextFeature": "freespins",
"pendingFeature": 10,
"totalWin": 110,
"scatterPay": 60,
"featureResults": [
{
"featureType": "freespins",
"totalCount": 10,
"closed": false
}
]
}
}
This example is realistic in terms of structure, but it is intentionally constructed from the actual values present in the code.
17. Code-to-Game Mapping
| Game concept | Code location |
|---|---|
| Game registration | games/frostedmarketbonanza/project.json |
| Platform entry | games/frostedmarketbonanza/src/plugin.ts |
| Spin orchestration | games/frostedmarketbonanza/src/engine.ts |
| Core round logic | games/frostedmarketbonanza/src/logic.ts |
| Request/result contracts | games/frostedmarketbonanza/src/interfaces.ts |
| Cluster detection | groupSymbolPositions(...) called in logic.spin() |
| Scatter trigger | lastScCount >= fsTriggerCount in logic.spin() |
| Free-spin trigger | nextFeature = 'freespins' in logic.spin() |
| Nudge feature | NudgeFeature weights and nudge block in logic.spin() |
| Symbol conversion | games/frostedmarketbonanza/src/symbol-conversion.ts |
| Multiplier bombs | free-spin BN handling in logic.spin() |
| Win cap | applyWinCap() call in logic.spin() and runFeature() |
| Response generation | plugin.spin() and plugin.feature() |
| Simulation analysis | tools/game-tools/…/frosted-market-bonanza.analyzer.ts |
18. Important Functions
| File | Function / class | Purpose | Called by | Why it matters |
|---|---|---|---|---|
| games/frostedmarketbonanza/src/plugin.ts | FrostedMarketBonanzaPlugin.spin() | Main platform entry for a base spin | platform loader | Builds request and returns response |
| games/frostedmarketbonanza/src/plugin.ts | FrostedMarketBonanzaPlugin.feature() | Continues the feature round | platform loader | Handles free spins |
| games/frostedmarketbonanza/src/engine.ts | FrostedMarketBonanzaEngine.executeSpin() | Build board and orchestrate spin | plugin spin | Chooses mode and reel set |
| games/frostedmarketbonanza/src/engine.ts | FrostedMarketBonanzaEngine.executeFeature() | Starts feature execution | plugin feature | Calls the feature logic |
| games/frostedmarketbonanza/src/logic.ts | FrostedMarketBonanzaLogic.spin() | Core game evaluator | engine and free-spin runner | Determines wins, feature triggers |
| games/frostedmarketbonanza/src/logic.ts | FrostedMarketBonanzaLogic.runFeature() | Updates feature totals and continue state | engine executeFeature | Controls free-spin progression |
| games/frostedmarketbonanza/src/logic.ts | FrostedMarketBonanzaLogic.spinFreeGame() | Generates a free-spin board | runFeature | Ensures free-spin board is created |
| games/frostedmarketbonanza/src/symbol-conversion.ts | applySymbolConversion() | Converts low symbols to high symbols | engine executeSpin | Passive reuse feature |
| games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json | JSON config | Contains all reel, pay, and feature data | plugin onLoad | Defines the actual game rules |
19. Configuration
All the actual game configuration is in games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json.
Symbol configuration
symbolsMapdefines raw names to internal names- internal symbols include
H1,H2,H3,H4,L1,L2,L3,L4,L5,SC,BN,STACK1,STACK2
Pay table
The payTable contains cluster payout values by symbol and cluster size.
Examples:
H1:[0, 0, ..., 200, 200, 500, 500, 1000]H2:[0, 0, ..., 50, 50, 200, 200, 500]SC:[0, 0, 0, 60, 100, 2000, ...]
Reel configuration
Each mode (BG, ANTE, FG, BB, BB_Trigger) has its own ReelSet_1 and sometimes ReelSet_2 values. The board size is fixed as 6 columns × 5 rows.
Weights
Weighted arrays determine:
- stack symbol replacement
- reel-set selection
- free-spin bomb values
- nudge trigger decisions
- symbol conversion trigger
Bet configuration
games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json defines:
baseBet = 20bbBaseBet = 20anteBaseBet = 25
Feature configuration
features.freeSpinsTrigger.minScatters = 4features.freeSpinsTrigger.initialCount = 10features.freeSpinsRetrigger.minScatters = 3features.freeSpinsRetrigger.additionalSpins = 5
Max win configuration
maxWinMultiplier = 10000maxWinBbMultiplier = 10000
Where config is loaded
The config is loaded in games/frostedmarketbonanza/src/plugin.ts inside onLoad().
20. Error Handling and Validation
Invalid request
The code path does not implement a large custom validation layer for malformed requests. The game relies on the platform contract and the GameSpinInput types.
The plugin does do basic checks around math availability:
if (!m) {
throw new Error(
`[FrostedMarketBonanza] Math not loaded for mode "${mode}"...`
);
}
This is in games/frostedmarketbonanza/src/plugin.ts.
Invalid bet
There is no explicit bet-validation function in the game. The code expects betAmount to be numeric and then computes stakeValue = totalBet / baseBet.
Invalid game state
The free-spin feature uses previousSpinResult and expects a valid SpinResult with pendingFeature and featureResults. If those are absent or malformed, the code will behave unpredictably. The current implementation does not contain a dedicated guard against that.
Invalid feature state
The code checks pendingFeature and closes the feature when it reaches zero or when the max cap is reached. This is the main guard in runFeature().
Important exceptions
The main explicit exception is the missing math error thrown in getMath().
Validation location
Validation is light and mostly occurs in:
21. External Dependencies
| Dependency | Why it is used | Which file uses it | Does it affect outcome? |
|---|---|---|---|
@slot-platform/sdk | Core game platform types and helpers | game files | Yes, indirectly |
@slot-platform/sdk/critical/reel-generator | Generates board layout and reel view | games/frostedmarketbonanza/src/engine.ts, games/frostedmarketbonanza/src/logic.ts | Yes |
@slot-platform/sdk/critical/cluster-detector | Detects symbol clusters | games/frostedmarketbonanza/src/logic.ts | Yes |
@slot-platform/sdk/critical/cascade-refill | Refills the board after wins | games/frostedmarketbonanza/src/logic.ts | Yes |
@slot-platform/sdk/critical/generic-logic | Weighted random selection | games/frostedmarketbonanza/src/logic.ts, games/frostedmarketbonanza/src/symbol-conversion.ts | Yes |
@slot-platform/sdk/critical/rng | Random number generation | games/frostedmarketbonanza/src/logic.ts | Yes |
@slot-platform/sdk/critical/reel-set-selector | chooses free-spin reel set | games/frostedmarketbonanza/src/logic.ts | Yes |
@slot-platform/sdk/critical/win-cap | Applies max-win cap | games/frostedmarketbonanza/src/logic.ts | Yes |
The platform SDK is not just infrastructure; it is central to the actual game logic and math execution.
22. Testing
There are no dedicated game unit tests in the game folder itself. The code is exercised through simulation and certification tooling, especially in:
- tools/game-tools/src/executors/simulate/analyzers/games/frosted-market-bonanza.analyzer.ts
- tools/game-tools/src/executors/ezu-dataset/serializers/games/frosted-market-bonanza.serializer.ts
What these tools test
The analyzer tracks:
- cluster wins
- scatter pay wins
- free-spin win totals
- bomb multiplier distribution
- nudge triggers
- symbol conversion triggers
- win distribution and reel-stop frequency
This is important because it confirms which features are actually present in the math and logic flow.
Important test insight
There is a known issue documented in docs/superpowers/handoff/2026-09-21-frosted-market-bonanza-freespin-trigger-bug.md: some converted-grid scatter triggers are being silently dropped because engine.executeSpin() copies only part of the converted result back into the original spin result.
This means the simulator and cert tooling are important because they reveal logic bugs that are not obvious from the code alone.
23. Things a New Developer Should Be Careful About
Here are the highest-risk areas in the current implementation.
- Changing
minClusterSizein games/frostedmarketbonanza/src/logic.ts changes the win threshold and dramatically changes all results. - Changing
math.payTablevalues changes cluster payouts and scatter payouts. - Changing
symbolsMapcan break both the raw display mapping and the internal pay calculations. - Changing
ReelSelectionWeightschanges free-spin reel-set selection probabilities. - Changing
BOMB_SET1_WeightsandBOMB_SET2_Weightschanges free-spin multiplier distribution. - Altering
TriggerWeightsfor nudge or conversion changes feature frequency. - Editing
pendingFeaturehandling inrunFeature()changes free-spin progression. - Changing
applyWinCap()parameters or the cap multipliers changes max-win behavior. - Reordering or deleting
reelModifiershas a direct effect on debugging and feature-state interpretation.
24. Debugging Guide
Use this flow to inspect one round.
1. Put breakpoints here
- games/frostedmarketbonanza/src/plugin.ts:
spin()andfeature() - games/frostedmarketbonanza/src/engine.ts:
executeSpin()andexecuteFeature() - games/frostedmarketbonanza/src/logic.ts:
spin(),runFeature(),spinFreeGame() - games/frostedmarketbonanza/src/symbol-conversion.ts:
applySymbolConversion()
2. Which function executes first?
The first real game function is FrostedMarketBonanzaPlugin.spin().
3. Where are random numbers generated?
The random choices go through:
selectWeightedRandom(...)rng.randomNumber(...)selectReelSet(...)
These are called inside games/frostedmarketbonanza/src/logic.ts.
4. Where are symbols generated?
generateReelView()creates the boardreplaceStackSymbols()resolves stack symbolsapplySymbolConversion()modifies the board after a blank spin
5. Where are wins calculated?
The cluster pay calculation happens in the cascade loop within logic.spin().
6. Where are features triggered?
- scatter triggers are checked in
logic.spin() - nudge feature is checked in the cascade loop
- conversion is evaluated in
engine.executeSpin() - free-spin continuation is updated in
runFeature()
7. Where is the response created?
plugin.spin()wraps the finalSpinResultintoISpinOutput<SpinResult>plugin.feature()does the same for feature rounds
8. Useful variables to inspect
spinResult.totalWinspinResult.scatterPayspinResult.pendingFeaturespinResult.nextFeaturespinResult.featureResultsspinResult.cascadeDataspinResult.reelModifierslastScCountstakeValuetotalBombMultiplier
25. Quick Reference
GAME
- Type: cluster/cascade slot
- Reels: 6 columns
- Rows: 5
- Win system: cluster-based, not payline-based
- Wild: Not defined in current code
- Scatter:
SC - Free spins: yes
- Main features: scatter trigger, nudge, symbol conversion, free-spin bomb multipliers
MATH ENTRY
GAME LOGIC ENTRY
REQUEST
GameSpinInputin games/frostedmarketbonanza/src/interfaces.ts- Entry point:
FrostedMarketBonanzaPlugin.spin()
RESPONSE
SpinResultin games/frostedmarketbonanza/src/interfaces.ts- Public wrapper:
ISpinOutput<SpinResult>
IMPORTANT FILES
- games/frostedmarketbonanza/src/plugin.ts
- games/frostedmarketbonanza/src/engine.ts
- games/frostedmarketbonanza/src/logic.ts
- games/frostedmarketbonanza/src/symbol-conversion.ts
- games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json
IMPORTANT FUNCTIONS
FrostedMarketBonanzaPlugin.spin()FrostedMarketBonanzaPlugin.feature()FrostedMarketBonanzaEngine.executeSpin()FrostedMarketBonanzaLogic.spin()FrostedMarketBonanzaLogic.runFeature()applySymbolConversion()
26. Things That Are Not Clear From The Code
This section lists items that cannot be confidently determined from the current implementation.
- Exact RTP: Not clearly identifiable from the current code. The math file includes weights and payout tables, but no explicit
RTPfield is exposed in the game JSON or plugin layer. Relevant files: games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json, games/frostedmarketbonanza/src/plugin.ts. - Exact volatility model: Not clearly identifiable from the current code. The code exposes weighted probabilities and wins, but no explicit volatility label or formula. Relevant files: same math JSON and logic.
- Exact business meaning of all bracket / rally weights: The code passes
RallyNonRallyCascadeWeights,RallyBracketsWeights, andNonRallyBracketsWeightsintocascadeRefillReelView(), but the underlying SDK helper is not fully explained in this game project. Relevant file: games/frostedmarketbonanza/src/logic.ts. - Exact display symbolism of
STACK1/STACK2: The code creates them and resolves them, but their final UI display connection is not fully visible in this repository. Relevant files: games/frostedmarketbonanza/src/logic.ts, games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json. - Exact cause of some free-spin trigger edge cases: The code contains a known bug around symbol conversion and dropped feature state, documented here: docs/superpowers/handoff/2026-09-21-frosted-market-bonanza-freespin-trigger-bug.md.
This list should be treated as a caution list for future debugging, not as a set of missing game rules.
Final Summary
Frosted Market Bonanza is a 6×5 cluster/cascade slot. Its core logic is:
- build a board
- find symbol clusters of size 8 or more
- pay by cluster size from the pay table
- remove winning symbols and refill the board
- trigger free spins at 4 scatters in base game
- continue free spins with bomb multipliers
- optionally apply nudge or symbol conversion on certain non-winning spins
The most important implementation files are:
- games/frostedmarketbonanza/src/plugin.ts
- games/frostedmarketbonanza/src/engine.ts
- games/frostedmarketbonanza/src/logic.ts
- games/frostedmarketbonanza/src/symbol-conversion.ts
- games/frostedmarketbonanza/src/maths/frosted-market-bonanza-R3.json
The code is the source of truth. If a behavior is debated between documentation and source, the actual implementation in the source files should be treated as authoritative.