Skip to main content
Every game mode — base game, free spin purchase, bonus buy — is defined as a BetMode instance in config.bet_modes. Bet modes control how much a spin costs, what RTP it targets, what the maximum win is, and — crucially — how simulation outcomes are pre-allocated to ensure the game hits its mathematical targets.

The BetMode class

game_config.py

BetMode fields

string
required
Unique identifier for this bet mode. Used as the key in num_sim_args in run.py and referenced throughout the library output.
number
required
Bet cost multiplier. A value of 1.0 means the player bets 1 unit. A bonus-buy mode might use 100.0 to indicate a 100x stake purchase.
number
required
Target RTP for this mode, typically self.rtp. The optimization algorithm uses this value to weight simulations appropriately.
number
required
Maximum win multiplier for this mode. Usually self.wincap. The engine caps all wins at this value.
boolean
default:"false"
When False (default), the RGS automatically calls /endround to close the bet once the round completes. Set to True for bonus modes where a player might resume an interrupted session — in that case the frontend must close the round manually.
boolean
default:"false"
When True, the frontend preserves the current bet mode without requiring player interaction between rounds. Useful for “feature” modes that automatically repeat until complete.
boolean
default:"false"
Signals to the frontend that this mode was purchased directly (a buy-bonus or bonus-buy feature). The frontend may use this to switch asset sets or apply regulatory restrictions.
object[]
required
List of Distribution instances defining how simulation outcomes are pre-assigned. See Distributions below.

Distributions

Each BetMode contains a list of Distribution objects. A distribution assigns a proportion of simulations to a named criteria bucket, and specifies the conditions (reel weights, forcing flags, custom parameters) that should apply to simulations in that bucket. This mechanism lets you control the hit-rate and RTP contribution of specific game events — max-win spins, free-spin entry, zero-win spins — without relying on random chance alone.
game_config.py

Distribution fields

string
required
A short name identifying this win condition — for example "winCap", "freegame", "basegame", or "0". This value appears in the lookup table output files to identify which bucket each simulation belongs to.
number
required
The fraction of simulations (as a proportion of the total for this bet mode) that should be assigned to this criteria. Quotas are normalised automatically, so they do not need to sum to exactly 1. A minimum of 1 simulation is always assigned per criteria, regardless of quota size.
object
required
A dict of conditions that apply to simulations in this bucket. Read at runtime using get_distribution_conditions(). Must include reel_weights. See Conditions keys below.
number
Optional expected payout multiplier for simulations in this bucket. When set, check_repeat() verifies the final win matches this value. Use 0.0 to enforce zero-win simulations and self.wincap to enforce max-win simulations. Defaults to None (no win constraint).

Conditions keys

The conditions dict can contain any custom keys your run_spin() reads via get_distribution_conditions(). The three built-in keys are: Custom keys are passed through unchanged and can hold any value:
game_config.py

Reading conditions at runtime

In run_spin() or run_freespin(), read the active distribution’s conditions using get_distribution_conditions():
gamestate.py
This is the primary mechanism for adapting game logic based on the known expected outcome of a simulation — for example, drawing from a multiplier distribution weighted toward high values when the simulation is pre-assigned to the winCap criteria.

check_repeat() and win verification

At the end of run_spin(), check_repeat() verifies that the completed simulation satisfies its pre-assigned criteria:
  • If win_criteria is None: no win constraint, check_repeat() always passes.
  • If win_criteria is 0.0: the final win must be exactly 0.0.
  • If win_criteria is self.wincap: the final win must equal the wincap.
If the criteria are not met, self.repeat is set back to True and the simulation is re-run from reset_book() with the same random seed.

Full BetMode example

The following example is taken from the sample lines game. It defines a bonus buy mode with two distributions: a small quota of wincap simulations and a larger quota of regular freegame simulations.
game_config.py
And the base game mode with four distributions controlling zero-win, basegame-win, freegame, and wincap hit-rates:
game_config.py
The order of distributions matters for exclusive simulation assignment. Simulations are assigned to criteria buckets in order, and a given simulation number belongs to exactly one criteria bucket. Place more restrictive or rare criteria (like winCap) first so that their simulations are reserved before the larger buckets are filled.

Quota normalisation

Quotas are normalised to sum to 1 internally, so you do not need to ensure they add up exactly. For example, quotas of 0.001, 0.1, 0.4, and 0.5 are valid — they sum to 1.001, which is fine. The engine also guarantees a minimum of 1 simulation per criteria bucket, regardless of how small the quota is.

Game Configuration

Add BetMode instances to config.bet_modes.

Implementing GameState

Use get_distribution_conditions() in run_spin().

Concepts: simulation lifecycle

Understand how simulation numbers map to criteria buckets.

Config API reference

Full reference for the Config base class.