mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 04:42:10 +03:00
153 lines
4.7 KiB
TypeScript
153 lines
4.7 KiB
TypeScript
/**
|
|
* XP/Level Engine for OmniRoute Gamification
|
|
*
|
|
* Pure functions for XP calculations, level progression, and reward definitions.
|
|
* No side effects or DB calls — all stateful logic lives in the persistence layer.
|
|
*/
|
|
|
|
// ─── XP Curve ────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* XP required for a specific level (delta, not cumulative).
|
|
* Polynomial curve: `xp_for_level(n) = floor(100 * n^1.5)`
|
|
*
|
|
* @param level - Target level (must be >= 1)
|
|
* @returns XP needed to gain this level (0 for level 1)
|
|
*
|
|
* @example
|
|
* xpForLevel(1) // 0
|
|
* xpForLevel(10) // 3162
|
|
* xpForLevel(50) // 35355
|
|
*/
|
|
export function xpForLevel(level: number): number {
|
|
if (level <= 1) return 0;
|
|
return Math.floor(100 * Math.pow(level, 1.5));
|
|
}
|
|
|
|
/**
|
|
* Cumulative XP required to reach a given level (sum of all prior level deltas).
|
|
*
|
|
* @param level - Target level (must be >= 1)
|
|
* @returns Total XP accumulated by that level
|
|
*
|
|
* @example
|
|
* cumulativeXpForLevel(1) // 0
|
|
* cumulativeXpForLevel(10) // sum of xpForLevel(2..10)
|
|
*/
|
|
export function cumulativeXpForLevel(level: number): number {
|
|
if (level <= 1) return 0;
|
|
let total = 0;
|
|
for (let i = 2; i <= level; i++) {
|
|
total += xpForLevel(i);
|
|
}
|
|
return total;
|
|
}
|
|
|
|
/**
|
|
* Calculate level from total XP using the inverse of the cumulative XP curve.
|
|
*
|
|
* The cumulative XP for level L approximates to `100 * L^2.5 / 2.5`.
|
|
* Solving for L gives `L ≈ (totalXp * 2.5 / 100) ^ 0.4`.
|
|
*
|
|
* @param totalXp - Total accumulated XP
|
|
* @returns Current level (minimum 1)
|
|
*
|
|
* @example
|
|
* calculateLevel(0) // 1
|
|
* calculateLevel(5000) // ~8
|
|
* calculateLevel(100000) // ~100
|
|
*/
|
|
export function calculateLevel(totalXp: number): number {
|
|
if (totalXp <= 0) return 1;
|
|
return Math.max(1, Math.floor(Math.pow((totalXp * 2.5) / 100, 0.4)));
|
|
}
|
|
|
|
/**
|
|
* Get XP needed to reach the next level from the current total XP.
|
|
*
|
|
* @param totalXp - Current total XP
|
|
* @returns Remaining XP until next level-up
|
|
*
|
|
* @example
|
|
* xpToNextLevel(0) // XP needed to reach level 2
|
|
* xpToNextLevel(5000) // XP needed from 5000 to next level
|
|
*/
|
|
export function xpToNextLevel(totalXp: number): number {
|
|
const currentLevel = calculateLevel(totalXp);
|
|
const nextLevelXp = cumulativeXpForLevel(currentLevel + 1);
|
|
return nextLevelXp - totalXp;
|
|
}
|
|
|
|
// ─── Level Titles & Tiers ────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Get a human-readable title for a given level.
|
|
*
|
|
* @param level - Current level
|
|
* @returns Level title string
|
|
*
|
|
* @example
|
|
* getLevelTitle(5) // "Beginner"
|
|
* getLevelTitle(15) // "Explorer"
|
|
* getLevelTitle(30) // "Expert"
|
|
* getLevelTitle(60) // "Master"
|
|
* getLevelTitle(80) // "Legend"
|
|
*/
|
|
export function getLevelTitle(level: number): string {
|
|
if (level >= 76) return "Legend";
|
|
if (level >= 51) return "Master";
|
|
if (level >= 26) return "Expert";
|
|
if (level >= 11) return "Explorer";
|
|
return "Beginner";
|
|
}
|
|
|
|
/**
|
|
* Get the badge tier for a given level.
|
|
*
|
|
* @param level - Current level
|
|
* @returns Tier identifier suitable for badge display
|
|
*
|
|
* @example
|
|
* getLevelTier(5) // "bronze"
|
|
* getLevelTier(20) // "silver"
|
|
* getLevelTier(40) // "gold"
|
|
* getLevelTier(60) // "platinum"
|
|
* getLevelTier(90) // "diamond"
|
|
*/
|
|
export function getLevelTier(level: number): "bronze" | "silver" | "gold" | "platinum" | "diamond" {
|
|
if (level >= 76) return "diamond";
|
|
if (level >= 51) return "platinum";
|
|
if (level >= 26) return "gold";
|
|
if (level >= 11) return "silver";
|
|
return "bronze";
|
|
}
|
|
|
|
// ─── XP Rewards ──────────────────────────────────────────────────────────────
|
|
|
|
/** Base XP rewards for each gamified action. */
|
|
export const XP_REWARDS = {
|
|
/** Per API request routed through OmniRoute */
|
|
request: 1,
|
|
/** Switching to a different provider */
|
|
provider_switch: 5,
|
|
/** Switching to a different model */
|
|
model_switch: 3,
|
|
/** Creating a new combo */
|
|
combo_create: 10,
|
|
/** Using a combo for a request */
|
|
combo_use: 2,
|
|
/** Per 1 000 tokens shared with another user */
|
|
token_share: 1,
|
|
/** Redeeming an invite code */
|
|
invite_redeem: 50,
|
|
/** Daily active usage (once per day) */
|
|
daily_login: 5,
|
|
/** Per consecutive streak day (multiplied by streak length) */
|
|
streak_bonus: 2,
|
|
/** Unlocking a badge */
|
|
badge_unlock: 10,
|
|
} as const;
|
|
|
|
/** Union type of all XP action keys. */
|
|
export type XpAction = keyof typeof XP_REWARDS;
|