Рейтинг TAC: как он рассчитывается

Рейтинг оценивает уровень игры участника (и постоянной пары игроков) на основе зарегистрированных результатов игр. На этой странице сначала дается общее объяснение, а затем — подробное описание для разработчиков.

Общая информация

Что такое рейтинг TAC?

Рейтинг — это число, которое показывает уровень игры в TAC — как рейтинг Эло в шахматах, но с более современным методом расчёта. Он рассчитывается только на основе партий, занесённых в приложение. Очки не начисляются вручную.

Существует два вида рейтинга: индивидуальный рейтинг для каждого участника и командный рейтинг для постоянной пары игроков (двух участников, которые играют вместе).

Когда появляется рейтинг

Рейтинг рассчитывается только в том случае, если на обеих сторонах участвует как минимум один зарегистрированный участник. Гости (игроки без учётной записи) не учитываются. Таким образом, если один участник играет только с гостями или против них, сравнение не происходит, а значит, рейтинг не начисляется. Для получения командного рейтинга необходимо, чтобы два участника без гостей играли вместе в одной команде.

Как меняется рейтинг?

После каждой рейтинговой партии рейтинг обновляется. Победа над более сильным соперником заметно повышает его, а поражение от более слабого — снижает ещё сильнее. Победа над гораздо более слабым соперником почти не влияет на рейтинг, так как такой результат был ожидаем.

У каждого рейтинга есть показатель неопределённости: в начале система мало знает о тебе, поэтому твой рейтинг может заметно меняться. Чем больше ты играешь, тем точнее и стабильнее становится оценка.

Предварительный рейтинг

Пока у участника меньше 10 рейтинговых партий, рейтинг считается предварительным — он ещё недостаточно информативен для честного сравнения.

Различные таблицы лидеров

Рейтинг ведётся раздельно по разным категориям, чтобы сравнение оставалось честным:

  • Глобальный — по всем клубам.
  • Клуб — только партии в рамках одного клуба.
  • Встреча — только партии конкретной встречи/турнира.
  • Сезон — только партии в рамках одного сезона в клубе.

Неактивность

Если долго не играть, оценка рейтинга со временем становится менее точной. Сам рейтинг (оценочная сила) при этом остаётся прежним — снижается только уровень уверенности до следующей партии.

Отдельные партии могут не учитываться в рейтинге (например, тестовые). Гости без аккаунта участника не имеют собственного рейтинга.

Техническое описание (для разработчиков)

Модель и начальные значения

Рейтинг основан на OpenSkill (байесовский метод Венга-Лина с моделью Плакетта-Люса). Эталонная реализация работает на Java с библиотекой com.pocketcombats:openskill; сам метод не зависит от языка программирования и подробно описан ниже.

Каждый рейтинг — это нормальное распределение со средним значением μ (расчетная сила) и стандартным отклонением σ (неопределенность). Начальные значения для нового игрока:

μ₀ = 25.0     σ₀ = 25 / 3 ≈ 8.3333
Начальные значения рейтинга (RatingEngine.DEFAULT_MU / DEFAULT_SIGMA)

Отображаемое сравнимое значение — это консервативный ординал — сила за вычетом поправки на неопределённость:

ordinal = μ − Z · σ     (Z = 3)
Консервативный балл. Новый участник начинает с 25 − 3 · 8.3333 = 0.

Для параметров партии используются стандартные значения библиотеки (Weng-Lin) без переопределения: β = σ₀ / 2 ≈ 4.167 (разброс между силой и текущей формой) и τ = σ₀ / 100 ≈ 0.083 (аддитивный динамический коэффициент, который немного увеличивает σ перед каждым обновлением, чтобы рейтинг оставался динамичным).

Оценить партию

Партия состоит из двух сторон (команда A и команда B). Рейтинг каждой стороны формируется из индивидуальных рейтингов игроков (сумма μ и сумма σ² соответственно, DefaultTeamRatingAggregator). На основе этого определяются ранги:

  • Сторона победителя: ранг 1, сторона проигравшего: ранг 2.
  • Ничья: обе стороны — ранг 1.

На основе этих рангов модель Плакета — Люса обновляет данные каждого участника: сила μ смещается в сторону фактического результата, а неопределённость σ снижается с каждой новой партией. Величина корректировки зависит от разницы между ожидаемым и фактическим результатом, а также от текущего уровня неопределённости.

Вероятность победы стороны А над стороной Б (для прогнозов/матчмейкинга) рассчитывается на основе агрегированных значений команд с помощью кумулятивной функции нормального распределения Φ:

P(A > B) = Φ( (μ_A − μ_B) / √(σ_A² + σ_B² + β²) )
RatingEngine.predictWin — Гауссова кумулятивная функция распределения.

Две проекции: одиночная и парная

Каждая партия засчитывается в двух независимых проекциях:

  • Индивидуальный — субъект это ID участника. Требуется как минимум один реальный участник с обеих сторон.
  • Пара (команда) — субъект представляет собой неупорядоченную пару участников. Полная сторона (2 участника) образует пару; сторона с гостем пару не образует.

Распределение по командам на основе собранных данных: result_player.team принимает значение A или B; победитель указан в result.winner_team (если поле пустое, партия считается ничьей). Игроки без ID участника (гости) исключаются из расчёта рейтинга. Если одна сторона состоит только из гостей, она сама не учитывается в рейтинге, но служит в качестве стандартного противника (субъекта с начальными значениями), чтобы другая сторона всё равно могла быть засчитана. Партии с exclude_from_rating полностью пропускаются.

Партиции и хронологическое повторение

Партиция — это кортеж (scope, scopeRefId). Есть четыре области (scope): GLOBAL (без Ref), CLUB (clubId), EVENT (eventId), SEASON (seasonId). Каждая партиция считается независимо — одна партия одновременно попадает в несколько партиций.

Рейтинги зависят от последовательности и взаимосвязаны (результат партии зависит от рейтингов до неё). Поэтому они не пересчитываются инкрементально, а формируются путём хронологического пересчёта партий внутри раздела. История снимков по каждой партии (μ/σ до и после каждой игры) является источником истины; материализованные таблицы member_rating / team_rating хранят только текущее состояние для быстрого чтения.

  • Полный пересчёт: все партии раздела пересчитываются заново, начиная с исходных значений.
  • Частичный повтор с определённого момента: состояние загружается из последнего снапшота *до* момента изменения (сидинг) и проигрывается заново с этой точки — снапшоты после этого момента удаляются.

Материализованные значения на субъекта

После пересчёта текущая запись для каждого участника перезаписывается:

ПолеЗначение
mu, sigmaЗначения после последней партии
ordinalmu − 3 · sigma (консервативно)
ordinalPreviousПорядковый номер перед последней партией (для стрелки тренда)
peakOrdinal / peakAtлучшее достигнутое место и время
gamesCountКоличество засчитанных партий в разделе
provisionalда, пока gamesCount < 10
lastGameAtВремя последней рейтинговой партии
Все сохранённые значения μ/σ/порядковые округляются до 4 знаков после запятой (HALF_UP).

Снижение рейтинга за неактивность

Ежедневная задача (по умолчанию: 04:00) повышает неопределённость неактивных рейтингов. Для каждого рейтинга, последняя партия в котором была более 30 дней назад, действует следующее:

σ ← min(σ + 0.05, 8.3333)
ordinal ← μ − 3 · σ
За один запуск задания. μ остаётся неизменным; история снимков не записывается (Decay не является игровым событием).
  • За каждую партию σ увеличивается на 0.05, с ограничением σ_max = 8.3333 (= начальное значение σ₀).
  • Изменяются только рейтинги ниже верхнего предела (одна команда Batch-UPDATE).
  • Рамки EVENT не подвержены Decay (турнир — это фиксированный период времени).

Обработка (асинхронная, в конечном счёте)

Расчёт никогда не происходит в режиме inline при сохранении результата, а выполняется отложенно: изменение результата помечает затронутые разделы после коммита (AFTER_COMMIT) как «грязные» (добавление в очередь пересчёта). Отдельный воркер обрабатывает очередь последовательно и заново пересчитывает каждый затронутый раздел, начиная с момента изменения. Благодаря этому связанные рейтинги остаются согласованными, а процесс сохранения не блокируется.

Изменение результата всегда затрагивает: GLOBAL, клуб, в котором прошла партия, встречу/турнир (старый и новый, если дата была перенесена), а также любой сезон, в период которого попадает время партии.