docs: define monthly MVP ranking v1 scoring
This commit is contained in:
@@ -70,5 +70,6 @@ Community website repository with a static landing in the current phase and a pl
|
||||
- Historical match and player statistics must come from the public CRCON scoreboard JSON layer, not from A2S or the `/games` HTML shell.
|
||||
- The validated discovery for those historical sources is documented in `docs/historical-crcon-source-discovery.md`.
|
||||
- The persisted historical domain model for CRCON matches, players and ingestion runs is documented in `docs/historical-domain-model.md`.
|
||||
- The V1 monthly MVP scoring proposal for persisted historical player metrics is documented in `docs/monthly-mvp-ranking-scoring-design.md`.
|
||||
- Frontend data consumption should remain progressive, endpoint by endpoint, with static fallbacks preserved during migration.
|
||||
- The frontend integration strategy is documented in `docs/frontend-data-consumption-plan.md`.
|
||||
|
||||
90
ai/tasks/done/TASK-066-monthly-mvp-ranking-scoring-design.md
Normal file
90
ai/tasks/done/TASK-066-monthly-mvp-ranking-scoring-design.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# TASK-066-monthly-mvp-ranking-scoring-design
|
||||
|
||||
## Goal
|
||||
Diseñar de forma precisa y defendible la fórmula de puntuación para una V1 del ranking mensual de mejores jugadores, usando únicamente métricas ya persistidas y fiables en el proyecto.
|
||||
|
||||
## Context
|
||||
La auditoría de datos previa ya dejó claro qué métricas están hoy disponibles con fiabilidad suficiente para una primera versión del ranking mensual. La recomendación actual para una V1 realista se basa en:
|
||||
- kills
|
||||
- support
|
||||
- time played
|
||||
- KPM derivado
|
||||
- KDA derivado
|
||||
- umbrales de participación
|
||||
- penalización por teamkills opcional
|
||||
|
||||
Antes de implementar nada en backend o UI, hace falta fijar claramente:
|
||||
- qué métricas entran en la fórmula
|
||||
- con qué pesos
|
||||
- qué requisitos mínimos de elegibilidad debe cumplir un jugador
|
||||
- cómo evitar rankings absurdos por muestras demasiado pequeñas
|
||||
- cómo se resuelven empates
|
||||
- si la V1 se calcula por servidor, globalmente o en ambos modos
|
||||
|
||||
## Steps
|
||||
1. Revisar la auditoría previa de datos del ranking mensual y confirmar el conjunto de métricas fiables disponibles hoy.
|
||||
2. Definir qué significa exactamente “mejor jugador del mes” en la V1 del proyecto:
|
||||
- orientación más ofensiva
|
||||
- orientación más equilibrada
|
||||
- orientación más MVP de equipo
|
||||
3. Diseñar una fórmula de scoring concreta para V1 basada únicamente en métricas persistidas y fiables.
|
||||
4. Definir con claridad:
|
||||
- métricas incluidas
|
||||
- pesos de cada métrica
|
||||
- normalización o escala
|
||||
- tratamiento de muestras pequeñas
|
||||
- mínimos de elegibilidad
|
||||
- penalizaciones opcionales (por ejemplo teamkills)
|
||||
- desempates
|
||||
5. Recomendar si la V1 debe publicarse como:
|
||||
- ranking por servidor
|
||||
- ranking global
|
||||
- ambos
|
||||
6. Justificar por qué esa fórmula es razonable para una primera versión del producto.
|
||||
7. Dejar expresamente fuera de V1 las métricas no suficientemente confirmadas o no persistidas hoy, por ejemplo:
|
||||
- garrisons/OPs
|
||||
- duelos directos
|
||||
- kills por arma
|
||||
- impacto táctico fino no persistido
|
||||
8. Añadir, si aporta valor, una breve nota de futuro sobre cómo podría ampliarse la fórmula en una V2 sin rediseñarla por completo.
|
||||
9. No implementar todavía nuevas tablas, rutas, snapshots ni UI.
|
||||
10. Al completar la implementación:
|
||||
- dejar el repositorio consistente
|
||||
- hacer commit
|
||||
- hacer push al remoto si el entorno lo permite
|
||||
|
||||
## Files to Read First
|
||||
- AGENTS.md
|
||||
- ai/repo-context.md
|
||||
- ai/architecture-index.md
|
||||
- docs/monthly-player-ranking-data-audit.md
|
||||
- docs/historical-domain-model.md
|
||||
- docs/historical-data-quality-notes.md
|
||||
- backend/README.md
|
||||
- backend/app/historical_models.py
|
||||
- backend/app/historical_storage.py
|
||||
- backend/app/payloads.py
|
||||
|
||||
## Expected Files to Modify
|
||||
- un documento nuevo, por ejemplo:
|
||||
- docs/monthly-mvp-ranking-scoring-design.md
|
||||
- opcionalmente docs/decisions.md si surge una decisión técnica clara sobre alcance V1
|
||||
- opcionalmente ai/architecture-index.md si conviene enlazar el diseño de scoring
|
||||
|
||||
## Constraints
|
||||
- No implementar todavía el ranking mensual.
|
||||
- No tocar producto visible.
|
||||
- No depender de métricas no confirmadas o no persistidas hoy.
|
||||
- No hacer cambios destructivos.
|
||||
- Mantener el trabajo centrado en diseño de scoring y reglas de elegibilidad de V1.
|
||||
|
||||
## Validation
|
||||
- Existe un documento claro con la fórmula de scoring propuesta para la V1.
|
||||
- Quedan definidos los pesos, métricas, mínimos y desempates.
|
||||
- Queda claro qué se incluye en V1 y qué se deja para V2.
|
||||
- La propuesta es coherente con la auditoría de datos previa.
|
||||
- Los cambios quedan committeados y se hace push si el entorno lo permite.
|
||||
|
||||
## Change Budget
|
||||
- Preferir menos de 4 archivos modificados o creados.
|
||||
- Preferir menos de 220 líneas cambiadas.
|
||||
197
docs/monthly-mvp-ranking-scoring-design.md
Normal file
197
docs/monthly-mvp-ranking-scoring-design.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# Monthly MVP Ranking Scoring Design
|
||||
|
||||
## Validation Date
|
||||
|
||||
- 2026-03-24
|
||||
|
||||
## Objective
|
||||
|
||||
Definir una formula V1 precisa y auditable para un ranking mensual de mejores
|
||||
jugadores usando solo metricas ya persistidas y suficientemente fiables en el
|
||||
repositorio.
|
||||
|
||||
## Evidence Base
|
||||
|
||||
This proposal is based on:
|
||||
|
||||
- `docs/monthly-player-ranking-data-audit.md`
|
||||
- `docs/historical-domain-model.md`
|
||||
- `docs/historical-data-quality-notes.md`
|
||||
- `backend/app/historical_models.py`
|
||||
- `backend/app/historical_storage.py`
|
||||
- `backend/app/payloads.py`
|
||||
|
||||
The design assumes the existing monthly window already used by the backend:
|
||||
|
||||
- UTC calendar month
|
||||
- closed matches only
|
||||
- fallback to the previous closed month only when the current month has no
|
||||
closed matches at all
|
||||
|
||||
## V1 Meaning Of "Best Player Of The Month"
|
||||
|
||||
V1 should not mean "highest raw kills only" and should not pretend to measure
|
||||
full tactical impact that the project does not persist yet.
|
||||
|
||||
For this project, "monthly MVP" in V1 means:
|
||||
|
||||
- sustained offensive contribution across the month
|
||||
- meaningful team contribution through support
|
||||
- good efficiency without rewarding one or two short outlier matches
|
||||
- enough participation to make the result credible
|
||||
|
||||
This is therefore a balanced MVP model with a light offensive bias.
|
||||
|
||||
## Metrics Included In V1
|
||||
|
||||
Included metrics:
|
||||
|
||||
- total kills
|
||||
- total support
|
||||
- total time played
|
||||
- KPM derived from monthly totals
|
||||
- KDA derived from monthly totals
|
||||
- optional teamkill penalty
|
||||
- matches played as an eligibility guard
|
||||
|
||||
Derived metrics must be recomputed from monthly totals, not from the average of
|
||||
per-match ratios:
|
||||
|
||||
- `kpm = total_kills / max(total_time_minutes, 1)`
|
||||
- `kda = total_kills / max(total_deaths, 1)`
|
||||
|
||||
## Metrics Explicitly Out Of Scope For V1
|
||||
|
||||
Do not include in V1:
|
||||
|
||||
- combat
|
||||
- offense
|
||||
- defense
|
||||
- matches over 100 kills
|
||||
- win/loss context
|
||||
- weapons profile
|
||||
- kill streaks or life-span fields
|
||||
- duels, `most_killed`, `death_by`
|
||||
- garrisons, OPs or tactical events not confirmed as persisted
|
||||
|
||||
Reason:
|
||||
|
||||
- some are useful but would complicate the first release without improving
|
||||
reliability enough
|
||||
- others are not persisted today or are not confirmed with stable semantics
|
||||
|
||||
## Eligibility Rules
|
||||
|
||||
A player is eligible for the monthly MVP ranking only if all conditions hold:
|
||||
|
||||
- played at least `6` closed matches in the selected month and scope
|
||||
- accumulated at least `21600` seconds (`6` hours) of play time in that month
|
||||
- has non-null persisted stats for kills, deaths, support and time
|
||||
|
||||
These gates are intentionally dual:
|
||||
|
||||
- match count blocks one-match outliers
|
||||
- time played blocks short-session inflation
|
||||
|
||||
## Scope Recommendation
|
||||
|
||||
V1 should be computed in both scopes from the same formula:
|
||||
|
||||
- per server
|
||||
- global aggregate using `all-servers`
|
||||
|
||||
Publication recommendation:
|
||||
|
||||
- default visible ranking: per server
|
||||
- secondary comparable view: global aggregate
|
||||
|
||||
Why:
|
||||
|
||||
- per-server ranking is easier to interpret and fairer for each community shard
|
||||
- the repository already supports the logical aggregate `all-servers`
|
||||
- using one formula for both scopes avoids redesign later
|
||||
|
||||
## Normalized Component Scores
|
||||
|
||||
For each month and scope, first aggregate one row per eligible player.
|
||||
|
||||
Then calculate these normalized component scores on a `0..100` scale:
|
||||
|
||||
- `kills_score = 100 * ln(1 + total_kills) / ln(1 + max_total_kills_eligible)`
|
||||
- `support_score = 100 * ln(1 + total_support) / ln(1 + max_total_support_eligible)`
|
||||
- `kpm_score = 100 * ln(1 + kpm) / ln(1 + max_kpm_eligible)`
|
||||
- `kda_score = 100 * ln(1 + kda) / ln(1 + max_kda_eligible)`
|
||||
- `participation_score = 100 * min(1, total_time_seconds / 28800)`
|
||||
|
||||
Implementation notes:
|
||||
|
||||
- `ln(1 + x)` dampens extreme leaders without hiding real advantage
|
||||
- participation reaches full score at `8` hours
|
||||
- all `max_*_eligible` references are calculated inside the same month and scope
|
||||
|
||||
## V1 Scoring Formula
|
||||
|
||||
Recommended V1 monthly MVP score:
|
||||
|
||||
`mvp_score = 0.35 * kills_score + 0.20 * support_score + 0.20 * kpm_score + 0.15 * kda_score + 0.10 * participation_score - teamkill_penalty`
|
||||
|
||||
Weight rationale:
|
||||
|
||||
- `35%` kills: offensive impact should matter most in a first public ranking
|
||||
- `20%` support: keeps the model closer to MVP than to a pure frag ranking
|
||||
- `20%` KPM: rewards productive time, not only volume
|
||||
- `15%` KDA: rewards cleaner performance but keeps it below kills volume
|
||||
- `10%` participation: favors sustained monthly presence without turning the
|
||||
ranking into a pure grind chart
|
||||
|
||||
## Teamkill Penalty
|
||||
|
||||
Use a small optional penalty in V1:
|
||||
|
||||
- `teamkill_penalty = min(6, total_teamkills * 0.5)`
|
||||
|
||||
Effect:
|
||||
|
||||
- `1` teamkill subtracts `0.5`
|
||||
- `4` teamkills subtract `2`
|
||||
- penalty caps at `6`
|
||||
|
||||
This keeps the penalty visible without letting it dominate the ranking.
|
||||
|
||||
## Tie-Break Rules
|
||||
|
||||
If two players have the same `mvp_score`, resolve ties in this order:
|
||||
|
||||
1. higher `participation_score`
|
||||
2. higher `kills_score`
|
||||
3. higher `support_score`
|
||||
4. lower `total_teamkills`
|
||||
5. alphabetical `display_name`
|
||||
6. stable player key as final deterministic fallback
|
||||
|
||||
## Why This V1 Is Reasonable
|
||||
|
||||
This design is defendable for a first release because it:
|
||||
|
||||
- uses only metrics already persisted with strong coverage
|
||||
- recomputes efficiency from totals instead of averaging noisy per-match ratios
|
||||
- blocks absurd winners from tiny samples with explicit eligibility gates
|
||||
- stays interpretable enough to explain in product copy
|
||||
- can be implemented from current monthly aggregates without new ingestion or
|
||||
schema work
|
||||
|
||||
## V2 Expansion Path
|
||||
|
||||
V2 can extend the same structure without redesigning the whole ranking:
|
||||
|
||||
- add combat, offense and defense as extra weighted components
|
||||
- add win/loss context only where team scores are present and validated
|
||||
- review whether teamkill penalty should become rate-based instead of absolute
|
||||
- later add tactical metrics only after deliberate persistence work
|
||||
|
||||
The important constraint for V2 is to preserve the same shape:
|
||||
|
||||
- explicit eligibility
|
||||
- normalized component scores
|
||||
- weighted sum
|
||||
- deterministic tie-breaks
|
||||
Reference in New Issue
Block a user