Рейтинг — пакет формул и начисление
Все рейтинговые формулы живут только в пакете
repos/poker-api/packages/rating — чистые функции без обращений к базе, сети и
времени. Это не стилистика: по этим числам игроки сравнивают себя друг с другом,
поэтому формулы обязаны быть проверяемы отдельно от всего остального. Пакет
покрыт тестами на 100%, и это условие, а не достижение.
Правило простое: если формула появилась вне этого пакета — это ошибка.
Конфигурация
Заголовок раздела «Конфигурация»Одна структура RatingConfig со значениями по умолчанию:
| Поле | Значение | Что задаёт |
|---|---|---|
pointsBase |
10 | множитель формулы очков |
minPoints |
1 | минимум очков за участие |
xpLevelBase |
50 | база порога уровня |
xpLevelExponent |
1.5 | показатель порога уровня |
skillAlpha |
0.1 | сглаживание силы |
skillInitial |
0.4444 | стартовая сила, даёт отображаемые 5.0 |
strikesToBan |
2 | страйков до блокировки записи |
strikeWindowDays |
30 | окно подсчёта страйков |
banDays |
14 | длительность блокировки |
Значения меняет админ платформы через GET/PUT /admin/rating-config.
Обратите внимание: пороги страйков и бана лежат здесь же, а не в модуле
записи — это одна настроечная поверхность.
Формулы
Заголовок раздела «Формулы»Очки сезона. round(pointsBase * sqrt(N) * (N - place + 1) / N), не меньше
minPoints. Здесь N — число реально игравших, place — от 1 до N.
При N <= 0 или месте вне диапазона бросается RangeError.
Перформанс. (N - place) / (N - 1); при N === 1 возвращает 1.
Новая сила. prev + skillAlpha * (perf - prev), зажатая в [0, 1].
Это экспоненциальное сглаживание: один турнир смещает силу на десятую часть
разрыва между текущим значением и результатом.
Отображаемая сила. 1 + 9 * skill, округление до 0.1. Отсюда стартовые
0.4444 → 5.0.
Порог уровня. reachXp(L) = round(xpLevelBase * (L - 1) ^ xpLevelExponent),
причём reachXp(1) === 0. Уровень по XP — максимальный L, для которого
reachXp(L) <= xp; цикл ограничен MAX_LEVEL = 1000 как защита от зацикливания
на аномальном значении.
XP начисляется один в один с очками сезона.
Эндпоинты
Заголовок раздела «Эндпоинты»| Метод и путь | Что отдаёт |
|---|---|
POST /tournaments/{id}/confirm-results |
подтверждение результатов — момент начисления |
GET /tournaments/{id}/results |
итоги турнира |
GET /players/{userId}/results |
результаты игрока |
GET /leaderboards/season |
сезонный лидерборд |
GET /leaderboards/season/summary |
топ-3 и строка игрока |
GET /leaderboards/level |
уровневый, топ-100 по XP |
GET /seasons/current, GET /seasons |
сезоны |
GET/PUT /admin/rating-config |
настройки платформы |
Пути даны без префикса /api/v1.
Сезон — календарный квартал. ensureCurrentSeason() идемпотентен: гонка ловится
по уникальному startsOn. Вызывается и джобой season-rollover
(5 0 * * * UTC), и лидербордами — то есть первый же запрос после полуночи
заведёт квартал, даже если джоба не отработала.
Ограничения
Заголовок раздела «Ограничения»- Начисление происходит только при подтверждении результатов и необратимо для клуба. Пересчёта задним числом нет — исправление делает платформа вручную.
- Нокауты не учитываются. В рейтинге есть только места и победы.
- Уровневый лидерборд ограничен топ-100, сезонный отдаётся целиком.
Что рядом
Заголовок раздела «Что рядом»- Рейтинг, уровни и сезоны — те же формулы словами продукта.
- poker-api — как запустить сервис и прогнать тесты.