From ad5050370bc548e8e580e73c066d03ddf2524d9c Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Tue, 21 Dec 2021 08:31:25 +0000 Subject: [PATCH 1/2] First steps of WACCA Support --- server/.github/test.conf.json5 | 1 + server/package.json | 2 +- server/pnpm-lock.yaml | 8 +-- server/src/external/mongo/db.ts | 2 + server/src/external/mongo/schemas.ts | 11 +++ server/src/lib/constants/classes.ts | 12 ++++ .../calculated-data/calculated-data.ts | 41 ++++++++++- .../framework/sessions/calculated-data.ts | 5 ++ .../user-game-stats/builtin-class-handlers.ts | 40 ++++++++++- .../framework/user-game-stats/classes.ts | 4 ++ .../framework/user-game-stats/rating.ts | 71 +++++++++++++++++++ 11 files changed, 189 insertions(+), 8 deletions(-) diff --git a/server/.github/test.conf.json5 b/server/.github/test.conf.json5 index c134c4fbd..7f7698450 100644 --- a/server/.github/test.conf.json5 +++ b/server/.github/test.conf.json5 @@ -46,6 +46,7 @@ "bms", "chunithm", "usc", + "wacca", ], IMPORT_TYPES: [ "file/eamusement-iidx-csv", diff --git a/server/package.json b/server/package.json index ad3099822..02c3766e5 100644 --- a/server/package.json +++ b/server/package.json @@ -83,7 +83,7 @@ "rimraf": "3.0.2", "safe-json-stringify": "1.2.0", "seq-logging": "^1.1.1", - "tachi-common": "0.3.24", + "tachi-common": "0.4.1", "ts-node": "10.0.0", "typescript": "4.3.4", "winston": "3.3.3", diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index bff659924..0dc5b932f 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -63,7 +63,7 @@ specifiers: safe-json-stringify: 1.2.0 seq-logging: ^1.1.1 supertest: 6.1.3 - tachi-common: 0.3.24 + tachi-common: 0.4.1 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.3.24 + tachi-common: 0.4.1 ts-node: 10.0.0_83f53b0a0c5616d3fa00ed4e30b9ce1b typescript: 4.3.4 winston: 3.3.3 @@ -5387,8 +5387,8 @@ packages: strip-ansi: 6.0.0 dev: true - /tachi-common/0.3.24: - resolution: {integrity: sha512-nTfVZeRXqOyVF5viHACawbbrERzwyY88wBxNuhpZVvSL4jTjFy7dSRf/7LnJVDC1XizhUBKfYMlOBXOUXTIzzg==} + /tachi-common/0.4.1: + resolution: {integrity: sha512-AREsya2jvgpBq036I17XUeR/jF+lKKJ+CeKCtzKLGE1j8GgyHH6HAy6zP5Vzw6eXRXknubCgr/xFbq+LDqWPmw==} dependencies: monk: 7.3.4 transitivePeerDependencies: diff --git a/server/src/external/mongo/db.ts b/server/src/external/mongo/db.ts index b599be8db..0a4d61f1c 100644 --- a/server/src/external/mongo/db.ts +++ b/server/src/external/mongo/db.ts @@ -122,6 +122,7 @@ const songs = { popn: monkDB.get(`songs-popn`), sdvx: monkDB.get(`songs-sdvx`), usc: monkDB.get(`songs-usc`), + wacca: monkDB.get(`songs-wacca`), }; const charts = { @@ -136,6 +137,7 @@ const charts = { popn: monkDB.get(`charts-popn`), sdvx: monkDB.get(`charts-sdvx`), usc: monkDB.get(`charts-usc`), + wacca: monkDB.get(`charts-wacca`), }; const db = { diff --git a/server/src/external/mongo/schemas.ts b/server/src/external/mongo/schemas.ts index c663cf169..796c9c3fe 100644 --- a/server/src/external/mongo/schemas.ts +++ b/server/src/external/mongo/schemas.ts @@ -767,6 +767,12 @@ export const DatabaseSchemas: Record = { displayVersion: "string", }) ), + "songs-wacca": prSchemaify( + PR_SongDocument({ + genre: "string", + displayVersion: "?string", + }) + ), "charts-iidx": (self) => { const playtype = getPlaytype("iidx", self); @@ -849,6 +855,11 @@ export const DatabaseSchemas: Record = { }) )(self); }, + "charts-wacca": prSchemaify( + PR_ChartDocument("wacca", "Single", { + releaseVersion: p.isIn("wacca", "s", "lily", "lily-r", "reverse"), + }) + ), goals: prSchemaify({ game: p.isIn(games), playtype: isValidPlaytype, diff --git a/server/src/lib/constants/classes.ts b/server/src/lib/constants/classes.ts index 2f48f3aaa..7e787c6b8 100644 --- a/server/src/lib/constants/classes.ts +++ b/server/src/lib/constants/classes.ts @@ -172,3 +172,15 @@ export enum CHUNITHM_COLOURS { PLATINUM, RAINBOW, } + +export enum WACCA_COLOURS { + ASH, + NAVY, + YELLOW, + RED, + PURPLE, + BLUE, + SILVER, + GOLD, + RAINBOW, +} diff --git a/server/src/lib/score-import/framework/calculated-data/calculated-data.ts b/server/src/lib/score-import/framework/calculated-data/calculated-data.ts index 9a2c14240..c6dc8b0ac 100644 --- a/server/src/lib/score-import/framework/calculated-data/calculated-data.ts +++ b/server/src/lib/score-import/framework/calculated-data/calculated-data.ts @@ -3,13 +3,13 @@ import { KtLogger } from "lib/logger/logger"; import { ChartDocument, Game, + GetGamePTConfig, Grades, IDStrings, + IIDX_LAMPS, Lamps, Playtypes, ScoreDocument, - GetGamePTConfig, - IIDX_LAMPS, } from "tachi-common"; import { HasOwnProperty } from "utils/misc"; import { DryScore } from "../common/types"; @@ -93,6 +93,9 @@ const CalculatedDataFunctions: CalculatedDataFunctions = { Controller: CalculateDataSDVXorUSC, Keyboard: CalculateDataSDVXorUSC, }, + wacca: { + Single: CalculateDataWACCA, + }, }; // Creates Game-Specific calculatedData for the provided game & playtype. @@ -323,3 +326,37 @@ export function CalculateDataBMS7K( // jubility: 0, // @todo #163 Jubeat Jubility // }; // } + +function CalculateDataWACCA( + dryScore: DryScore, + chart: ChartDocument, + logger: KtLogger +): CalculatedData<"wacca:Single"> { + let scoreCoef = 1; + + // See https://github.com/TNG-dev/tachi-server/issues/598. + // This is genuinely how the game works. + if (scoreCoef >= 990_000) { + scoreCoef = 4; + } else if (scoreCoef >= 980_000) { + scoreCoef = 3.75; + } else if (scoreCoef >= 970_000) { + scoreCoef = 3.5; + } else if (scoreCoef >= 960_000) { + scoreCoef = 3.25; + } else if (scoreCoef >= 950_000) { + scoreCoef = 3; + } else if (scoreCoef >= 940_000) { + scoreCoef = 2.75; + } else if (scoreCoef >= 920_000) { + scoreCoef = 2.5; + } else if (scoreCoef >= 900_000) { + scoreCoef = 2; + } else if (scoreCoef >= 850_000) { + scoreCoef = 1.5; + } + + // That's... it?? + // How discrete. How boring! + return { rate: scoreCoef * chart.levelNum }; +} diff --git a/server/src/lib/score-import/framework/sessions/calculated-data.ts b/server/src/lib/score-import/framework/sessions/calculated-data.ts index 61317b393..de5b680ef 100644 --- a/server/src/lib/score-import/framework/sessions/calculated-data.ts +++ b/server/src/lib/score-import/framework/sessions/calculated-data.ts @@ -127,6 +127,11 @@ const CalculatedDataFunctions: CalculatedDataFunctions = { }; }, }, + wacca: { + Single: (scd) => ({ + naiveRate: AvgBest10Map(scd, "rate"), + }), + }, }; export function CreateSessionCalcData( diff --git a/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.ts b/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.ts index ef3d820d0..f87fe35dc 100644 --- a/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.ts +++ b/server/src/lib/score-import/framework/user-game-stats/builtin-class-handlers.ts @@ -1,4 +1,9 @@ -import { CHUNITHM_COLOURS, GitadoraColours, SDVXVFClasses } from "lib/constants/classes"; +import { + CHUNITHM_COLOURS, + GitadoraColours, + SDVXVFClasses, + WACCA_COLOURS, +} from "lib/constants/classes"; import { KtLogger } from "lib/logger/logger"; import { Game, integer, Playtypes, ScoreCalculatedDataLookup } from "tachi-common"; import { GameClasses } from "tachi-common/js/game-classes"; @@ -94,6 +99,39 @@ export function GitadoraSkillToColour(sk: number) { return GitadoraColours.WHITE; } +export function CalculateWACCAColour( + game: Game, + playtype: Playtypes[Game], + userID: integer, + ratings: Record +) { + const colour = WACCARateToColour(ratings.naiveRate); + + return { colour }; +} + +function WACCARateToColour(rate: number) { + if (rate >= 2500) { + return WACCA_COLOURS.RAINBOW; + } else if (rate >= 2200) { + return WACCA_COLOURS.GOLD; + } else if (rate >= 1900) { + return WACCA_COLOURS.SILVER; + } else if (rate >= 1600) { + return WACCA_COLOURS.BLUE; + } else if (rate >= 1300) { + return WACCA_COLOURS.PURPLE; + } else if (rate >= 1000) { + return WACCA_COLOURS.RED; + } else if (rate >= 600) { + return WACCA_COLOURS.YELLOW; + } else if (rate >= 300) { + return WACCA_COLOURS.NAVY; + } + + return WACCA_COLOURS.ASH; +} + export function CalculateChunithmColour( game: Game, playtype: Playtypes[Game], diff --git a/server/src/lib/score-import/framework/user-game-stats/classes.ts b/server/src/lib/score-import/framework/user-game-stats/classes.ts index d3968b5ac..d27fd9a10 100644 --- a/server/src/lib/score-import/framework/user-game-stats/classes.ts +++ b/server/src/lib/score-import/framework/user-game-stats/classes.ts @@ -9,6 +9,7 @@ import { CalculateChunithmColour, CalculateGitadoraColour, CalculateSDVXClass, + CalculateWACCAColour, } from "./builtin-class-handlers"; import { ClassHandler, ScoreClasses } from "./types"; @@ -50,6 +51,9 @@ const STATIC_CLASS_HANDLERS: ClassHandlerMap = { Single: CalculateSDVXClass, }, usc: null, + wacca: { + Single: CalculateWACCAColour, + }, }; /** diff --git a/server/src/lib/score-import/framework/user-game-stats/rating.ts b/server/src/lib/score-import/framework/user-game-stats/rating.ts index 9c758062d..cd0649217 100644 --- a/server/src/lib/score-import/framework/user-game-stats/rating.ts +++ b/server/src/lib/score-import/framework/user-game-stats/rating.ts @@ -132,6 +132,12 @@ const RatingFunctions: RatingFunctions = { ktRating: await LazyMeanN("ktRating", 20)(g, p, u), }), }, + wacca: { + Single: async (g, p, u, l) => ({ + naiveRate: await LazySumN("rate", 50)(g, p, u), + rate: await CalculateWACCARate(g, p, u, l), + }), + }, // jubeat: { // Single: async (g, p, u, l) => ({ // jubility: await CalculateJubility(g, p, u, l), @@ -149,6 +155,71 @@ export function CalculateRatings( return RatingFunctions[game][playtype](game, playtype, userID, logger); } +// Wacca has a funny algorithm for rate involving gitadora-style latest chart bonuses, +async function CalculateWACCARate( + game: Game, + playtype: Playtypes[Game], + userID: integer, + logger: KtLogger +) { + const hotChartIDs = ( + await db.charts.wacca.find( + { "data.releaseVersion": "reverse" }, + { projection: { chartID: 1 } } + ) + ).map((e) => e.chartID); + + const coldChartIDs = ( + await db.charts.wacca.find( + { "data.releaseVersion": { $ne: "reverse" } }, + { projection: { chartID: 1 } } + ) + ).map((e) => e.chartID); + + const best15Hot = await db["personal-bests"].find( + { + game, + playtype, + userID, + chartID: { $in: hotChartIDs }, + "calculatedData.rate": { $type: "number" }, + }, + { + sort: { + "calculatedData.rate": -1, + }, + limit: 15, + projection: { + "calculatedData.rate": 1, + }, + } + ); + + const best35Cold = await db["personal-bests"].find( + { + game, + playtype, + userID, + chartID: { $in: coldChartIDs }, + "calculatedData.rate": { $type: "number" }, + }, + { + sort: { + "calculatedData.rate": -1, + }, + limit: 35, + projection: { + "calculatedData.rate": 1, + }, + } + ); + + return ( + best15Hot.reduce((a, r) => a + r.calculatedData.rate!, 0) + + best35Cold.reduce((a, r) => a + r.calculatedData.rate!, 0) + ); +} + async function CalculateGitadoraSkill( game: Game, playtype: Playtypes[Game], From d822ac16a0b30e9f223e4b72b90fed1700e493d4 Mon Sep 17 00:00:00 2001 From: zkldi <20380519+zkldi@users.noreply.github.com> Date: Tue, 21 Dec 2021 08:45:17 +0000 Subject: [PATCH 2/2] Add some mock charts-/songs- data. --- server/src/test-utils/mock-db/charts-wacca.json | 1 + server/src/test-utils/mock-db/songs-wacca.json | 1 + 2 files changed, 2 insertions(+) create mode 100644 server/src/test-utils/mock-db/charts-wacca.json create mode 100644 server/src/test-utils/mock-db/songs-wacca.json diff --git a/server/src/test-utils/mock-db/charts-wacca.json b/server/src/test-utils/mock-db/charts-wacca.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/server/src/test-utils/mock-db/charts-wacca.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/server/src/test-utils/mock-db/songs-wacca.json b/server/src/test-utils/mock-db/songs-wacca.json new file mode 100644 index 000000000..0637a088a --- /dev/null +++ b/server/src/test-utils/mock-db/songs-wacca.json @@ -0,0 +1 @@ +[] \ No newline at end of file