You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

8.8 KiB

balanceall — CLI баланса монстров

Утилита backend/cmd/balanceall прогоняет архетипы монстров из PostgreSQL (таблица enemies) через то же ядро боя, что онлайн/оффлайн (game.ResolveCombatToEndWithDuration), на референсном герое (game.NewReferenceHeroForBalance).

Режим по умолчанию: сетка (-grid, по умолчанию true)

Для каждого архетипа строится сетка сценариев:

  • Уровни: для каждого L от min_level до max_level включительно — бой герой L против экземпляра монстра L (heroLv == enemyLv), честный тир.
  • Шмот: -gear-variants профилей (по умолчанию 4): один с медианным шмотом, остальные с rolled ilvl (как в дропе).

Агрегированные цели (как «медиана медиан» по ячейкам):

  1. Длительность — медиана по ячейкам от медиан длительности победных боёв попадает в полосу
    [targetSec × (1 tolerancePct/100), targetSec × (1 + tolerancePct/100)] (по умолчанию при 330 с и 10% — примерно 297363 с).
  2. HP героя — медиана по ячейкам от медиан доли HP после победы попадает в полосу
    hero-hp-mid ± hero-hp-pp процентных пунктов (по умолчанию 60% ± 7 п.п.5367%).

После подбора выполняется смягчение атаки, пока в худшей ячейке есть хотя бы одна победа в выборке (как в прежнем отдельном прототипе сетки).

Логика вынесена в backend/cmd/balanceall/grid.go.

Режим legacy (-grid=false)

Один сценарий на архетип:

  • Герой и монстр: уровень = середина полосы (min_level + max_level) / 2, только медианный шмот.
  • Длительность — медиана длительности побед в полосе по -tolerance-pct.
  • Давление — медиана оставшегося HP не выше -max-hero-hp-pct-on-win (при необходимости ослабление до 65/70/75%).

Подключение к БД

Нужен DSN (без БД утилита не запускается):

  • переменная окружения DATABASE_URL, или
  • флаг -dsn (перекрывает env).

Шаблоны подгружаются через storage.ContentStore.LoadEnemyTemplates (как в balancesim).

Запуск

Из каталога модуля Go:

cd backend
set DATABASE_URL=postgres://...
go run ./cmd/balanceall [флаги]

Сборка:

go build -o balanceall ./cmd/balanceall
./balanceall -iterations 200

Область прогона

  • Все архетипы из БД — по умолчанию: строки из enemies в порядке ORDER BY min_level, type (как ListEnemyRows).
  • Один монстр по enemies.id-enemy-id <id> (удобно после go run ./cmd/balanceall -list-enemies).
  • Один архетип по строке type-enemy-type <type> (в таблице enemies это строка вроде wolf, не catalog id enemy.wolf_forest).

Нельзя одновременно задавать -enemy-id и -enemy-type.

JSON-оверлей (-config)

Флаг -config path.json задаёт файл с объектом верхнего уровня: ключи — строки type, как в таблице enemies. Значение — объект с любым подмножеством полей шаблона монстра (имена полей как в JSON у model.Enemy: maxHp, attack, hpPerLevel, specialAbilities, …).

После загрузки из БД данные из файла накладываются в памяти: указанное в JSON поле заменяет значение из БД; отсутствующие в JSON поля не трогаются.

Неизвестный ключ верхнего уровня (тип, которого нет среди загруженных шаблонов) пропускается с предупреждением в stderr.

Пример:

{
  "wolf": {
    "attack": 12,
    "attackPerLevel": 1.1
  },
  "demon_fire": {
    "maxHp": 800,
    "hpPerLevel": 45
  }
}

Если в оверлее задан только один из пары hp / maxHp, второй выравнивается под него для согласованности шаблона.

Флаги

Флаг По умолчанию Смысл
-dsn "" Postgres DSN; если пусто — берётся DATABASE_URL.
-enemy-id 0 Только строка с этим enemies.id.
-enemy-type "" Только архетип с этим type.
-config "" Путь к JSON: частичные шаблоны по ключу type, поверх БД.
-grid true Сетка уровней × шмот; false — legacy (один уровень, медианный шмот).
-gear-variants 4 Режим сетки: число профилей шмота на уровень (1 median + N1 rolled).
-hero-hp-mid 60 Режим сетки: центр полосы HP героя на победах (%).
-hero-hp-pp 7 Режим сетки: ±п.п. вокруг -hero-hp-mid.
-refine 2 Режим сетки: проходы подгонки длительности после атаки.
-iterations 120 Число боёв на ячейку сетки (grid) или на архетип (legacy). Рекомендуется ≥ 120200.
-seed 20260331 База RNG; на архетип добавляется хеш type.
-target-sec 330 Центр полосы медианы длительности побед (секунды).
-tolerance-pct 10 Полоса вокруг центра; при 330 и 10% → 297363 с.
-max-hero-hp-pct-on-win 60 Только legacy: верхняя граница медианы HP героя после побед (%).
-min-win-rate 0.35 Legacy: планка винрейта при накрутке атаки. Сетка: планка медианного винрейта по ячейкам.
-sql true Печатать предлагаемые UPDATE enemies ....
-list-enemies false Список архетипов из БД (с колонкой id) и выход.
-filter "" Подстрока для -list-enemies.
-limit 50 Максимум строк для -list-enemies (1500).

Примеры:

go run ./cmd/balanceall -iterations 200

go run ./cmd/balanceall -enemy-type wolf -iterations 200

# Старый алгоритм (один уровень — середина полосы)
go run ./cmd/balanceall -grid=false -enemy-type wolf

go run ./cmd/balanceall -list-enemies
go run ./cmd/balanceall -sql=false

Вывод

  • Сетка: для каждого типа — baseline по текущему шаблону, затем hpScale/atkScale, агрегаты medOfMed(duration), medOfMed(heroHp%), диапазоны по ячейкам, при -sqlUPDATE.
  • Legacy: как раньше — одна строка метрик и UPDATE.

Связь с репозиторием

  • Загрузка из БД: internal/storage/content_store.go (LoadEnemyTemplates, ListEnemyRows).
  • Сетка: cmd/balanceall/grid.go.
  • Одиночная симуляция: cmd/balancesim + DATABASE_URL.
  • Краткий снимок для контента: docs/monster-catalog-balanced-v1.md.

Ограничения

  • Сетка не моделирует все пары (герой L5 vs монстр L1): для одной кривой в БД агрегаты по «честному» тиру (hero == enemy) устойчивее.
  • Элиты с сильным DoT могут требовать ослабления целей или точечной настройки.
  • Вывод SQL — предложение; источник правды в продакшене — таблица enemies после миграций и reload.