Rough baseline for pop'n music support.

This commit is contained in:
zkldi
2021-12-23 01:57:40 +00:00
parent ca606c4367
commit daf81cf923
10 changed files with 140 additions and 100 deletions
+1 -1
View File
@@ -82,7 +82,7 @@
"rimraf": "3.0.2",
"safe-json-stringify": "1.2.0",
"seq-logging": "^1.1.1",
"tachi-common": "0.4.4",
"tachi-common": "0.4.7",
"ts-node": "10.0.0",
"tsconfig-paths": "3.10.1",
"typescript": "4.3.4",
+4 -4
View File
@@ -63,7 +63,7 @@ specifiers:
safe-json-stringify: 1.2.0
seq-logging: ^1.1.1
supertest: 6.1.3
tachi-common: 0.4.4
tachi-common: 0.4.7
tap: 15.0.9
ts-node: 10.0.0
tsconfig-paths: 3.10.1
@@ -107,7 +107,7 @@ dependencies:
rimraf: 3.0.2
safe-json-stringify: 1.2.0
seq-logging: 1.1.1
tachi-common: 0.4.4
tachi-common: 0.4.7
ts-node: 10.0.0_83f53b0a0c5616d3fa00ed4e30b9ce1b
tsconfig-paths: 3.10.1
typescript: 4.3.4
@@ -5386,8 +5386,8 @@ packages:
strip-ansi: 6.0.0
dev: true
/tachi-common/0.4.4:
resolution: {integrity: sha512-vXLMkMUCEE5ja57HYSLqM7Mhe+oj416JPIKGMdz5ctrwjf7BX4eU8rEAYoBbibvl57SOFLylT6Bm1icyJuQC0w==}
/tachi-common/0.4.7:
resolution: {integrity: sha512-tlntA6tOJSG7l2GNx7byktIma/2upSr+s1tTS1c/t+6IUCED9scsCTwDcqaRSkjzz+fvx3SMg79jyyKIu0Wt9Q==}
dependencies:
monk: 7.3.4
transitivePeerDependencies:
-2
View File
@@ -15,7 +15,6 @@ import {
FolderDocument,
Game,
GoalDocument,
IIDXBPIData,
ImportDocument,
ImportTimingsDocument,
integer,
@@ -157,7 +156,6 @@ const db = {
imports: monkDB.get<ImportDocument>("imports"),
"import-timings": monkDB.get<ImportTimingsDocument>("import-timings"),
sessions: monkDB.get<SessionDocument>("sessions"),
"iidx-bpi-data": monkDB.get<IIDXBPIData>("iidx-bpi-data"),
invites: monkDB.get<InviteCodeDocument>("invites"),
counters: monkDB.get<CounterDocument>("counters"),
"game-stats": monkDB.get<UserGameStats>("game-stats"),
+12 -1
View File
@@ -773,6 +773,12 @@ export const DatabaseSchemas: Record<Databases, ValidatorFunction> = {
displayVersion: "?string",
})
),
"songs-popn": prSchemaify(
PR_SongDocument({
genre: "string",
displayVersion: "?string",
})
),
"charts-iidx": (self) => {
const playtype = getPlaytype("iidx", self);
@@ -857,7 +863,12 @@ export const DatabaseSchemas: Record<Databases, ValidatorFunction> = {
},
"charts-wacca": prSchemaify(
PR_ChartDocument("wacca", "Single", {
releaseVersion: p.isIn("wacca", "s", "lily", "lily-r", "reverse"),
isHot: "boolean",
})
),
"charts-popn": prSchemaify(
PR_ChartDocument("popn", "9B", {
hashSHA256: "?string",
})
),
goals: prSchemaify({
+11
View File
@@ -184,3 +184,14 @@ export enum WACCA_COLOURS {
GOLD,
RAINBOW,
}
export enum POPN_CLASSES {
KITTY,
GRADE_SCHOOL,
DELINQUENT,
DETECTIVE,
IDOL,
GENERAL,
HERMIT,
GOD,
}
@@ -45,57 +45,30 @@ export async function CreateCalculatedData(
}
type CalculatedDataFunctions = {
[G in Game]: {
[P in Playtypes[G]]: (
dryScore: DryScore,
chart: ChartDocument,
logger: KtLogger
) => Promise<ScoreDocument["calculatedData"]> | ScoreDocument["calculatedData"];
};
[I in IDStrings]: (
dryScore: DryScore<I>,
chart: ChartDocument<I>,
logger: KtLogger
) => Promise<ScoreDocument<I>["calculatedData"]> | ScoreDocument<I>["calculatedData"];
};
const CalculatedDataFunctions: CalculatedDataFunctions = {
iidx: {
SP: CalculateDataIIDXSP,
DP: CalculateDataIIDXDP,
},
sdvx: {
Single: CalculateDataSDVXorUSC,
},
// popn: {
// "9B": () => ({}),
// },
museca: {
Single: CalculateDataMuseca,
},
chunithm: {
Single: CalculateDataCHUNITHM,
},
maimai: {
Single: CalculateDataMaimai,
},
gitadora: {
Gita: CalculateDataGitadora,
Dora: CalculateDataGitadora,
},
bms: {
"7K": CalculateDataBMS7K,
"14K": CalculateDataBMS14K,
},
ddr: {
SP: CalculateDataDDR,
DP: CalculateDataDDR,
},
// jubeat: {
// Single: CalculateDataJubeat,
// },
usc: {
Controller: CalculateDataSDVXorUSC,
Keyboard: CalculateDataSDVXorUSC,
},
wacca: {
Single: CalculateDataWACCA,
},
"iidx:SP": CalculateDataIIDXSP,
"iidx:DP": CalculateDataIIDXDP,
"sdvx:Single": CalculateDataSDVXorUSC,
"popn:9B": CalculateDataPopn,
"museca:Single": CalculateDataMuseca,
"chunithm:Single": CalculateDataCHUNITHM,
"maimai:Single": CalculateDataMaimai,
"gitadora:Gita": CalculateDataGitadora,
"gitadora:Dora": CalculateDataGitadora,
"bms:7K": CalculateDataBMS7K,
"bms:14K": CalculateDataBMS14K,
"ddr:SP": CalculateDataDDR,
"ddr:DP": CalculateDataDDR,
"usc:Controller": CalculateDataSDVXorUSC,
"usc:Keyboard": CalculateDataSDVXorUSC,
"wacca:Single": CalculateDataWACCA,
};
// Creates Game-Specific calculatedData for the provided game & playtype.
@@ -110,39 +83,26 @@ export async function CalculateDataForGamePT<G extends Game>(
esd: number | null,
logger: KtLogger
): Promise<ScoreDocument["calculatedData"]> {
const GameRatingFns = CalculatedDataFunctions[game];
if (!HasOwnProperty(GameRatingFns, playtype)) {
logger.error(
`Invalid playtype of ${playtype} given for game ${game} in CalculateDataForGamePT, returning an empty object.`
);
return {};
}
// @ts-expect-error standard game->pt stuff.
return GameRatingFns[playtype](dryScore, chart, logger);
// @ts-expect-error too many minor complains here...
return CalculatedDataFunctions[`${game}:${playtype}` as IDStrings](dryScore, chart, logger);
}
type CalculatedData<I extends IDStrings> = Required<ScoreDocument<I>["calculatedData"]>;
async function CalculateDataIIDXSP(
function CalculateDataIIDXSP(
dryScore: DryScore,
chart: ChartDocument,
chart: ChartDocument<"iidx:SP">,
logger: KtLogger
): Promise<CalculatedData<"iidx:SP">> {
const BPIData = await db["iidx-bpi-data"].findOne({
chartID: chart.chartID,
});
): CalculatedData<"iidx:SP"> {
let bpi;
if (BPIData) {
if (chart.data.kaidenAverage && chart.data.worldRecord) {
bpi = CalculateBPI(
BPIData.kavg,
BPIData.wr,
chart.data.kaidenAverage,
chart.data.worldRecord,
dryScore.scoreData.score,
(chart as ChartDocument<"iidx:SP">).data.notecount * 2,
BPIData.coef
chart.data.bpiCoefficient
);
// kesdc = esd === null ? null : CalculateKESDC(BPIData.kesd, esd); disabled
@@ -156,24 +116,20 @@ async function CalculateDataIIDXSP(
};
}
async function CalculateDataIIDXDP(
function CalculateDataIIDXDP(
dryScore: DryScore,
chart: ChartDocument,
chart: ChartDocument<"iidx:SP" | "iidx:DP">,
logger: KtLogger
): Promise<CalculatedData<"iidx:DP">> {
const BPIData = await db["iidx-bpi-data"].findOne({
chartID: chart.chartID,
});
): CalculatedData<"iidx:DP"> {
let bpi;
if (BPIData) {
if (chart.data.kaidenAverage && chart.data.worldRecord) {
bpi = CalculateBPI(
BPIData.kavg,
BPIData.wr,
chart.data.kaidenAverage,
chart.data.worldRecord,
dryScore.scoreData.score,
(chart as ChartDocument<"iidx:DP">).data.notecount * 2,
BPIData.coef
chart.data.bpiCoefficient
);
// kesdc = esd === null ? null : CalculateKESDC(BPIData.kesd, esd); disabled
@@ -213,11 +169,11 @@ function CalculateDataSDVXorUSC(
};
}
async function CalculateDataMuseca(
function CalculateDataMuseca(
dryScore: DryScore,
chart: ChartDocument,
logger: KtLogger
): Promise<CalculatedData<"museca:Single">> {
): CalculatedData<"museca:Single"> {
return {
ktRating: CalculateKTRating(dryScore, "museca", "Single", chart, logger),
};
@@ -232,11 +188,11 @@ function CalculateDataCHUNITHM(
};
}
async function CalculateDataMaimai(
function CalculateDataMaimai(
dryScore: DryScore,
chart: ChartDocument,
logger: KtLogger
): Promise<CalculatedData<"maimai:Single">> {
): CalculatedData<"maimai:Single"> {
// @todo #373 Add maimai rating algorithms.
return {
ktRating: 0,
@@ -360,3 +316,28 @@ function CalculateDataWACCA(
// How discrete. How boring!
return { rate: scoreCoef * chart.levelNum };
}
function CalculateDataPopn(
dryScore: DryScore,
chart: ChartDocument,
logger: KtLogger
): CalculatedData<"popn:9B"> {
const score = dryScore.scoreData.score;
const lamp = dryScore.scoreData.lamp;
if (score <= 50000) {
return { classPoints: 0 };
}
let clearBonus = 0;
if (lamp === "CLEAR" || lamp === "EASY CLEAR") {
clearBonus = 3000;
} else if (lamp === "FULL COMBO" || lamp === "PERFECT") {
clearBonus = 5000;
}
return {
classPoints: (10_000 * chart.levelNum + score - 50_000 + clearBonus) / 5440,
};
}
@@ -62,9 +62,11 @@ const CalculatedDataFunctions: CalculatedDataFunctions = {
};
},
},
// popn: {
// "9B": () => ({}),
// },
popn: {
"9B": (scd) => ({
classPoints: AvgBest10Map(scd, "classPoints"),
}),
},
museca: {
Single: (scd) => ({
ktRating: AvgBest10Map(scd, "ktRating"),
@@ -1,6 +1,7 @@
import {
CHUNITHM_COLOURS,
GitadoraColours,
POPN_CLASSES,
SDVXVFClasses,
WACCA_COLOURS,
} from "lib/constants/classes";
@@ -132,6 +133,37 @@ function WACCARateToColour(rate: number) {
return WACCA_COLOURS.ASH;
}
export function CalculatePopnClass(
game: Game,
playtype: Playtypes[Game],
userID: integer,
ratings: Record<string, number>
) {
const cls = PopnClassPointsToClass(ratings.naiveClassPoints);
return { class: cls };
}
function PopnClassPointsToClass(points: number) {
if (points < 21) {
return POPN_CLASSES.KITTY;
} else if (points < 34) {
return POPN_CLASSES.GRADE_SCHOOL;
} else if (points < 46) {
return POPN_CLASSES.DELINQUENT;
} else if (points < 59) {
return POPN_CLASSES.DETECTIVE;
} else if (points < 68) {
return POPN_CLASSES.IDOL;
} else if (points < 79) {
return POPN_CLASSES.GENERAL;
} else if (points < 91) {
return POPN_CLASSES.HERMIT;
}
return POPN_CLASSES.GOD;
}
export function CalculateChunithmColour(
game: Game,
playtype: Playtypes[Game],
@@ -8,6 +8,7 @@ import { ReturnClassIfGreater } from "utils/class";
import {
CalculateChunithmColour,
CalculateGitadoraColour,
CalculatePopnClass,
CalculateSDVXClass,
CalculateWACCAColour,
} from "./builtin-class-handlers";
@@ -46,7 +47,9 @@ const STATIC_CLASS_HANDLERS: ClassHandlerMap = {
// },
maimai: null,
museca: null,
// popn: null,
popn: {
"9B": CalculatePopnClass,
},
sdvx: {
Single: CalculateSDVXClass,
},
@@ -119,9 +119,11 @@ const RatingFunctions: RatingFunctions = {
naiveRating: await LazyMeanN("rating", 20)(g, p, u),
}),
},
// popn: {
// "9B": async () => ({}),
// },
popn: {
"9B": async (g, p, u) => ({
naiveClassPoints: await LazyMeanN("classPoints", 20)(g, p, u),
}),
},
museca: {
Single: async (g, p, u) => ({
ktRating: await LazyMeanN("ktRating", 20)(g, p, u),