mirror of
https://github.com/283375/arcaea-offline.git
synced 2025-04-21 23:10:18 +00:00
106 lines
3.9 KiB
Python
106 lines
3.9 KiB
Python
from decimal import Decimal
|
|
from math import floor
|
|
from typing import Tuple, TypedDict, Union
|
|
|
|
from arcaea_offline.constants.play_result import ScoreLowerLimits
|
|
|
|
|
|
class PlayResultCalculators:
|
|
@staticmethod
|
|
def score_possible_range(notes: int, pure: int, far: int) -> tuple[int, int]:
|
|
"""
|
|
Returns the possible range of score based on the given values.
|
|
|
|
The first integer of returned tuple is the lower limit of the score,
|
|
and the second integer is the upper limit.
|
|
|
|
For example, ...
|
|
"""
|
|
single_note_score = 10000000 / Decimal(notes)
|
|
|
|
actual_score = floor(
|
|
single_note_score * pure + single_note_score * Decimal(0.5) * far
|
|
)
|
|
return (actual_score, actual_score + pure)
|
|
|
|
@staticmethod
|
|
def shiny_pure(notes: int, score: int, pure: int, far: int) -> int:
|
|
single_note_score = 10000000 / Decimal(notes)
|
|
actual_score = single_note_score * pure + single_note_score * Decimal(0.5) * far
|
|
return score - floor(actual_score)
|
|
|
|
@staticmethod
|
|
def score_modifier(score: int) -> Decimal:
|
|
"""
|
|
Returns the score modifier of the given score
|
|
|
|
https://arcaea.fandom.com/wiki/Potential#Score_Modifier
|
|
|
|
:param score: The score of the play result, e.g. 9900000
|
|
:return: The modifier of the given score, e.g. Decimal("1.5")
|
|
"""
|
|
if not isinstance(score, int):
|
|
raise TypeError("score must be an integer")
|
|
if score < 0:
|
|
raise ValueError("score cannot be negative")
|
|
|
|
if score >= 10000000:
|
|
return Decimal(2)
|
|
if score >= 9800000:
|
|
return Decimal(1) + (Decimal(score - 9800000) / 200000)
|
|
return Decimal(score - 9500000) / 300000
|
|
|
|
@classmethod
|
|
def play_rating(cls, score: int, constant: int) -> Decimal:
|
|
"""
|
|
Returns the play rating of the given score
|
|
|
|
https://arcaea.fandom.com/wiki/Potential#Play_Rating
|
|
|
|
:param constant: The (constant * 10) of the played chart, e.g. 120 for Testify[BYD]
|
|
:param score: The score of the play result, e.g. 10002221
|
|
:return: The play rating of the given values, e.g. Decimal("14.0")
|
|
"""
|
|
if not isinstance(score, int):
|
|
raise TypeError("score must be an integer")
|
|
if not isinstance(constant, int):
|
|
raise TypeError("constant must be an integer")
|
|
if score < 0:
|
|
raise ValueError("score cannot be negative")
|
|
if constant < 0:
|
|
raise ValueError("constant cannot be negative")
|
|
|
|
score_modifier = cls.score_modifier(score)
|
|
return max(Decimal(0), Decimal(constant) / 10 + score_modifier)
|
|
|
|
class ConstantsFromPlayRatingResult(TypedDict):
|
|
EX_PLUS: Tuple[Decimal, Decimal]
|
|
EX: Tuple[Decimal, Decimal]
|
|
AA: Tuple[Decimal, Decimal]
|
|
A: Tuple[Decimal, Decimal]
|
|
B: Tuple[Decimal, Decimal]
|
|
C: Tuple[Decimal, Decimal]
|
|
|
|
@classmethod
|
|
def constants_from_play_rating(
|
|
cls, play_rating: Union[Decimal, str, float, int]
|
|
) -> ConstantsFromPlayRatingResult:
|
|
play_rating = Decimal(play_rating)
|
|
|
|
def _result(score_upper: int, score_lower: int) -> Tuple[Decimal, Decimal]:
|
|
upper_score_modifier = cls.score_modifier(score_upper)
|
|
lower_score_modifier = cls.score_modifier(score_lower)
|
|
return (
|
|
play_rating - upper_score_modifier,
|
|
play_rating - lower_score_modifier,
|
|
)
|
|
|
|
return {
|
|
"EX_PLUS": _result(10000000, ScoreLowerLimits.EX_PLUS),
|
|
"EX": _result(ScoreLowerLimits.EX_PLUS - 1, ScoreLowerLimits.EX),
|
|
"AA": _result(ScoreLowerLimits.EX - 1, ScoreLowerLimits.AA),
|
|
"A": _result(ScoreLowerLimits.AA - 1, ScoreLowerLimits.A),
|
|
"B": _result(ScoreLowerLimits.A - 1, ScoreLowerLimits.B),
|
|
"C": _result(ScoreLowerLimits.B - 1, ScoreLowerLimits.C),
|
|
}
|