Optimization Guide¶
The Math SDK includes a Rust-based genetic algorithm for optimizing win distributions to meet target RTP (Return to Player), hit rates, and other constraints.
Overview¶
The optimization algorithm: - Adjusts win probabilities and multiplier distributions - Uses genetic algorithms to search for optimal configurations - Can target specific RTP, hit rate, and average win constraints - Scales specific win ranges to fine-tune distributions
Setup¶
1. Install Rust/Cargo¶
# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Verify installation
cargo --version
2. Build Optimization Program¶
Configuration¶
Optimization is configured in each game's game_optimization.py file:
Basic Structure¶
from optimization_program.optimization_config import (
ConstructConditions,
ConstructScaling,
ConstructParameters,
)
class OptimizationSetup:
def __init__(self, game_config):
self.game_config = game_config
self.game_config.opt_params = {
"base": { # Bet mode name
"conditions": {...},
"scaling": {...},
"parameters": {...}
}
}
Conditions¶
Define target metrics for different game phases:
"conditions": {
# Base game conditions
"base_game": ConstructConditions(
hr=3.5, # Target hit rate (% of spins with wins)
rtp=0.59 # Target RTP contribution from base game
).return_dict(),
# Free game conditions
"free_game": ConstructConditions(
rtp=0.37, # Target RTP contribution from free games
hr=200, # Target hit rate (per 10k spins)
search_conditions={"symbol": "scatter"} # Trigger condition
).return_dict(),
}
ConstructConditions Parameters¶
hr(float): Target hit rate- For base game: percentage (e.g., 3.5 = 3.5%)
- For features: occurrences per 10k spins (e.g., 200 = 200 times)
rtp(float): Target RTP contribution (e.g., 0.59 = 59%)average_win(float, optional): Target average win when feature hitssearch_conditions(dict, optional): Conditions to identify the feature{"symbol": "scatter"}- Look for scatter symbol{"event_type": "triggerFreeSpins"}- Look for specific event
Scaling¶
Adjust specific win ranges:
"scaling": ConstructScaling([
# Scale wins between 10x-20x by factor of 1.5
{"min": 10, "max": 20, "scale": 1.5},
# Scale wins above 100x by factor of 0.8
{"min": 100, "max": float('inf'), "scale": 0.8},
]).return_dict()
This is useful for: - Increasing frequency of mid-range wins - Reducing extreme wins - Balancing volatility
Parameters¶
Optimization algorithm settings:
"parameters": ConstructParameters(
num_show=5000, # Number of top outcomes to display
num_per_fence=10000, # Population size per optimization group
test_spins=[50, 100, 200], # Test with these spin counts
score_type="rtp" # Optimization scoring method
).return_dict()
ConstructParameters Options¶
num_show: How many outcomes to show in resultsnum_per_fence: Population size (larger = more thorough but slower)test_spins: Spin counts to test optimized distributionsscore_type: What to optimize for"rtp"- Target RTP accuracy"hr"- Target hit rate accuracy"balanced"- Balance both
Complete Example¶
# games/my_game/game_optimization.py
from optimization_program.optimization_config import (
ConstructConditions,
ConstructScaling,
ConstructParameters,
)
class OptimizationSetup:
def __init__(self, game_config):
self.game_config = game_config
self.game_config.opt_params = {
"base": {
"conditions": {
# Base game: 3.5% hit rate, contributes 59% RTP
"base_game": ConstructConditions(
hr=3.5,
rtp=0.59
).return_dict(),
# Free spins: triggers 200 times per 10k spins, contributes 37% RTP
"free_game": ConstructConditions(
rtp=0.37,
hr=200,
search_conditions={"symbol": "scatter"}
).return_dict(),
# Big wins: average 50x when hitting 100x+
"bigwin": ConstructConditions(
average_win=50,
search_conditions={"min_win": 100}
).return_dict(),
},
"scaling": ConstructScaling([
# Increase mid-range wins (10x-50x)
{"min": 10, "max": 50, "scale": 1.3},
# Reduce very high wins (500x+)
{"min": 500, "max": float('inf'), "scale": 0.7},
]).return_dict(),
"parameters": ConstructParameters(
num_show=5000,
num_per_fence=20000, # Larger for better accuracy
test_spins=[100, 500, 1000],
score_type="balanced"
).return_dict()
}
}
)
Running Optimization¶
Method 1: Via run.py¶
Edit games/<game_name>/run.py:
from optimization_program.run_script import OptimizationExecution
# Load game and config
game_state = GameState()
from games.my_game.game_optimization import optimization_setup
# Run optimization
OptimizationExecution(
game_id="my_game",
game_state=game_state,
bet_modes=["base"], # Bet modes to optimize
run_sims=False, # Use existing books (set True to regenerate)
run_optimization=True, # Enable optimization
optimization_setup=optimization_setup
)
Then run:
Method 2: Separate Optimization Run¶
- Generate books first:
- Then optimize:
Optimization Flow¶
- Load Books: Reads existing simulation results
- Parse Setup: Loads
game_optimization.pyconfig - Write Setup File: Generates
optimization_program/src/setup.txt - Run Rust Optimizer: Executes genetic algorithm
- Generate Results:
- Optimized probability distributions
- Updated CSV files
- Statistics and analysis
- Validate: Tests optimized distributions against targets
Output Files¶
After optimization:
games/<game_name>/build/optimization_files/
├── optimized_base_probs.csv # Optimized probabilities
├── optimization_report.json # Detailed statistics
└── optimization_results/ # Intermediate results
├── generation_*.csv
└── best_candidates.json
Interpreting Results¶
Optimization Report¶
{
"target_rtp": 0.97,
"achieved_rtp": 0.9698,
"target_base_hr": 3.5,
"achieved_base_hr": 3.48,
"target_free_game_hr": 200,
"achieved_free_game_hr": 198,
"generations": 150,
"best_score": 0.0023
}
Score: Lower is better (deviation from targets) - < 0.01: Excellent match - 0.01-0.05: Good match - > 0.05: May need adjustment
Common Issues¶
RTP too high/low - Adjust scaling factors - Modify paytable multipliers - Check base game win frequencies
Hit rate off target - Adjust trigger symbol distribution - Modify win condition thresholds - Check scatter placement in reels
Optimization not converging
- Increase num_per_fence for larger population
- Adjust test_spins for more accurate testing
- Simplify conditions (fewer constraints)
Advanced Techniques¶
Multi-Stage Optimization¶
Optimize different aspects separately:
# Stage 1: Base game RTP
"base_game": ConstructConditions(rtp=0.60).return_dict()
# Stage 2: Free game frequency
"free_game": ConstructConditions(hr=200).return_dict()
# Stage 3: Fine-tune both
"base_game": ConstructConditions(hr=3.5, rtp=0.59).return_dict()
Conditional Scaling¶
Different scaling for different game states:
# In base game: scale medium wins up
"base_scaling": ConstructScaling([
{"min": 5, "max": 20, "scale": 1.5}
]).return_dict()
# In free spins: scale high wins down
"free_spin_scaling": ConstructScaling([
{"min": 50, "max": float('inf'), "scale": 0.8}
]).return_dict()
Search Conditions¶
Find specific outcomes:
# Find free spin triggers
search_conditions={"symbol": "scatter", "min_count": 3}
# Find high wins
search_conditions={"min_win": 100}
# Find specific events
search_conditions={"event_type": "triggerFreeSpins"}
# Combine conditions
search_conditions={
"symbol": "M",
"min_win": 50,
"event_type": "updateGlobalMult"
}
Performance Tips¶
Quick Iteration¶
Production Run¶
Memory Usage¶
- Large
num_per_fenceincreases memory usage - Use smaller books for initial optimization
- Generate full books after optimization converges
Troubleshooting¶
"Optimization failed to converge"¶
- Conflicting conditions (impossible to satisfy all targets)
- Try relaxing some constraints
- Check if paytable supports target RTP
"Setup file not found"¶
- Ensure
game_optimization.pyexists - Check file is imported correctly in
run.py - Verify
OptimizationSetupis constructed properly
"Rust build failed"¶
- Install Rust:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - Update Rust:
rustup update - Check
optimization_program/Cargo.tomlis valid
See Also¶
- Game Structure - Understanding game architecture
- Running Games - Generating books for optimization
- CLAUDE.md - Detailed optimization setup reference