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 (как в дропе).
Агрегированные цели (как «медиана медиан» по ячейкам):
- Длительность — медиана по ячейкам от медиан длительности победных боёв попадает в полосу
[targetSec × (1 − tolerancePct/100), targetSec × (1 + tolerancePct/100)](по умолчанию при 330 с и 10% — примерно 297–363 с). - HP героя — медиана по ячейкам от медиан доли HP после победы попадает в полосу
hero-hp-mid±hero-hp-ppпроцентных пунктов (по умолчанию 60% ± 7 п.п. → 53–67%).
После подбора выполняется смягчение атаки, пока в худшей ячейке есть хотя бы одна победа в выборке (как в прежнем отдельном прототипе сетки).
Логика вынесена в 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 idenemy.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 + N−1 rolled). |
-hero-hp-mid |
60 | Режим сетки: центр полосы HP героя на победах (%). |
-hero-hp-pp |
7 | Режим сетки: ±п.п. вокруг -hero-hp-mid. |
-refine |
2 | Режим сетки: проходы подгонки длительности после атаки. |
-iterations |
120 | Число боёв на ячейку сетки (grid) или на архетип (legacy). Рекомендуется ≥ 120–200. |
-seed |
20260331 |
База RNG; на архетип добавляется хеш type. |
-target-sec |
330 | Центр полосы медианы длительности побед (секунды). |
-tolerance-pct |
10 | Полоса вокруг центра; при 330 и 10% → 297–363 с. |
-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 (1–500). |
Примеры:
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%), диапазоны по ячейкам, при-sql—UPDATE. - 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.