Lookup-table optimization
Алгоритм балансировки симулированной выборки к таргетной RTP путём подбора весов в lookUpTable_<mode>.csv. Решает проблему: после симуляции с force-сегментами фактическая RTP сильно завышена (потому что мы намеренно форсировали max-win и freegame чаще, чем они выпадают «по природе»). Веса подбираются так, чтобы при weighted sample реальная RTP сошлась к таргету.
Идиома взята из Stake math-sdk, реализация в optimization_program/ (Rust). Алгоритм описан в distribution_optimization.pdf и optimization_algorithm.md.
Когда нужно
- Precomputed подход (Stake-style) — обязательно, иначе опубликованная игра с RTP=163% всех разорит.
- Real-time подход — опционально, когда хочется precomputed-семантику для отдельного режима (например, buy-bonus, где раундов мало и нужна точная RTP). Для базовых спинов в real-time подходит другая балансировка — через сам reelstrip и paytable.
Постановка задачи
После симуляции имеем N спинов с payout-ами p₁, p₂, ..., p_N и весами w₁ = w₂ = ... = w_N = 1. Текущая sample-RTP:
[ \text{RTP}_{\text{raw}} = \frac{\sum w_i \cdot p_i}{N \cdot \text{cost}} ]
Из-за force-сегментации RTP_raw ≠ target. Хотим найти такие wᵢ ≥ 1 (целые), чтобы:
[ \text{RTP}{\text{weighted}} = \frac{\sum w_i \cdot p_i}{\sum w_i \cdot \text{cost}} \approx \text{RTP}{\text{target}} ]
При этом:
- Сохранить желаемую форму распределения (среднее, медиана, хвосты — чтобы профиль волатильности не схлопнулся).
- Сохранить hit-rate per criteria: max-win —
0.001, freegame —0.10, и т.д. wᵢ— uint64, чтобы пройти RGS-валидацию формата (см. RGS data format).
Алгоритм Stake (high-level)
1. По OptimizationSetup → построить trial-distributions (Gaussian-семплированные веса по диапазонам payout).
2. Для каждого trial:
a. Применить веса → пересчитать RTP_weighted.
b. Отсеять, если RTP далеко от target (за допуск) или mean/median не попадают в коридор.
3. Ранжировать прошедшие по симулированным тестам:
a. Раздать N виртуальных спинов с этими весами.
b. Проверить hit-rates per criteria (max-win, freegame, 0-win) против target.
c. Score = combined penalty.
4. Лучший trial → финальный lookUpTable_<mode>_0.csv.
Реализовано на Rust ради скорости (миллионы trial-distributions, каждый с N симулированных тестов).
Параметры optimization
В Stake OptimizationSetup для каждого bet-mode задаёт opt_params = {conditions, scaling, parameters}:
conditions
Какие criteria и с какой target RTP / hit-rate. Например:
"base": {
"conditions": {
"wincap": {"rtp": 0.005, "hit_rate": 0.001},
"freegame": {"rtp": 0.30, "hit_rate": 0.10},
"0": {"hit_rate": "x"}, # свободная переменная
"basegame": {"rtp": 0.665, "hit_rate": "x"},
},
}Warning
Для каждой criteria нужны 2 из 3: RTP / average_win / hit_rate. Третья выводится. Минимум одна криteria может быть
x(свободная) — иначе система переопределена. Сумма hit-rates = 1; сумма RTP-вкладов = target_rtp.
Warning
Порядок ключей criteria важен (как в BetMode + Distribution):
wincapидёт раньшеfreegame, потому что max-win-сценарии обычно тоже freegame. Оптимизатор разносит id-ы по корзинам в порядке объявления.
scaling
Bias-фактор для конкретных payout-диапазонов: «при генерации trial-distributions подкручивай Gaussian-веса в диапазоне [100, 500]× в k раз чаще». Используется для сдвига профиля волатильности в нужную сторону (high-vol → больше веса хвостам).
parameters
Гиперпараметры optimization-цикла:
- Сколько trial-distributions сгенерировать.
- min/max mean-to-median score (волатильность profile).
- Сколько симулированных тестовых спинов на trial.
Артефакт
lookUpTable_<mode>_0.csv:
1,199895486317,0
2,25668581149,20
3,126752606,140
...
Колонки: simulation_id, weight, payout (uint64). RGS на каждый /play делает weighted random sample по этой таблице → возвращает книгу с этим id.
В нашем real-time бэке
Прямая weight-optimization для базовых спинов не нужна — RTP балансируется через сам reelstrip и paytable (классическая методология PAR sheet, см. PAR sheet).
Где может пригодиться:
- Buy-bonus mode — если делаем покупаемый бонус, где симулируем 100K сценариев freegame и хотим точную RTP. Можно сохранить precomputed lookup-table только для этого режима, на /play в bonus-mode брать готовый исход.
- Superspin / hold-mode — режимы с малым числом возможных исходов, где precomputed даёт лучше cert-evidence.
- Tournament leaderboard prizes — если нужен фиксированный пул призовых исходов с известным распределением.
Реализация optimization-алгоритма на TS — компактно (несколько сотен строк). На Rust — для production-скорости, как у Stake.
Связь с PAR sheet
После optimization симулятор пересчитывает все статистики и пишет финальный PAR sheet: фактическая RTP, hit-rate per criteria, RTP-split base/free, max-win frequency, distribution-percentiles. Это и есть cert-evidence + product reporting.