mirror of
https://github.com/MewoLab/AquaDX.git
synced 2026-02-10 07:57:28 +08:00
[+] Chusan rating calculation
This commit is contained in:
@@ -1,85 +1,151 @@
|
||||
export type GameName = 'mai2' | 'chu3' | 'ongeki' | 'wacca'
|
||||
|
||||
const multTable = {
|
||||
'mai2': [
|
||||
[ 100.5, 22.4, 'SSSp' ],
|
||||
[ 100.0, 21.6, 'SSS' ],
|
||||
[ 99.5, 21.1, 'SSp' ],
|
||||
[ 99, 20.8, 'SS' ],
|
||||
[ 98, 20.3, 'Sp' ],
|
||||
[ 97, 20, 'S' ],
|
||||
[ 94, 16.8, 'AAA' ],
|
||||
[ 90, 15.2, 'AA' ],
|
||||
[ 80, 13.6, 'A' ],
|
||||
[ 75, 12, 'BBB' ],
|
||||
[ 70, 11.2, 'BB' ],
|
||||
[ 60, 9.6, 'B' ],
|
||||
[ 50, 8, 'C' ],
|
||||
[ 40, 6.4, 'D' ],
|
||||
[ 30, 4.8, 'D' ],
|
||||
[ 20, 3.2, 'D' ],
|
||||
[ 10, 1.6, 'D' ],
|
||||
[ 0, 0, 'D' ]
|
||||
],
|
||||
|
||||
// TODO: Fill in multipliers for Chunithm and Ongeki
|
||||
'chu3': [
|
||||
[ 100.75, 0, 'SSS' ],
|
||||
[ 100.0, 0, 'SS' ],
|
||||
[ 97.5, 0, 'S' ],
|
||||
[ 95.0, 0, 'AAA' ],
|
||||
[ 92.5, 0, 'AA' ],
|
||||
[ 90.0, 0, 'A' ],
|
||||
[ 80.0, 0, 'BBB' ],
|
||||
[ 70.0, 0, 'BB' ],
|
||||
[ 60.0, 0, 'B' ],
|
||||
[ 50.0, 0, 'C' ],
|
||||
[ 0.0, 0, 'D' ]
|
||||
],
|
||||
|
||||
'ongeki': [
|
||||
[ 100.75, 0, 'SSS+' ],
|
||||
[ 100.0, 0, 'SSS' ],
|
||||
[ 99.0, 0, 'SS' ],
|
||||
[ 97.0, 0, 'S' ],
|
||||
[ 94.0, 0, 'AAA' ],
|
||||
[ 90.0, 0, 'AA' ],
|
||||
[ 85.0, 0, 'A' ],
|
||||
[ 80.0, 0, 'BBB' ],
|
||||
[ 75.0, 0, 'BB' ],
|
||||
[ 70.0, 0, 'B' ],
|
||||
[ 50.0, 0, 'C' ],
|
||||
[ 0.0, 0, 'D' ]
|
||||
],
|
||||
|
||||
'wacca': [
|
||||
[ 100.0, 0, 'AP' ],
|
||||
[ 98.0, 0, 'SSS' ],
|
||||
[ 95.0, 0, 'SS' ],
|
||||
[ 90.0, 0, 'S' ],
|
||||
[ 85.0, 0, 'AAA' ],
|
||||
[ 80.0, 0, 'AA' ],
|
||||
[ 70.0, 0, 'A' ],
|
||||
[ 60.0, 0, 'B' ],
|
||||
[ 1.0, 0, 'C' ],
|
||||
[ 0.0, 0, 'D' ]
|
||||
]
|
||||
}
|
||||
|
||||
export function getMult(achievement: number, game: GameName) {
|
||||
achievement /= 10000
|
||||
const mt = multTable[game]
|
||||
for (let i = 0; i < mt.length; i++) {
|
||||
if (achievement >= (mt[i][0] as number)) return mt[i]
|
||||
}
|
||||
return [ 0, 0, 0 ]
|
||||
}
|
||||
|
||||
export function roundFloor(achievement: number, game: GameName, digits = 2) {
|
||||
// Round, but if the rounded number reaches the next rank, use floor instead
|
||||
const mult = getMult(achievement, game);
|
||||
achievement /= 10000
|
||||
const rounded = achievement.toFixed(digits);
|
||||
if (getMult(+rounded * 10000, game)[2] === mult[2] && rounded !== '101.0') return rounded;
|
||||
return (+rounded - Math.pow(10, -digits)).toFixed(digits);
|
||||
}
|
||||
import { DATA_HOST } from "./config"
|
||||
import type { MusicMeta } from "./generalTypes"
|
||||
|
||||
export type GameName = 'mai2' | 'chu3' | 'ongeki' | 'wacca'
|
||||
|
||||
const multTable = {
|
||||
'mai2': [
|
||||
[ 100.5, 22.4, 'SSSp' ],
|
||||
[ 100.0, 21.6, 'SSS' ],
|
||||
[ 99.5, 21.1, 'SSp' ],
|
||||
[ 99, 20.8, 'SS' ],
|
||||
[ 98, 20.3, 'Sp' ],
|
||||
[ 97, 20, 'S' ],
|
||||
[ 94, 16.8, 'AAA' ],
|
||||
[ 90, 15.2, 'AA' ],
|
||||
[ 80, 13.6, 'A' ],
|
||||
[ 75, 12, 'BBB' ],
|
||||
[ 70, 11.2, 'BB' ],
|
||||
[ 60, 9.6, 'B' ],
|
||||
[ 50, 8, 'C' ],
|
||||
[ 40, 6.4, 'D' ],
|
||||
[ 30, 4.8, 'D' ],
|
||||
[ 20, 3.2, 'D' ],
|
||||
[ 10, 1.6, 'D' ],
|
||||
[ 0, 0, 'D' ]
|
||||
],
|
||||
|
||||
// TODO: Fill in multipliers for Chunithm and Ongeki
|
||||
'chu3': [
|
||||
[ 100.9, 215, 'SSS+' ],
|
||||
[ 100.75, 200, 'SSS' ],
|
||||
[ 100.0, 0, 'SS' ],
|
||||
[ 97.5, 0, 'S' ],
|
||||
[ 95.0, 0, 'AAA' ],
|
||||
[ 92.5, 0, 'AA' ],
|
||||
[ 90.0, 0, 'A' ],
|
||||
[ 80.0, 0, 'BBB' ],
|
||||
[ 70.0, 0, 'BB' ],
|
||||
[ 60.0, 0, 'B' ],
|
||||
[ 50.0, 0, 'C' ],
|
||||
[ 0.0, 0, 'D' ]
|
||||
],
|
||||
|
||||
'ongeki': [
|
||||
[ 100.75, 0, 'SSS+' ],
|
||||
[ 100.0, 0, 'SSS' ],
|
||||
[ 99.0, 0, 'SS' ],
|
||||
[ 97.0, 0, 'S' ],
|
||||
[ 94.0, 0, 'AAA' ],
|
||||
[ 90.0, 0, 'AA' ],
|
||||
[ 85.0, 0, 'A' ],
|
||||
[ 80.0, 0, 'BBB' ],
|
||||
[ 75.0, 0, 'BB' ],
|
||||
[ 70.0, 0, 'B' ],
|
||||
[ 50.0, 0, 'C' ],
|
||||
[ 0.0, 0, 'D' ]
|
||||
],
|
||||
|
||||
'wacca': [
|
||||
[ 100.0, 0, 'AP' ],
|
||||
[ 98.0, 0, 'SSS' ],
|
||||
[ 95.0, 0, 'SS' ],
|
||||
[ 90.0, 0, 'S' ],
|
||||
[ 85.0, 0, 'AAA' ],
|
||||
[ 80.0, 0, 'AA' ],
|
||||
[ 70.0, 0, 'A' ],
|
||||
[ 60.0, 0, 'B' ],
|
||||
[ 1.0, 0, 'C' ],
|
||||
[ 0.0, 0, 'D' ]
|
||||
]
|
||||
}
|
||||
|
||||
export function getMult(achievement: number, game: GameName) {
|
||||
achievement /= 10000
|
||||
const mt = multTable[game]
|
||||
for (let i = 0; i < mt.length; i++) {
|
||||
if (achievement >= (mt[i][0] as number)) return mt[i]
|
||||
}
|
||||
return [ 0, 0, 0 ]
|
||||
}
|
||||
|
||||
export function roundFloor(achievement: number, game: GameName, digits = 2) {
|
||||
// Round, but if the rounded number reaches the next rank, use floor instead
|
||||
const mult = getMult(achievement, game);
|
||||
achievement /= 10000
|
||||
const rounded = achievement.toFixed(digits);
|
||||
if (getMult(+rounded * 10000, game)[2] === mult[2] && rounded !== '101.0') return rounded;
|
||||
return (+rounded - Math.pow(10, -digits)).toFixed(digits);
|
||||
}
|
||||
|
||||
export function chusanRating(lv: number, score: number) {
|
||||
console.log(lv)
|
||||
lv = lv * 100
|
||||
if (score >= 1009000) return lv + 215; // SSS+
|
||||
if (score >= 1007500) return lv + 200 + (score - 1007500) / 100; // SSS
|
||||
if (score >= 1005000) return lv + 150 + (score - 1005000) / 50; // SS+
|
||||
if (score >= 1000000) return lv + 100 + (score - 1000000) / 100; // SS
|
||||
if (score >= 975000) return lv + (score - 975000) / 250; // S+, S
|
||||
if (score >= 925000) return lv - 300 + (score - 925000) * 3 / 500; // AA
|
||||
if (score >= 900000) return lv - 500 + (score - 900000) * 4 / 500; // A
|
||||
if (score >= 800000) return ((lv - 500) / 2 + (score - 800000) * ((lv - 500) / 2) / (100000)); // BBB
|
||||
return 0; // C
|
||||
}
|
||||
|
||||
interface ParsedComposition {
|
||||
musicId: number
|
||||
diffId: number // ID of the difficulty
|
||||
score: number
|
||||
cutoff: number
|
||||
mult: number
|
||||
rank: string // e.g. 'SSS+'
|
||||
difficulty?: number // Actual difficulty of the map
|
||||
img: string
|
||||
ratingChange?: number // Rating change after playing this map
|
||||
}
|
||||
|
||||
|
||||
export function parseComposition(item: string, meta: MusicMeta, game: GameName): ParsedComposition {
|
||||
// Chuni & ongeki: musicId, difficultId, score
|
||||
// Mai: musicId, level (difficultyId), romVersion, achievement (score)
|
||||
const mapData = item.split(':').map(Number)
|
||||
if (game === 'mai2') mapData.splice(2, 1)
|
||||
const [ musicId, diffId, score ] = mapData
|
||||
|
||||
// Get score multiplier
|
||||
const tup = getMult(score, game)
|
||||
const [ cutoff, mult ] = [ +tup[0], +tup[1] ]
|
||||
const rank = tup[2] as string
|
||||
|
||||
let diff = meta?.notes?.[mapData[1] === 10 ? 0 : mapData[1]]?.lv
|
||||
|
||||
function calcDxChange() {
|
||||
if (!diff) return
|
||||
if (game === 'mai2')
|
||||
return Math.floor(diff * +mult * (Math.min(100.5, mapData[3] / 10000) / 100))
|
||||
if (game === 'chu3')
|
||||
return chusanRating(diff, score) / 100
|
||||
}
|
||||
|
||||
return {
|
||||
musicId,
|
||||
diffId,
|
||||
score,
|
||||
cutoff,
|
||||
mult,
|
||||
rank,
|
||||
difficulty: diff,
|
||||
img: `${DATA_HOST}/d/${game}/music/00${mapData[0].toString().padStart(6, '0').substring(2)}.png`,
|
||||
ratingChange: calcDxChange()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user