Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/StakeEngine/math-sdk/llms.txt

Use this file to discover all available pages before exploring further.

Prerequisites

  • Rust and Cargo installed (used to build and run the optimizer binary).
  • A completed simulation run with lookup tables already generated. For production games, 100,000+ simulations per mode are recommended to ensure a diverse payout distribution and reduce the chance of repeated round results.

Overview

OptimizationSetup is a per-game class (defined in game_optimization.py) that takes a GameConfig instance and builds the opt_params dictionary. OptimizationExecution then reads those params and invokes the Rust binary for each mode.

The opt_params structure

For each bet mode, opt_params requires three keys:
opt_params = {
    "<mode_name>": {
        "conditions": { ... },
        "scaling":    [ ... ],
        "parameters": { ... },
    }
}
Each key has a corresponding construction class in optimization_program/optimization_config.py.

Setup steps

1

Define conditions with ConstructConditions

ConstructConditions partitions simulations by win type and sets the RTP target for each partition.Each condition requires 2 of the 3 variables: rtp, av_win (average win), and hr (hit-rate). The third is derived automatically.The 0-win condition is a special case: its hit-rate can be left as a free variable (pass "x" or omit it) because all hit-rates across all conditions must sum to exactly 1, so the 0-win hit-rate can be deduced from the remainder.
from optimization_program.optimization_config import ConstructConditions

"conditions": {
    # wincap must come first — these sims are also freegame sims
    "wincap": ConstructConditions(
        rtp=0.01,
        av_win=wincaps["base"],
        search_conditions=wincaps["base"],
    ).return_dict(),
    "freegame": ConstructConditions(
        rtp=0.37,
        hr=200,
        search_conditions={"symbol": "scatter"},
    ).return_dict(),
    # 0-win: hit-rate is a free variable
    "0": ConstructConditions(
        rtp=0,
        av_win=0,
        search_conditions=0,
    ).return_dict(),
    "basegame": ConstructConditions(
        hr=3.5,
        rtp=0.59,
    ).return_dict(),
}
The search_conditions argument tells the optimizer which simulation IDs belong to this condition:
  • A number matches simulations with that exact payout.
  • A tuple (min, max) matches simulations within that payout range.
  • A dict (e.g., {"symbol": "scatter"}) matches by recorded event data in the force records.
2

Configure scaling with ConstructScaling

ConstructScaling biases specific win ranges during trial distribution generation. Each entry requires:
KeyTypeDescription
criteriastrWhich condition to apply the bias to
win_rangetuple(min, max) payout range to bias
scale_factorfloatMultiplier applied to Gaussian weights in this range
probabilityfloatProbability (0–1) that this scaling is applied per trial distribution
from optimization_program.optimization_config import ConstructScaling

"scaling": ConstructScaling(
    [
        {"criteria": "basegame", "scale_factor": 1.2, "win_range": (1, 2),     "probability": 1.0},
        {"criteria": "basegame", "scale_factor": 1.5, "win_range": (10, 20),   "probability": 1.0},
        {"criteria": "freegame", "scale_factor": 0.8, "win_range": (1000, 2000), "probability": 1.0},
        {"criteria": "freegame", "scale_factor": 1.2, "win_range": (3000, 4000), "probability": 1.0},
    ]
).return_dict()
Large scale factors significantly reduce the probability that a randomly generated trial distribution will be accepted, so use them conservatively.
3

Set run parameters with ConstructParameters

ConstructParameters defines the optimizer’s computational budget and volatility bounds.
from optimization_program.optimization_config import ConstructParameters

"parameters": ConstructParameters(
    num_show=5000,           # number of final candidate distributions to retain
    num_per_fence=10000,     # number of trial distributions to generate per batch
    min_m2m=4,               # minimum mean-to-median ratio (lower bound on volatility)
    max_m2m=8,               # maximum mean-to-median ratio (upper bound on volatility)
    pmb_rtp=1.0,             # RTP weighting for scoring
    sim_trials=5000,         # number of distributions evaluated before combination
    test_spins=[50, 100, 200],      # spin counts used to rank viable distributions
    test_weights=[0.3, 0.4, 0.3],  # sampling weights for each test-spin count
    score_type="rtp",        # ranking metric
).return_dict()
The min_m2m / max_m2m bounds control volatility: a higher mean-to-median ratio means the distribution has a heavier tail relative to its median, producing a more volatile game.
4

Instantiate OptimizationSetup

Create the OptimizationSetup class in your run.py. It attaches opt_params to the GameConfig and validates that the conditions match the configured bet modes.
from game_optimization import OptimizationSetup

config = GameConfig()
optimization_setup_class = OptimizationSetup(config)
# config.opt_params is now populated and validated
5

Run the optimizer with OptimizationExecution

Pass the configured GameConfig and the list of modes you want to optimize to OptimizationExecution.run_all_modes().
from optimization_program.run_script import OptimizationExecution

optimization_modes_to_run = ["base", "bonus"]
OptimizationExecution().run_all_modes(config, optimization_modes_to_run, rust_threads)
This generates a setup.toml for each mode and invokes the Rust binary via cargo run --release. Optimized lookup tables are written to library/lookup_tables/lookUpTable_<mode>_0.csv.

Complete example (run.py)

The following is the full run.py from the 0_0_lines sample game, showing how all pieces connect:
"""Main file for generating results for sample lines-pay game."""

from gamestate import GameState
from game_config import GameConfig
from game_optimization import OptimizationSetup
from optimization_program.run_script import OptimizationExecution
from utils.game_analytics.run_analysis import create_stat_sheet
from utils.rgs_verification import execute_all_tests
from src.state.run_sims import create_books
from src.write_data.write_configs import generate_configs

if __name__ == "__main__":

    num_threads = 10
    rust_threads = 20
    batching_size = 5000
    compression = True
    profiling = False

    num_sim_args = {
        "base":  int(1e4),
        "bonus": int(1e4),
    }

    run_conditions = {
        "run_sims":          True,
        "run_optimization":  True,
        "run_analysis":      True,
        "run_format_checks": True,
    }
    target_modes = list(num_sim_args.keys())

    config = GameConfig()
    gamestate = GameState(config)
    if run_conditions["run_optimization"] or run_conditions["run_analysis"]:
        optimization_setup_class = OptimizationSetup(config)

    if run_conditions["run_sims"]:
        create_books(
            gamestate, config, num_sim_args, batching_size, num_threads, compression, profiling
        )

    generate_configs(gamestate)

    if run_conditions["run_optimization"]:
        OptimizationExecution().run_all_modes(config, target_modes, rust_threads)
        generate_configs(gamestate)

    if run_conditions["run_analysis"]:
        custom_keys = [{"symbol": "scatter"}]
        create_stat_sheet(gamestate, custom_keys=custom_keys)

    if run_conditions["run_format_checks"]:
        execute_all_tests(config)
Set run_optimization: True in run_conditions to enable the optimization step. Set it to False to skip optimization and only run simulations or analysis.

Next steps

Optimization algorithm

Understand how the iterative weighted sampling algorithm works under the hood.

Game analysis

Generate a PAR sheet and analyze the optimized win distribution.

Build docs developers (and LLMs) love