from app.schemas.level import LevelInfo # 체크 1회당 얻는 기본 경험치. XP_PER_CHECK = 10 # 예정일을 놓쳤을 때의 페널티. 연속으로 놓칠수록 (miss_streak * MISS_PENALTY_BASE)로 가속되며, # MISS_PENALTY_CAP에서 더 이상 커지지 않는다(끝없이 방치된 습관이 비정상적인 값으로 폭주하지 않도록). MISS_PENALTY_BASE = 8 MISS_PENALTY_CAP = 80 # 연속 달성일 마일스톤(log_service.MILESTONE_STREAKS와 동일한 값) 도달 시 추가로 얹어주는 보너스 경험치. STREAK_MILESTONE_BONUS_XP: dict[int, int] = { 7: 20, 30: 80, 66: 150, 100: 250, 200: 500, 365: 1000, } # 레벨업에 필요한 경험치는 LEVEL_BASE_XP에서 시작해 레벨마다 LEVEL_STEP_XP씩 늘어난다 # (초반엔 빠르게, 갈수록 완만하게 오르는 곡선). LEVEL_BASE_XP = 20 LEVEL_STEP_XP = 10 def _xp_required_for_level(level: int) -> int: """`level`에서 `level + 1`로 올라가는 데 필요한 경험치.""" return LEVEL_BASE_XP + LEVEL_STEP_XP * (level - 1) def level_from_xp(xp: int) -> LevelInfo: xp = max(xp, 0) level = 1 remaining = xp while True: needed = _xp_required_for_level(level) if remaining < needed: progress_pct = round(remaining / needed * 100, 1) return LevelInfo( xp=xp, level=level, xp_into_level=remaining, xp_for_next_level=needed, progress_pct=progress_pct, ) remaining -= needed level += 1