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.

145 lines
8.8 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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:
```bash
cd backend
set DATABASE_URL=postgres://...
go run ./cmd/balanceall [флаги]
```
Сборка:
```bash
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.
Пример:
```json
{
"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). |
Примеры:
```bash
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.