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).

Где может пригодиться:

  1. Buy-bonus mode — если делаем покупаемый бонус, где симулируем 100K сценариев freegame и хотим точную RTP. Можно сохранить precomputed lookup-table только для этого режима, на /play в bonus-mode брать готовый исход.
  2. Superspin / hold-mode — режимы с малым числом возможных исходов, где precomputed даёт лучше cert-evidence.
  3. 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.

Связанные страницы