Проблема: средний балл не учитывал число игр — новичок с 10 удачными партиями обходил ветерана с сотнями стабильных. Теперь score = (C·m + сумма очков) / (C + игр) × 100 при C=10, m=0.5: к реальным партиям «дописываются» 10 виртуальных со средним 0.5, рейтинг стартует около 50 и с опытом сходится к чистому среднему (формула IMDB). Применено в топах и профиле; разбивка по фракциям осталась на чистом среднем (служебная метрика «лучшая/худшая», сглаживание давило бы её к 50). MIN_GAMES 5 -> 10 («Новички» во всех топах — до 10 партий; подписи берут порог из API). Справка обновлена, добавлен тест (62 passed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
40 lines
1.7 KiB
Python
40 lines
1.7 KiB
Python
"""Сглаживание рейтинга: score = (C·m + сумма очков) / (C + игр) × 100, C=10, m=0.5."""
|
||
from __future__ import annotations
|
||
|
||
from fastapi.testclient import TestClient
|
||
|
||
from tests.conftest import add_group_member, create_finished_match, csrf_headers, login
|
||
|
||
|
||
def test_leaderboard_and_profile_score_are_smoothed(client: TestClient, engine):
|
||
me = login(client, "Хозяин")
|
||
gid = client.post(
|
||
"/api/groups", json={"name": "Группа", "expansion_ids": []}, headers=csrf_headers(client)
|
||
).json()["id"]
|
||
b = add_group_member(engine, gid, "Гость")
|
||
factions = client.get(f"/api/groups/{gid}/factions").json()
|
||
f1, f2 = factions[0]["id"], factions[1]["id"]
|
||
|
||
create_finished_match(
|
||
client,
|
||
gid,
|
||
[
|
||
{"user_id": me["id"], "faction_id": f1, "place": 1},
|
||
{"user_id": b, "faction_id": f2, "place": 2},
|
||
],
|
||
)
|
||
|
||
board = client.get("/api/stats/leaderboard").json()
|
||
by_id = {e["user_id"]: e for e in board["entries"] + board["provisional"]}
|
||
# Победитель: 1 очко за партию → (10·0.5 + 1) / (10 + 1) × 100 = 54.5.
|
||
# Проигравший: 0 очков → (10·0.5 + 0) / 11 × 100 = 45.5.
|
||
assert by_id[me["id"]]["score"] == 54.5
|
||
assert by_id[b]["score"] == 45.5
|
||
# 1 игра < MIN_GAMES=10 → оба пока «Новички», ранжированный топ пуст.
|
||
assert board["entries"] == []
|
||
assert board["min_games"] == 10
|
||
|
||
# Профиль показывает тот же сглаженный рейтинг, что и топ.
|
||
prof = client.get("/api/users/me/stats").json()
|
||
assert prof["overall"]["score"] == 54.5
|