fix(statistics): fix levels
This commit is contained in:
@@ -79,21 +79,152 @@ def calculate_pp(
|
|||||||
|
|
||||||
|
|
||||||
# https://osu.ppy.sh/wiki/Gameplay/Score/Total_score
|
# https://osu.ppy.sh/wiki/Gameplay/Score/Total_score
|
||||||
def calculate_level_to_score(level: int) -> float:
|
def calculate_level_to_score(n: int) -> float:
|
||||||
if level <= 100:
|
if n <= 100:
|
||||||
# 55 = 4^3 - 3^2
|
return 5000 / 3 * (4 * n**3 - 3 * n**2 - n) + 1.25 * 1.8 ** (n - 60)
|
||||||
return 5000 / 3 * (55 - level) + 1.25 * math.pow(1.8, level - 60)
|
|
||||||
else:
|
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:
|
# https://github.com/ppy/osu-queue-score-statistics/blob/4bdd479530408de73f3cdd95e097fe126772a65b/osu.Server.Queues.ScoreStatisticsProcessor/Processors/TotalScoreProcessor.cs#L70-L116
|
||||||
if score < 5000:
|
def calculate_score_to_level(total_score: int) -> float:
|
||||||
return int(55 - (3 * score / 5000)) # 55 = 4^3 - 3^2
|
to_next_level = [
|
||||||
elif score < 26_931_190_827:
|
30000,
|
||||||
return int(60 + math.log(score / 1.25, 1.8))
|
100000,
|
||||||
else:
|
210000,
|
||||||
return int((score - 26_931_190_827) / 99_999_999_999 + 100)
|
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
|
# https://osu.ppy.sh/wiki/Performance_points/Weighting_system
|
||||||
|
|||||||
@@ -585,7 +585,7 @@ async def process_user(
|
|||||||
session.add(previous_score_best)
|
session.add(previous_score_best)
|
||||||
|
|
||||||
statistics.ranked_score += difference
|
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)
|
statistics.maximum_combo = max(statistics.maximum_combo, score.max_combo)
|
||||||
if score.passed and ranked:
|
if score.passed and ranked:
|
||||||
if previous_score_best_mod is not None:
|
if previous_score_best_mod is not None:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
import math
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from app.models.score import GameMode
|
from app.models.score import GameMode
|
||||||
@@ -59,8 +60,7 @@ class UserStatistics(UserStatisticsBase, table=True):
|
|||||||
grade_sh: int = Field(default=0)
|
grade_sh: int = Field(default=0)
|
||||||
grade_a: int = Field(default=0)
|
grade_a: int = Field(default=0)
|
||||||
|
|
||||||
level_current: int = Field(default=1)
|
level_current: float = Field(default=1)
|
||||||
level_progress: int = Field(default=0)
|
|
||||||
|
|
||||||
user: "User" = Relationship(back_populates="statistics") # type: ignore[valid-type]
|
user: "User" = Relationship(back_populates="statistics") # type: ignore[valid-type]
|
||||||
|
|
||||||
@@ -97,9 +97,10 @@ class UserStatisticsResp(UserStatisticsBase):
|
|||||||
"a": obj.grade_a,
|
"a": obj.grade_a,
|
||||||
}
|
}
|
||||||
s.level = {
|
s.level = {
|
||||||
"current": obj.level_current,
|
"current": int(obj.level_current),
|
||||||
"progress": obj.level_progress,
|
"progress": int(math.fmod(obj.level_current, 1) * 100),
|
||||||
}
|
}
|
||||||
|
|
||||||
s.global_rank = await get_rank(session, obj)
|
s.global_rank = await get_rank(session, obj)
|
||||||
s.country_rank = await get_rank(session, obj, user_country)
|
s.country_rank = await get_rank(session, obj, user_country)
|
||||||
return s
|
return s
|
||||||
|
|||||||
@@ -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 ###
|
||||||
Reference in New Issue
Block a user