fix(statistics): fix levels

This commit is contained in:
MingxuanGame
2025-08-13 10:37:51 +00:00
parent 46746f5b71
commit 7a6a548a65
4 changed files with 203 additions and 17 deletions

View File

@@ -79,21 +79,152 @@ def calculate_pp(
# https://osu.ppy.sh/wiki/Gameplay/Score/Total_score
def calculate_level_to_score(level: int) -> float:
if level <= 100:
# 55 = 4^3 - 3^2
return 5000 / 3 * (55 - level) + 1.25 * math.pow(1.8, level - 60)
def calculate_level_to_score(n: int) -> float:
if n <= 100:
return 5000 / 3 * (4 * n**3 - 3 * n**2 - n) + 1.25 * 1.8 ** (n - 60)
else:
return 26_931_190_827 + 99_999_999_999 * (level - 100)
return 26931190827 + 99999999999 * (n - 100)
def calculate_score_to_level(score: float) -> int:
if score < 5000:
return int(55 - (3 * score / 5000)) # 55 = 4^3 - 3^2
elif score < 26_931_190_827:
return int(60 + math.log(score / 1.25, 1.8))
else:
return int((score - 26_931_190_827) / 99_999_999_999 + 100)
# https://github.com/ppy/osu-queue-score-statistics/blob/4bdd479530408de73f3cdd95e097fe126772a65b/osu.Server.Queues.ScoreStatisticsProcessor/Processors/TotalScoreProcessor.cs#L70-L116
def calculate_score_to_level(total_score: int) -> float:
to_next_level = [
30000,
100000,
210000,
360000,
550000,
780000,
1050000,
1360000,
1710000,
2100000,
2530000,
3000000,
3510000,
4060000,
4650000,
5280000,
5950000,
6660000,
7410000,
8200000,
9030000,
9900000,
10810000,
11760000,
12750000,
13780000,
14850000,
15960000,
17110000,
18300000,
19530000,
20800000,
22110000,
23460000,
24850000,
26280000,
27750000,
29260000,
30810000,
32400000,
34030000,
35700000,
37410000,
39160000,
40950000,
42780000,
44650000,
46560000,
48510000,
50500000,
52530000,
54600000,
56710000,
58860000,
61050000,
63280000,
65550000,
67860000,
70210001,
72600001,
75030002,
77500003,
80010006,
82560010,
85150019,
87780034,
90450061,
93160110,
95910198,
98700357,
101530643,
104401157,
107312082,
110263748,
113256747,
116292144,
119371859,
122499346,
125680824,
128927482,
132259468,
135713043,
139353477,
143298259,
147758866,
153115959,
160054726,
169808506,
184597311,
208417160,
248460887,
317675597,
439366075,
655480935,
1041527682,
1733419828,
2975801691,
5209033044,
9225761479,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
99999999999,
]
remaining_score = total_score
level = 0.0
while remaining_score > 0:
next_level_requirement = to_next_level[
min(len(to_next_level) - 1, round(level))
]
level += min(1, remaining_score / next_level_requirement)
remaining_score -= next_level_requirement
return level + 1
# https://osu.ppy.sh/wiki/Performance_points/Weighting_system

View File

@@ -585,7 +585,7 @@ async def process_user(
session.add(previous_score_best)
statistics.ranked_score += difference
statistics.level_current = calculate_score_to_level(statistics.ranked_score)
statistics.level_current = calculate_score_to_level(statistics.total_score)
statistics.maximum_combo = max(statistics.maximum_combo, score.max_combo)
if score.passed and ranked:
if previous_score_best_mod is not None:

View File

@@ -1,4 +1,5 @@
from datetime import UTC, datetime
import math
from typing import TYPE_CHECKING
from app.models.score import GameMode
@@ -59,8 +60,7 @@ class UserStatistics(UserStatisticsBase, table=True):
grade_sh: int = Field(default=0)
grade_a: int = Field(default=0)
level_current: int = Field(default=1)
level_progress: int = Field(default=0)
level_current: float = Field(default=1)
user: "User" = Relationship(back_populates="statistics") # type: ignore[valid-type]
@@ -97,9 +97,10 @@ class UserStatisticsResp(UserStatisticsBase):
"a": obj.grade_a,
}
s.level = {
"current": obj.level_current,
"progress": obj.level_progress,
"current": int(obj.level_current),
"progress": int(math.fmod(obj.level_current, 1) * 100),
}
s.global_rank = await get_rank(session, obj)
s.country_rank = await get_rank(session, obj, user_country)
return s

View File

@@ -0,0 +1,54 @@
"""statistics: remove level_progress
Revision ID: 8bab62d764a5
Revises: 59c9a0827de0
Create Date: 2025-08-13 10:34:03.430039
"""
from __future__ import annotations
from collections.abc import Sequence
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision: str = "8bab62d764a5"
down_revision: str | Sequence[str] | None = "59c9a0827de0"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
"""Upgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column(
"lazer_user_statistics",
"level_current",
existing_type=mysql.INTEGER(),
type_=sa.Float(),
existing_nullable=False,
)
op.drop_column("lazer_user_statistics", "level_progress")
# ### end Alembic commands ###
def downgrade() -> None:
"""Downgrade schema."""
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"lazer_user_statistics",
sa.Column(
"level_progress", mysql.INTEGER(), autoincrement=False, nullable=False
),
)
op.alter_column(
"lazer_user_statistics",
"level_current",
existing_type=sa.Float(),
type_=mysql.INTEGER(),
existing_nullable=False,
)
# ### end Alembic commands ###