Updated BL curve
Some checks are pending
continuous-integration/drone/push Build is running

This commit is contained in:
Lee 2023-03-26 20:39:55 +01:00
parent 4c32f504a4
commit 024f553586
No known key found for this signature in database
GPG Key ID: BAF8F4DB8E7F38EF
4 changed files with 154 additions and 40 deletions

@ -1,53 +1,121 @@
import Utils from "../utils/utils"; import Utils from "../utils/utils";
/** /**
* I'm not even sure what this shit does, ask BL * The pp curve of this leaderboard
* @see https://github.com/BeatLeader/beatleader-server/blob/16123a792b1a837faf6287e5bcd58e2e06e6a6f0/Utils/ReplayUtils.cs for more info */
const ppCurve = [
[1.0, 7.424],
[0.999, 6.241],
[0.9975, 5.158],
[0.995, 4.01],
[0.9925, 3.241],
[0.99, 2.7],
[0.9875, 2.303],
[0.985, 2.007],
[0.9825, 1.786],
[0.98, 1.618],
[0.9775, 1.49],
[0.975, 1.392],
[0.9725, 1.315],
[0.97, 1.256],
[0.965, 1.167],
[0.96, 1.101],
[0.955, 1.047],
[0.95, 1.0],
[0.94, 0.919],
[0.93, 0.847],
[0.92, 0.786],
[0.91, 0.734],
[0.9, 0.692],
[0.875, 0.606],
[0.85, 0.537],
[0.825, 0.48],
[0.8, 0.429],
[0.75, 0.345],
[0.7, 0.286],
[0.65, 0.246],
[0.6, 0.217],
[0.0, 0.0],
];
/**
* https://github.com/BeatLeader/beatleader-server/blob/master/Utils/ReplayUtils.cs#L45
* funky shit from ^
* *
* @param {Number} acc the accuracy of the score * @param {Number} accuracy the accuracy of the score
* @param {Number} stars the ranked star count * @param {Number} accRating the acc rating of the difficulty
* @param {Number} passRating the pass rating of the difficulty
* @param {Number} techRating the tech rating of the difficulty
* @returns * @returns
*/ */
function curve(acc, stars) { export function getBeatLeaderPP(accuracy, accRating, passRating, techRating) {
var l = 1 - (0.03 * (stars - 3.0)) / 11.0; const modifierBonus = Utils.calculateModifierBonus();
var a = 0.96 * l; const ppValues = getPP(
var f = 1.2 - (0.6 * stars) / 14.0; accuracy,
return Math.pow(Math.log10(l / (l - acc)) / Math.log10(l / (l - a)), f); accRating,
passRating * modifierBonus,
techRating * modifierBonus
);
const pp = inflate(ppValues.passPP + ppValues.accPP + ppValues.techPP);
return isNaN(pp) ? 1024 : pp;
} }
/** /**
* Gets the raw pp from the given score * https://github.com/BeatLeader/beatleader-server/blob/master/Utils/ReplayUtils.cs#L45
* funky shit from ^
* *
* @param {Number} acc the accuracy of the score * @param {Number} accuracy the accuracy of the score
* @param {Number} stars the ranked star count * @param {Number} accRating the acc rating of the difficulty
* @param {Number} passRating the pass rating of the difficulty
* @param {Number} techRating the tech rating of the difficulty
* @returns * @returns
*/ */
export function getBeatLeaderPP(acc, stars) { function getPP(accuracy, accRating, passRating, techRating) {
if (stars === undefined || acc === undefined) { let passPP = 15.2 * Math.exp(Math.pow(passRating, 1 / 2.62)) - 30;
return undefined; if (isNaN(passPP) || passPP == Infinity) {
passPP = 0;
} }
const modifierBonus = Utils.calculateModifierBonus(); let accPP = curve(accuracy) * accRating * 34;
let rawPP = curve(acc, stars - 0.5) * (stars + 0.5) * 42; let techPP = Math.exp(1.9 * accuracy) * techRating;
let fullPP =
curve(acc, stars * modifierBonus - 0.5) *
(stars * modifierBonus + 0.5) *
42;
const isNegativeAcc = acc < 0; return {
if (isNegativeAcc) { passPP: passPP,
acc *= -1; accPP: accPP,
} techPP: techPP,
};
if (isNaN(rawPP) || rawPP == Infinity) { }
return 1024;
} /**
if (isNaN(fullPP) || fullPP == Infinity) { * https://github.com/BeatLeader/beatleader-server/blob/master/Utils/ReplayUtils.cs#L45
return 1024; * funky shit from ^
} *
* @param {Number} acc the accuracy of the score
if (isNegativeAcc) { * @returns something
fullPP *= -1; */
} function curve(acc) {
let i = 0;
return fullPP; for (; i < ppCurve.length; i++) {
if (ppCurve[i][0] <= acc) {
break;
}
}
if (i == 0) {
i = 1;
}
const middle_dis =
(acc - ppCurve[i - 1][0]) / (ppCurve[i][0] - ppCurve[i - 1][0]);
return ppCurve[i - 1][1] + middle_dis * (ppCurve[i][1] - ppCurve[i - 1][1]);
}
/**
* https://github.com/BeatLeader/beatleader-server/blob/master/Utils/ReplayUtils.cs#L77
* funky shit from ^
*
* @param {Number} peepee
* @returns funny number, idk
*/
function inflate(peepee) {
return (650 * Math.pow(peepee, 1.3)) / Math.pow(650, 1.3);
} }

@ -33,6 +33,9 @@ export default async function handler(req, res) {
difficulty: difficulty, difficulty: difficulty,
stars: json.stars, stars: json.stars,
modifiers: json.modifiers, modifiers: json.modifiers,
passRating: json.passRating,
accRating: json.accRating,
techRating: json.techRating,
}); });
} }
@ -54,6 +57,10 @@ export default async function handler(req, res) {
const json = reesponse.data; const json = reesponse.data;
let starCount = undefined; let starCount = undefined;
let modifiers = undefined; let modifiers = undefined;
let passRating = undefined;
let accRating = undefined;
let techRating = undefined;
for (const diff of json.difficulties) { for (const diff of json.difficulties) {
if ( if (
diff.difficultyName === difficulty && diff.difficultyName === difficulty &&
@ -61,6 +68,9 @@ export default async function handler(req, res) {
) { ) {
starCount = diff.stars; starCount = diff.stars;
modifiers = diff.modifierValues; modifiers = diff.modifierValues;
passRating = diff.passRating;
accRating = diff.accRating;
techRating = diff.techRating;
} }
} }
if (starCount === undefined) { if (starCount === undefined) {
@ -74,6 +84,9 @@ export default async function handler(req, res) {
JSON.stringify({ JSON.stringify({
stars: starCount, stars: starCount,
modifiers: modifiers, modifiers: modifiers,
passRating: passRating,
accRating: accRating,
techRating: techRating,
}) })
); );
console.log( console.log(
@ -87,5 +100,8 @@ export default async function handler(req, res) {
difficulty: difficulty, difficulty: difficulty,
stars: starCount, stars: starCount,
modifiers: modifiers, modifiers: modifiers,
passRating: passRating,
accRating: accRating,
techRating: techRating,
}); });
} }

@ -20,6 +20,9 @@ interface SongDataState {
beatleader: { beatleader: {
stars: Number | undefined; stars: Number | undefined;
modifiers: Object; modifiers: Object;
passRating: number | undefined;
accRating: number | undefined;
techRating: number | undefined;
}; };
}; };
mapArt: string; mapArt: string;
@ -82,6 +85,9 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
beatleader: { beatleader: {
stars: 0, stars: 0,
modifiers: {}, modifiers: {},
passRating: undefined,
accRating: undefined,
techRating: undefined,
}, },
}, },
mapArt: "", mapArt: "",
@ -134,6 +140,16 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
} }
const { bsr, mapArt } = mapDataresponse.data.data; const { bsr, mapArt } = mapDataresponse.data.data;
console.log({
beatleader: {
stars: beatLeaderLeaderboardData.stars,
modifiers: beatLeaderLeaderboardData.modifiers,
passRating: beatLeaderLeaderboardData.passRating,
accRating: beatLeaderLeaderboardData.accRating,
techRating: beatLeaderLeaderboardData.techRating,
},
});
set({ set({
isLoading: false, isLoading: false,
hasError: hasError, hasError: hasError,
@ -141,6 +157,9 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
beatleader: { beatleader: {
stars: beatLeaderLeaderboardData.stars, stars: beatLeaderLeaderboardData.stars,
modifiers: beatLeaderLeaderboardData.modifiers, modifiers: beatLeaderLeaderboardData.modifiers,
passRating: beatLeaderLeaderboardData.passRating,
accRating: beatLeaderLeaderboardData.accRating,
techRating: beatLeaderLeaderboardData.techRating,
}, },
scoresaber: { scoresaber: {
stars: scoreSaberLeaderboardData.stars, stars: scoreSaberLeaderboardData.stars,
@ -244,6 +263,9 @@ export const useSongDataStore = create<SongDataState>()((set) => ({
beatleader: { beatleader: {
stars: undefined, stars: undefined,
modifiers: {}, modifiers: {},
passRating: undefined,
accRating: undefined,
techRating: undefined,
}, },
}, },
mapArt: "", mapArt: "",

@ -37,7 +37,15 @@ export default class Utils {
return undefined; return undefined;
} }
if (type === "BeatLeader") { if (type === "BeatLeader") {
return getBeatLeaderPP(acc, stars); const leaderboardData =
useSongDataStore.getState().mapLeaderboardData.beatleader;
return getBeatLeaderPP(
acc,
leaderboardData.accRating,
leaderboardData.passRating,
leaderboardData.techRating
);
} }
if (type === "ScoreSaber") { if (type === "ScoreSaber") {
return getScoreSaberPP(acc, stars); return getScoreSaberPP(acc, stars);
@ -48,7 +56,7 @@ export default class Utils {
static calculateModifierBonus() { static calculateModifierBonus() {
const songMods = useSongDataStore.getState().songModifiers; const songMods = useSongDataStore.getState().songModifiers;
const modifierMulipliers = const modifierMulipliers =
useSongDataStore.getState().mapLeaderboardData.modifiers; useSongDataStore.getState().mapLeaderboardData.beatleader.modifiers;
let bonus = 1; let bonus = 1;
// No Fail // No Fail