Merge pull request #603 from TNG-dev:zkldi/issue-597

Remove KTRating as the default IIDX Rating algorithm, and replace it with KTLampRating.
This commit is contained in:
zkldi
2021-12-21 07:46:07 +00:00
committed by GitHub
41 changed files with 434 additions and 488 deletions
+1 -1
View File
@@ -83,7 +83,7 @@
"rimraf": "3.0.2",
"safe-json-stringify": "1.2.0",
"seq-logging": "^1.1.1",
"tachi-common": "0.3.22",
"tachi-common": "0.3.24",
"ts-node": "10.0.0",
"typescript": "4.3.4",
"winston": "3.3.3",
+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.3.22
tachi-common: 0.3.24
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.22
tachi-common: 0.3.24
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.22:
resolution: {integrity: sha512-nLt1wv65wY5WdB/QXuVNeOOgA8r2c8OrohGEKyylE5wRyWIA8wOVsZJGPxi9Uxm0TqeVDXwBRV8CyV97czZAMA==}
/tachi-common/0.3.24:
resolution: {integrity: sha512-nTfVZeRXqOyVF5viHACawbbrERzwyY88wBxNuhpZVvSL4jTjFy7dSRf/7LnJVDC1XizhUBKfYMlOBXOUXTIzzg==}
dependencies:
monk: 7.3.4
transitivePeerDependencies:
@@ -20,7 +20,6 @@ t.test("#CreateCalculatedData", async (t) => {
prAssert(
res,
{
ktRating: p.aprx(2.65),
ktLampRating: p.equalTo(10),
BPI: "?number",
},
@@ -85,7 +84,6 @@ t.test("#CalculateDataForGamePT", (t) => {
prAssert(
res,
{
ktRating: "?number",
ktLampRating: "?number",
BPI: "?number",
},
@@ -109,7 +107,6 @@ t.test("#CalculateDataForGamePT", (t) => {
prAssert(
res,
{
ktRating: "?number",
ktLampRating: "?number",
BPI: "?number",
},
@@ -149,7 +149,6 @@ async function CalculateDataIIDXSP(
return {
BPI: bpi,
ktRating: CalculateKTRating(dryScore, "iidx", "SP", chart, logger),
ktLampRating: CalculateKTLampRatingIIDX(dryScore, "SP", chart as ChartDocument<"iidx:SP">),
};
}
@@ -181,7 +180,6 @@ async function CalculateDataIIDXDP(
return {
BPI: bpi,
ktRating: CalculateKTRating(dryScore, "iidx", "DP", chart, logger),
ktLampRating: CalculateKTLampRatingIIDX(dryScore, "DP", chart as ChartDocument<"iidx:DP">),
};
}
@@ -218,7 +216,7 @@ async function CalculateDataMuseca(
logger: KtLogger
): Promise<CalculatedData<"museca:Single">> {
return {
ktRating: await CalculateKTRating(dryScore, "museca", "Single", chart, logger),
ktRating: CalculateKTRating(dryScore, "museca", "Single", chart, logger),
};
}
@@ -251,14 +249,14 @@ function CalculateDataGitadora(
};
}
async function CalculateDataDDR(
function CalculateDataDDR(
dryScore: DryScore,
chart: ChartDocument,
logger: KtLogger
): Promise<CalculatedData<"ddr:SP" | "ddr:DP">> {
): CalculatedData<"ddr:SP" | "ddr:DP"> {
return {
MFCP: CalculateMFCP(dryScore, chart, logger),
ktRating: await CalculateKTRating(dryScore, "ddr", chart.playtype, chart, logger),
ktRating: CalculateKTRating(dryScore, "ddr", chart.playtype, chart, logger),
};
}
@@ -267,8 +265,25 @@ export function CalculateDataBMS14K(
chart: ChartDocument,
logger: KtLogger
): CalculatedData<"bms:14K"> {
const ecValue = chart.tierlistInfo["sgl-EC"]?.value ?? 0;
const hcValue = chart.tierlistInfo["sgl-HC"]?.value ?? 0;
const gptConfig = GetGamePTConfig("bms", "7K");
const lampIndex = gptConfig.lamps.indexOf(dryScore.scoreData.lamp);
if (lampIndex >= IIDX_LAMPS.HARD_CLEAR) {
return {
sieglinde: Math.max(hcValue, ecValue),
};
} else if (lampIndex >= IIDX_LAMPS.EASY_CLEAR) {
return {
sieglinde: ecValue,
};
}
return {
sieglinde: 0, // @todo #33
sieglinde: 0,
};
}
@@ -286,7 +301,7 @@ export function CalculateDataBMS7K(
if (lampIndex >= IIDX_LAMPS.HARD_CLEAR) {
return {
sieglinde: Math.max(hcValue, ecValue), // @todo #33
sieglinde: Math.max(hcValue, ecValue),
};
} else if (lampIndex >= IIDX_LAMPS.EASY_CLEAR) {
return {
@@ -3,7 +3,12 @@ import CreateLogCtx from "lib/logger/logger";
import { ChartDocument, Difficulties, Lamps, ScoreDocument } from "tachi-common";
import t from "tap";
import { isApproximately } from "test-utils/asserts";
import { GetKTDataJSON, Testing511SPA, TestingIIDXSPDryScore } from "test-utils/test-data";
import {
CHUNITHMBBKKChart,
GetKTDataJSON,
Testing511SPA,
TestingIIDXSPDryScore,
} from "test-utils/test-data";
import { DryScore } from "../common/types";
import {
CalculateBPI,
@@ -339,7 +344,7 @@ t.test("#CalculateMFCP", (t) => {
t.end();
});
const bbkk = GetKTDataJSON("./tachi/chunithm-bbkk-chart.json");
const bbkk = CHUNITHMBBKKChart;
// unit testing a mathematical function is a square-round-hole problem.
t.test("#CalculateCHUNITHMRating", (t) => {
@@ -374,81 +379,81 @@ t.test("#CalculateVF6", (t) => {
t.end();
});
t.test("#CalculateRating", (t) => {
t.test("Should call the success calculator if percent > pivotPercent", async (t) => {
const r = await CalculateKTRating(
deepmerge(TestingIIDXSPDryScore, { scoreData: { percent: 80 } }),
"iidx",
"SP",
Testing511SPA,
logger
);
t.skip("#CalculateKTRating", (t) => {
// t.test("Should call the success calculator if percent > pivotPercent", async (t) => {
// const r = await CalculateKTRating(
// deepmerge(TestingIIDXSPDryScore, { scoreData: { percent: 80 } }),
// "iidx",
// "SP",
// Testing511SPA,
// logger
// );
t.ok(r > 10, "Should return rating greater than the levelNum of the chart.");
// t.ok(r > 10, "Should return rating greater than the levelNum of the chart.");
t.end();
});
// t.end();
// });
t.test("Should call the fail calculator if percent > pivotPercent", async (t) => {
const r = await CalculateKTRating(
TestingIIDXSPDryScore,
"iidx",
"SP",
Testing511SPA,
logger
);
// t.test("Should call the fail calculator if percent > pivotPercent", async (t) => {
// const r = await CalculateKTRating(
// TestingIIDXSPDryScore,
// "iidx",
// "SP",
// Testing511SPA,
// logger
// );
t.ok(r < 10, "Should return rating less than the levelNum of the chart.");
// t.ok(r < 10, "Should return rating less than the levelNum of the chart.");
t.end();
});
// t.end();
// });
t.test("Should call levelNum if percent === pivotPercent", async (t) => {
const r = await CalculateKTRating(
deepmerge(TestingIIDXSPDryScore, { scoreData: { percent: 77.7777 } }),
"iidx",
"SP",
Testing511SPA,
logger
);
// t.test("Should call levelNum if percent === pivotPercent", async (t) => {
// const r = await CalculateKTRating(
// deepmerge(TestingIIDXSPDryScore, { scoreData: { percent: 77.7777 } }),
// "iidx",
// "SP",
// Testing511SPA,
// logger
// );
t.equal(
// hack for approximate tests
parseFloat(r.toFixed(2)),
10,
"Should return rating exactly that of the levelNum of the chart."
);
// t.equal(
// // hack for approximate tests
// parseFloat(r.toFixed(2)),
// 10,
// "Should return rating exactly that of the levelNum of the chart."
// );
t.end();
});
// t.end();
// });
t.test(
"Should trigger safety if completely invalid percent somehow gets through",
async (t) => {
let r = await CalculateKTRating(
deepmerge(TestingIIDXSPDryScore, { scoreData: { percent: 1000000000 } }),
"iidx",
"SP",
Testing511SPA,
logger
);
// t.test(
// "Should trigger safety if completely invalid percent somehow gets through",
// async (t) => {
// let r = await CalculateKTRating(
// deepmerge(TestingIIDXSPDryScore, { scoreData: { percent: 1000000000 } }),
// "iidx",
// "SP",
// Testing511SPA,
// logger
// );
t.equal(r, 0, "Should safely return 0 and log a warning.");
// t.equal(r, 0, "Should safely return 0 and log a warning.");
r = await CalculateKTRating(
// not high enough to be non-finite but high enough to be > 1000
deepmerge(TestingIIDXSPDryScore, { scoreData: { percent: 200 } }),
"iidx",
"SP",
Testing511SPA,
logger
);
// r = await CalculateKTRating(
// // not high enough to be non-finite but high enough to be > 1000
// deepmerge(TestingIIDXSPDryScore, { scoreData: { percent: 200 } }),
// "iidx",
// "SP",
// Testing511SPA,
// logger
// );
t.equal(r, 0, "Should safely return 0 and log a warning.");
// t.equal(r, 0, "Should safely return 0 and log a warning.");
t.end();
}
);
// t.end();
// }
// );
t.end();
});
@@ -306,7 +306,7 @@ const ratingParameters = {
export function CalculateKTRating(
dryScore: DryScore,
game: "iidx" | "ddr" | "museca",
game: "ddr" | "museca",
playtype: Playtypes[Game],
chart: ChartDocument,
logger: KtLogger
@@ -7,7 +7,7 @@ import ResetDBState from "test-utils/resets";
import { GetKTDataJSON, Testing511SPA, TestingIIDXSPScore } from "test-utils/test-data";
import { CreatePBDoc } from "./create-pb-doc";
let IIDXScore = TestingIIDXSPScore;
const IIDXScore = TestingIIDXSPScore;
const logger = CreateLogCtx(__filename);
@@ -15,10 +15,6 @@ const lamps = GetGamePTConfig("iidx", "SP").lamps;
t.test("#CreatePBDoc", (t) => {
t.beforeEach(ResetDBState);
t.beforeEach(() => {
// monk adds _id onto the file when you import it, so lets try and avoid that
IIDXScore = GetKTDataJSON("./tachi/iidx-score.json");
});
const chartID = Testing511SPA.chartID;
@@ -48,7 +44,6 @@ t.test("#CreatePBDoc", (t) => {
hitMeta: { bp: 1 },
},
calculatedData: {
ktRating: IIDXScore.calculatedData.ktRating,
ktLampRating: 12,
},
};
@@ -4,7 +4,7 @@ import { CreateSessionCalcData } from "./calculated-data";
function ratingwrap(ratings: [number, number][]) {
return ratings.map((e) => ({
calculatedData: {
ktRating: e[0],
BPI: e[0],
ktLampRating: e[1],
},
}));
@@ -23,13 +23,13 @@ t.test("#CreateSessionCalcData", (t) => {
t.test("Should return null if less than 10 scores", (t) => {
const res = CreateSessionCalcData("iidx", "SP", ratingwrap([[1, 2]]));
t.strictSame(res, { BPI: null, ktRating: null, ktLampRating: null });
t.strictSame(res, { BPI: null, ktLampRating: null });
t.end();
});
t.test("Should calculate session performance", (t) => {
const { BPI, ktRating, ktLampRating } = CreateSessionCalcData(
const { BPI, ktLampRating } = CreateSessionCalcData(
"iidx",
"SP",
ratingwrap([
@@ -55,13 +55,11 @@ t.test("#CreateSessionCalcData", (t) => {
);
t.equal(
ktRating,
BPI,
avgbest10([1, 2, 3, 4, 6, 1, 1, 2, 3, 4, 6, 1]),
"Should correctly calculate score performance"
"Should correctly calculate BPI performance"
);
t.equal(BPI, null);
t.end();
});
@@ -45,12 +45,10 @@ const CalculatedDataFunctions: CalculatedDataFunctions = {
iidx: {
SP: (scd) => ({
BPI: AvgBest10Map(scd, "BPI"),
ktRating: AvgBest10Map(scd, "ktRating"),
ktLampRating: AvgBest10Map(scd, "ktLampRating"),
}),
DP: (scd) => ({
BPI: AvgBest10Map(scd, "BPI"),
ktRating: AvgBest10Map(scd, "ktRating"),
ktLampRating: AvgBest10Map(scd, "ktLampRating"),
}),
},
@@ -11,19 +11,11 @@ t.test("#CalculateRatings", (t) => {
t.test("Should return BPI for IIDX", async (t) => {
const res = await CalculateRatings("iidx", "SP", 1, logger);
t.strictSame(
res,
{ BPI: 0, ktRating: 0, ktLampRating: 0 },
"Should return BPI as a custom key."
);
t.strictSame(res, { BPI: 0, ktLampRating: 0 }, "Should return BPI as a custom key.");
const resDP = await CalculateRatings("iidx", "DP", 1, logger);
t.strictSame(
resDP,
{ BPI: 0, ktRating: 0, ktLampRating: 0 },
"Should return BPI as a custom key."
);
t.strictSame(resDP, { BPI: 0, ktLampRating: 0 }, "Should return BPI as a custom key.");
t.end();
});
@@ -72,12 +72,10 @@ const RatingFunctions: RatingFunctions = {
iidx: {
SP: async (g, p, u) => ({
BPI: await LazyMeanN("BPI", 20)(g, p, u),
ktRating: await LazyMeanN("ktRating", 20)(g, p, u),
ktLampRating: await LazyMeanN("ktLampRating", 20)(g, p, u),
}),
DP: async (g, p, u) => ({
BPI: await LazyMeanN("BPI", 20)(g, p, u),
ktRating: await LazyMeanN("ktRating", 20)(g, p, u),
ktLampRating: await LazyMeanN("ktLampRating", 20)(g, p, u),
}),
},
@@ -31,7 +31,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
game: "iidx",
playtype: "SP",
userID: 1,
ratings: { ktRating: 0, ktLampRating: 0 },
ratings: { ktLampRating: 0 },
classes: {},
},
"Should insert an appropriate game-stats object"
@@ -57,7 +57,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
game: "iidx",
playtype: "SP",
userID: 1,
ratings: { ktRating: 0, ktLampRating: 0 },
ratings: { ktLampRating: 0 },
classes: {},
});
@@ -69,8 +69,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
deepmerge(TestingIIDXSPScorePB, {
chartID: crypto.randomBytes(20).toString("hex"),
calculatedData: {
ktRating: e,
ktLampRating: 0,
ktLampRating: e,
},
})
)
@@ -88,7 +87,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
game: "iidx",
playtype: "SP",
userID: 1,
ratings: { ktRating: ratings.reduce((a, r) => a + r, 0) / 20, ktLampRating: 0 },
ratings: { ktLampRating: ratings.reduce((a, r) => a + r, 0) / 20 },
classes: {},
},
"Should update the game-stats object"
@@ -104,7 +103,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
game: "iidx",
playtype: "SP",
userID: 1,
ratings: { ktRating: 0, ktLampRating: 0 },
ratings: { ktLampRating: 0 },
classes: {},
});
@@ -139,7 +138,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
game: "iidx",
playtype: "SP",
userID: 1,
ratings: { ktRating: 0, ktLampRating: 0 },
ratings: { ktLampRating: 0 },
classes: {
dan: 18,
},
@@ -157,7 +156,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
game: "iidx",
playtype: "SP",
userID: 1,
ratings: { ktRating: 0, ktLampRating: 0 },
ratings: { ktLampRating: 0 },
classes: {
dan: 17,
},
@@ -193,7 +192,7 @@ t.test("#UpdateUsersGamePlaytypeStats", (t) => {
game: "iidx",
playtype: "SP",
userID: 1,
ratings: { ktRating: 0, ktLampRating: 0 },
ratings: { ktLampRating: 0 },
classes: {
dan: 18,
},
@@ -6,6 +6,7 @@ import db from "external/mongo/db";
import UpdateScore from "./update-score";
import { CreateScoreID } from "lib/score-import/framework/score-importing/score-id";
import ResetDBState from "test-utils/resets";
import { rootLogger } from "lib/logger/logger";
const mockImportDocument: ImportDocument = {
userID: 1,
@@ -18,7 +19,7 @@ const mockImportDocument: ImportDocument = {
importID: "mockImportID",
importType: "file/batch-manual",
milestoneInfo: [],
scoreIDs: ["scoreid_1", "scoreid_2"],
scoreIDs: ["TESTING_SCORE_ID", "scoreid_2"],
timeFinished: 1000,
timeStarted: 0,
game: "iidx",
@@ -37,7 +38,7 @@ const mockSessionDocument: SessionDocument = {
scoreInfo: [
{
isNewScore: true,
scoreID: "scoreid_1",
scoreID: "TESTING_SCORE_ID",
},
{
isNewScore: true,
@@ -55,7 +56,9 @@ t.test("#UpdateScore", (t) => {
t.beforeEach(ResetDBState);
t.test("Should update a score and everything pertaining to it", async (t) => {
TestingIIDXSPScore.scoreID = "scoreid_1";
// n.b. this must be here!! otherwise we get nonsense errors due to _id bson
// errors.
delete TestingIIDXSPScore._id;
const score = deepmerge<ScoreDocument>(TestingIIDXSPScore, {
scoreData: { score: 1020 },
@@ -66,8 +69,6 @@ t.test("#UpdateScore", (t) => {
await db.imports.insert(mockImportDocument);
await db.sessions.insert(mockSessionDocument);
await db.scores.insert(TestingIIDXSPScore);
// This function doesn't return anything, instead,
// we need to check external state.
await UpdateScore(TestingIIDXSPScore, score);
@@ -82,6 +83,8 @@ t.test("#UpdateScore", (t) => {
scoreID: newScoreID,
});
rootLogger.crit("foo", await db.scores.find({}));
t.hasStrict(
dbNewScore?.scoreData,
score.scoreData,
@@ -95,7 +98,7 @@ t.test("#UpdateScore", (t) => {
t.strictSame(
dbImport?.scoreIDs,
[newScoreID, "scoreid_2"],
"Should update scoreid_1 to the new hash."
"Should update TESTING_SCORE_ID to the new hash."
);
const dbSession = await db.sessions.findOne({
@@ -114,7 +117,7 @@ t.test("#UpdateScore", (t) => {
scoreID: "scoreid_2",
},
],
"Should update scoreid_1 to the new hash."
"Should update TESTING_SCORE_ID to the new hash."
);
t.end();
@@ -74,6 +74,7 @@ export default async function UpdateScore(oldScore: ScoreDocument, newScore: Sco
{ $set: newScore }
);
} catch (err) {
logger.error(err);
logger.warn(
`Score ID ${newScoreID} already existed -- this update caused a collision. Removing old score and updating old references anyway.`
);
@@ -74,7 +74,6 @@ t.test("GET /api/v1/games/:game/:playtype/leaderboard", (t) => {
playtype: "SP",
ratings: {
BPI: 100,
ktRating: 50,
},
},
{
@@ -83,7 +82,6 @@ t.test("GET /api/v1/games/:game/:playtype/leaderboard", (t) => {
playtype: "SP",
ratings: {
BPI: 50,
ktRating: 100,
},
},
] as UserGameStats[]);
@@ -14,7 +14,7 @@ import {
t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs/best", (t) => {
t.beforeEach(ResetDBState);
t.test("Should return a users best 100 personal bests.", async (t) => {
t.test("Should return a user's best 100 personal bests.", async (t) => {
const mockPBs: PBScoreDocument[] = [];
for (let i = 0; i < 200; i++) {
@@ -26,7 +26,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs/best", (t) => {
chartID: i.toString(), // hack to generate some random chartIDs
songID: Testing511Song.id,
calculatedData: {
ktRating: i,
ktLampRating: i,
},
} as PBScoreDocument);
}
@@ -100,7 +100,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/pbs", (t) => {
chartID: charts[i].chartID,
songID: charts[i].songID,
calculatedData: {
ktRating: i,
ktLampRating: i,
},
} as PBScoreDocument);
}
@@ -33,7 +33,6 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype", (t) => {
mostRecentScore: null,
totalScores: 1,
rankingData: {
ktRating: { ranking: 1, outOf: 1 },
ktLampRating: {
ranking: 1,
outOf: 1,
@@ -97,7 +96,6 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype", (t) => {
},
totalScores: 4,
rankingData: {
ktRating: { ranking: 1, outOf: 1 },
ktLampRating: {
ranking: 1,
outOf: 1,
@@ -147,10 +145,6 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/history", (t) => {
t.strictSame(res.body.body, [
{
rankings: {
ktRating: {
ranking: 1,
outOf: 1,
},
ktLampRating: {
ranking: 1,
outOf: 1,
@@ -99,7 +99,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/scores", (t) => {
chartID: charts[i].chartID,
songID: charts[i].songID,
calculatedData: {
ktRating: i,
ktLampRating: i,
},
} as ScoreDocument);
}
@@ -81,7 +81,7 @@ t.test("GET /api/v1/users/:userID/games/:game/:playtype/sessions/best", (t) => {
playtype: "SP",
userID: 1,
calculatedData: {
ktRating: i,
ktLampRating: i,
},
} as SessionDocument);
}
@@ -50,7 +50,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => {
.patch("/api/v1/users/1/games/iidx/SP/settings")
.set("Authorization", "Bearer api_token")
.send({
preferredScoreAlg: "ktRating",
preferredScoreAlg: "ktLampRating",
});
t.strictSame(
@@ -60,7 +60,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => {
game: "iidx",
playtype: "SP",
preferences: {
preferredScoreAlg: "ktRating",
preferredScoreAlg: "ktLampRating",
preferredSessionAlg: null,
preferredProfileAlg: null,
scoreBucket: null,
@@ -79,7 +79,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => {
playtype: "SP",
});
t.equal(data?.preferences.preferredScoreAlg, "ktRating");
t.equal(data?.preferences.preferredScoreAlg, "ktLampRating");
t.end();
});
@@ -99,7 +99,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => {
.patch("/api/v1/users/1/games/iidx/SP/settings")
.set("Authorization", "Bearer api_token")
.send({
preferredScoreAlg: "ktRating",
preferredScoreAlg: "ktLampRating",
gameSpecific: {
display2DXTra: true,
},
@@ -112,7 +112,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => {
game: "iidx",
playtype: "SP",
preferences: {
preferredScoreAlg: "ktRating",
preferredScoreAlg: "ktLampRating",
preferredSessionAlg: null,
preferredProfileAlg: null,
scoreBucket: null,
@@ -131,7 +131,7 @@ t.test("PATCH /api/v1/users/:userID/games/:game/:playtype/settings", (t) => {
playtype: "SP",
});
t.equal(data?.preferences.preferredScoreAlg, "ktRating");
t.equal(data?.preferences.preferredScoreAlg, "ktLampRating");
t.end();
});
@@ -319,7 +319,7 @@ t.test("GET /api/v1/users/:userID/game-stats", (t) => {
playtype: "SP",
classes: {},
ratings: {
ktRating: 12,
ktLampRating: 12,
},
},
{
@@ -328,7 +328,7 @@ t.test("GET /api/v1/users/:userID/game-stats", (t) => {
playtype: "DP",
classes: {},
ratings: {
ktRating: 11,
ktLampRating: 11,
},
},
{
@@ -7,25 +7,13 @@
"playtype": "Single",
"levelNum": 3,
"level": "3",
"flags": {
"IN BASE GAME": true,
"OMNIMIX": true
},
"data": {
"inGameID": 3
},
"tierlistInfo": {},
"isPrimary": true,
"versions": [
"chuni",
"chuniplus",
"air",
"airplus",
"star",
"starplus",
"amazon",
"amazonplus",
"crystal",
"crystalplus"
"paradiselost"
]
}
]
@@ -11,7 +11,9 @@
"data": {
"inGameID": 1000,
"notecount": 786,
"arcChartID": "CYjwAuz7Yq9"
"arcChartID": "CYjwAuz7Yq9",
"2dxtraSet": null,
"hashSHA256": null
},
"isPrimary": true,
"versions": [
@@ -19,6 +19,7 @@
"heaven",
"vivid",
"konaste"
]
],
"tierlistInfo": {}
}
]
@@ -14,6 +14,7 @@
"hashSHA1": "USC_CHART_HASH"
},
"isPrimary": true,
"versions": []
"versions": [],
"tierlistInfo": {}
}
]
@@ -21,7 +21,6 @@
},
"scoreMeta": {},
"calculatedData": {
"ktRating": 0.1,
"ktLampRating": 5
},
"timeAchieved": null,
+287 -28
View File
@@ -30,18 +30,123 @@ const file = (name: string) => path.join(__dirname, "/test-data", name);
export const GetKTDataJSON = (name: string) => JSON.parse(fs.readFileSync(file(name), "utf-8"));
export const GetKTDataBuffer = (name: string) => fs.readFileSync(file(name));
export const TestingIIDXSPDryScore = GetKTDataJSON(
"./tachi/iidx-dryscore.json"
) as DryScore<"iidx:SP">;
export const TestingIIDXSPDryScore: DryScore<"iidx:SP"> = {
service: "e-amusement",
game: "iidx",
scoreData: {
score: 786,
lamp: "CLEAR",
percent: 50,
grade: "C",
judgements: {
pgreat: 50,
great: 10,
},
hitMeta: {},
},
scoreMeta: {},
timeAchieved: null,
comment: null,
importType: "file/eamusement-iidx-csv",
};
export const TestingIIDXSPScorePB = GetKTDataJSON(
"./tachi/iidx-scorepb.json"
) as PBScoreDocument<"iidx:SP">;
export const TestingIIDXSPScore = GetKTDataJSON(
"./tachi/iidx-score.json"
) as ScoreDocument<"iidx:SP">;
export const TestingIIDXSPScorePB: PBScoreDocument<"iidx:SP"> = {
chartID: "c2311194e3897ddb5745b1760d2c0141f933e683",
userID: 1,
calculatedData: {
ktLampRating: 0,
BPI: 10.1,
},
composedFrom: {
scorePB: "TESTING_SCORE_ID",
lampPB: "TESTING_SCORE_ID",
},
highlight: false,
isPrimary: true,
scoreData: {
score: 1479,
percent: 94.08396946564885,
esd: 10.64453125,
grade: "AAA",
gradeIndex: 7,
lamp: "EX HARD CLEAR",
lampIndex: 6,
judgements: {
pgreat: 697,
great: 85,
},
hitMeta: {
bp: 2,
},
},
rankingData: {
rank: 1,
outOf: 2,
},
songID: 1,
game: "iidx",
playtype: "SP",
timeAchieved: 10000,
};
export const TestingKsHookSV3CScore = GetKTDataJSON("./kshook-sv3c/base.json") as KsHookSV3CScore;
export const TestingIIDXSPScore: ScoreDocument<"iidx:SP"> = {
service: "foo (DIRECT-MANUAL)",
game: "iidx",
playtype: "SP",
userID: 1,
scoreData: {
score: 786,
lamp: "CLEAR",
percent: 50,
grade: "C",
esd: 30,
gradeIndex: 3,
lampIndex: 4,
judgements: {
pgreat: 50,
great: 10,
},
hitMeta: {},
},
scoreMeta: {},
calculatedData: {
ktLampRating: 5,
},
timeAchieved: 1619454485988,
songID: 1,
chartID: "c2311194e3897ddb5745b1760d2c0141f933e683",
highlight: false,
isPrimary: true,
comment: null,
timeAdded: 1,
scoreID: "TESTING_SCORE_ID",
importType: "ir/direct-manual",
};
export const TestingKsHookSV3CScore: KsHookSV3CScore = {
appeal_id: 2001,
btn_rate: 182,
clear: "CLEAR_EXCESSIVE",
critical: 1184,
difficulty: "DIFFICULTY_ADVANCED",
early: 36,
error: 30,
gauge: 71,
grade: "GRADE_AA_PLUS",
late: 10,
long_rate: 195,
max_chain: 158,
music_id: 1,
near: 46,
player_name: "EE",
rate: "RATE_EXCESSIVE",
score: 9579365,
skill_frame: "SKILL_FRAME_NONE",
skill_level: "SKILL_LEVEL_NONE",
skill_name: 5,
track_no: 0,
vol_rate: 193,
};
export const TestingLR2HookScore: LR2HookScore = {
md5: "38616b85332037cc12924f2ae2840262",
@@ -67,29 +172,167 @@ export const TestingLR2HookScore: LR2HookScore = {
},
};
export const Testing511SPA = GetKTDataJSON("./tachi/iidx-511spa.json") as ChartDocument<"iidx:SP">;
export const Testing511SPA: ChartDocument<"iidx:SP"> = {
rgcID: null,
chartID: "c2311194e3897ddb5745b1760d2c0141f933e683",
difficulty: "ANOTHER",
songID: 1,
playtype: "SP",
levelNum: 10,
level: "10",
data: {
inGameID: 1000,
notecount: 786,
arcChartID: "CYjwAuz7Yq9",
"2dxtraSet": null,
hashSHA256: null,
},
tierlistInfo: {},
isPrimary: true,
versions: [
"27-omni",
"26-omni",
"27",
"26",
"inf",
"16-cs",
"12-cs",
"10-cs",
"8-cs",
"7-cs",
"bmus",
],
};
export const Testing511Song = GetKTDataJSON("./tachi/iidx-511-song.json") as SongDocument<"iidx">;
export const Testing511Song: SongDocument<"iidx"> = {
title: "5.1.1.",
artist: "dj nagureo",
id: 1,
altTitles: [],
searchTerms: [],
data: {
genre: "PIANO AMBIENT",
displayVersion: "1",
},
};
export const TestingAlbidaADV = GetKTDataJSON(
"./tachi/sdvx-albida-chart.json"
) as ChartDocument<"sdvx:Single">;
export const TestingAlbidaADV: ChartDocument<"sdvx:Single"> = {
rgcID: null,
chartID: "5088a4d0e1ee9d0cc2f625934306e45b1a60699b",
difficulty: "ADV",
songID: 1,
playtype: "Single",
levelNum: 10,
level: "10",
data: {
inGameID: 1,
arcChartID: "EOZ7FixCDpv",
},
isPrimary: true,
versions: ["booth", "inf", "gw", "heaven", "vivid", "konaste"],
tierlistInfo: {},
};
export const TestingSDVXAlbidaSong = GetKTDataJSON(
"./tachi/sdvx-albida-song.json"
) as SongDocument<"sdvx">;
export const BMSGazerChart: ChartDocument<"bms:7K"> = {
songID: 27339,
chartID: "88eb6cc5683e2740cbd07f588a5f3db1db8d467b",
rgcID: null,
data: {
notecount: 2256,
hashMD5: "38616b85332037cc12924f2ae2840262",
hashSHA256: "195fe1be5c3e74fccd04dc426e05f8a9cfa8a1059c339d0a23e99f63661f0b7d",
tableFolders: [
{
level: "17",
table: "★",
},
],
},
level: "?",
levelNum: 0,
difficulty: "CHART",
playtype: "7K",
isPrimary: true,
versions: [],
tierlistInfo: {},
};
export const TestingDoraChart = GetKTDataJSON(
"./tachi/gitadora-ithinkabtyou.json"
) as ChartDocument<"gitadora:Dora">;
export const CHUNITHMBBKKChart: ChartDocument<"chunithm:Single"> = {
rgcID: null,
chartID: "192b96bdb6150f80ba6412ce02df1249e16c0cb0",
difficulty: "BASIC",
songID: 3,
playtype: "Single",
levelNum: 3,
level: "3",
data: {
inGameID: 3,
},
tierlistInfo: {},
isPrimary: true,
versions: ["paradiselost"],
};
export const TestingSDVXSingleDryScore = GetKTDataJSON(
"./tachi/sdvx-dryscore.json"
) as DryScore<"sdvx:Single">;
export const TestingSDVXAlbidaSong: SongDocument<"sdvx"> = {
title: "ALBIDA Powerless Mix",
artist: "無力P",
id: 1,
altTitles: [],
searchTerms: ["albida_muryoku", "アルビダパワーレスミックス"],
data: {
displayVersion: "booth",
},
};
export const TestingGITADORADoraDryScore = GetKTDataJSON(
"./tachi/gitadora-dryscore.json"
) as DryScore<"gitadora:Dora">;
export const TestingDoraChart: ChartDocument<"gitadora:Dora"> = {
songID: 0,
chartID: "29f0bfab357ba54e3fd0176fb3cbc578c9ec8df5",
difficulty: "BASIC",
playtype: "Dora",
levelNum: 1.6,
level: "1.60",
data: {
inGameID: 0,
},
isPrimary: true,
rgcID: null,
tierlistInfo: {},
versions: ["nextage"],
};
export const TestingSDVXSingleDryScore: DryScore<"sdvx:Single"> = {
service: "e-amusement",
game: "sdvx",
scoreData: {
score: 95000000,
lamp: "EXCESSIVE CLEAR",
percent: 95,
grade: "AA+",
judgements: {},
hitMeta: {},
},
scoreMeta: {},
timeAchieved: null,
comment: null,
importType: "file/batch-manual",
};
export const TestingGITADORADoraDryScore: DryScore<"gitadora:Dora"> = {
service: "fake-service",
game: "gitadora",
scoreData: {
score: 70.76,
lamp: "CLEAR",
percent: 70.76,
grade: "B",
judgements: {},
hitMeta: {},
},
scoreMeta: {},
timeAchieved: null,
comment: null,
importType: "file/batch-manual",
};
export const TestingIIDXEamusementCSV26 = GetKTDataBuffer(
"./eamusement-iidx-csv/pre-leggendaria.csv"
@@ -102,7 +345,23 @@ export const TestingSDVXEamusementCSV = GetKTDataBuffer(
"./eamusement-sdvx-csv/exceed-gear-score.csv"
);
export const TestingBarbatosScore: BarbatosScore = GetKTDataJSON("./barbatos/base.json");
export const TestingBarbatosScore: BarbatosScore = {
difficulty: 1,
level: 10,
song_id: 1,
max_chain: 50,
critical: 50,
near_total: 30,
near_fast: 20,
near_slow: 10,
score: 9500000,
error: 5,
percent: 81,
did_fail: false,
clear_type: 2,
gauge_type: 1,
is_skill_analyzer: false,
};
export const HC511Goal: GoalDocument = {
charts: {
@@ -1,21 +0,0 @@
{
"rgcID": null,
"chartID": "192b96bdb6150f80ba6412ce02df1249e16c0cb0",
"difficulty": "BASIC",
"songID": 3,
"playtype": "Single",
"levelNum": 3,
"level": "3",
"flags": {
"IN BASE GAME": true,
"OMNIMIX": true
},
"data": {
"inGameID": 3
},
"isPrimary": true,
"versions": [
"crystal",
"crystalplus"
]
}
@@ -1,11 +0,0 @@
{
"title": "B.B.K.K.B.K.K.",
"artist": "nora2r",
"id": 3,
"altTitles": [],
"searchTerms": [],
"data": {
"genre": "VARIETY",
"displayVersion": "chuni"
}
}
@@ -1,18 +0,0 @@
{
"rgcID": null,
"chartID": "48024d36bbe76c9fed09c3ffdc19412925d1efd3",
"difficulty": "DIFFICULT",
"songID": 10,
"playtype": "SP",
"levelNum": 6,
"level": "6",
"data": {
"inGameID": "puty",
"songHash": "DQlQ1DlPbq900oqdOo8l0d6I1lIOl99l",
"arcChartID": "BD_uIVl5USj"
},
"isPrimary": true,
"versions": [
"a"
]
}
@@ -1,12 +0,0 @@
{
"title": "PUT YOUR FAITH IN ME",
"artist": "UZI-LAY",
"id": 10,
"altTitles": [],
"searchTerms": [
"putyourfaithinme"
],
"data": {
"displayVersion": "2"
}
}
@@ -1,17 +0,0 @@
{
"service": "fake-service",
"game": "gitadora",
"scoreData": {
"score": 70.76,
"lamp": "CLEAR",
"percent": 70.76,
"grade": "B",
"esd": 30,
"judgements": {},
"hitMeta": {}
},
"scoreMeta": {},
"timeAchieved": null,
"comment": null,
"importType": "file/batch-manual"
}
@@ -1,34 +0,0 @@
{
"id": 0,
"chartID": "29f0bfab357ba54e3fd0176fb3cbc578c9ec8df5",
"difficulty": "BASIC",
"playtype": "Dora",
"levelNum": 1.6,
"level": "1.60",
"notedata": {
"notecount": 169,
"objects": {
"LEFT CYMBAL": 54,
"HI HAT": 78,
"LEFT PEDAL": 24,
"SNARE": 1,
"HI TOM": 1,
"RIGHT PEDAL": 2,
"LOW TOM": 4,
"FLOOR TOM": 5,
"RIGHT CYMBAL": 0
}
},
"length": null,
"bpmMin": 117,
"bpmMax": 117,
"monoBPM": true,
"indicators": {},
"flags": {
"IN BASE GAME": true
},
"internals": {
"inGameID": "0",
"inGameINTID": 0
}
}
@@ -1,11 +0,0 @@
{
"title": "5.1.1.",
"artist": "dj nagureo",
"id": 1,
"altTitles": [],
"searchTerms": [],
"data": {
"genre": "PIANO AMBIENT",
"displayVersion": "1"
}
}
@@ -1,29 +0,0 @@
{
"rgcID": null,
"chartID": "c2311194e3897ddb5745b1760d2c0141f933e683",
"difficulty": "ANOTHER",
"songID": 1,
"playtype": "SP",
"levelNum": 10,
"level": "10",
"data": {
"inGameID": 1000,
"notecount": 786,
"arcChartID": "CYjwAuz7Yq9"
},
"tierlistInfo": {},
"isPrimary": true,
"versions": [
"27-omni",
"26-omni",
"27",
"26",
"inf",
"16-cs",
"12-cs",
"10-cs",
"8-cs",
"7-cs",
"bmus"
]
}
@@ -1,19 +0,0 @@
{
"service": "e-amusement",
"game": "iidx",
"scoreData": {
"score": 786,
"lamp": "CLEAR",
"percent": 50,
"grade": "C",
"judgements": {
"pgreat": 50,
"great": 10
},
"hitMeta": {}
},
"scoreMeta": {},
"timeAchieved": null,
"comment": null,
"importType": "iidx:eamusement-csv"
}
@@ -1,35 +0,0 @@
{
"service": "foo (DIRECT-MANUAL)",
"game": "iidx",
"playtype": "SP",
"difficulty": "ANOTHER",
"userID": 1,
"scoreData": {
"score": 786,
"lamp": "CLEAR",
"percent": 50,
"grade": "C",
"esd": 30,
"gradeIndex": 3,
"lampIndex": 4,
"judgements": {
"pgreat": 50,
"great": 10
},
"hitMeta": {}
},
"scoreMeta": {},
"calculatedData": {
"ktRating": 0.1,
"ktLampRating": 5
},
"timeAchieved": 1619454485988,
"songID": 1,
"chartID": "c2311194e3897ddb5745b1760d2c0141f933e683",
"highlight": false,
"isPrimary": true,
"comment": null,
"timeAdded": 1,
"scoreID": "TESTING_SCORE_ID",
"importType": "ir/direct-manual"
}
@@ -1,38 +0,0 @@
{
"chartID": "c2311194e3897ddb5745b1760d2c0141f933e683",
"userID": 1,
"calculatedData": {
"ktRating": 0,
"ktLampRating": 0,
"BPI": 10.10
},
"composedFrom": {
"scorePB": "TESTING_SCORE_ID",
"lampPB": "TESTING_SCORE_ID"
},
"highlight": false,
"isPrimary": true,
"scoreData": {
"score": 1479,
"percent": 94.08396946564885,
"esd": 10.64453125,
"grade": "AAA",
"gradeIndex": 7,
"lamp": "EX HARD CLEAR",
"lampIndex": 6,
"judgements": {
"pgreat": 697,
"great": 85
},
"hitMeta": {
"bp": 2
}
},
"rankingData": {
"rank": 1,
"outOf": 2
},
"songID": 1,
"game": "iidx",
"playtype": "SP"
}
@@ -1,22 +0,0 @@
{
"rgcID": null,
"chartID": "5088a4d0e1ee9d0cc2f625934306e45b1a60699b",
"difficulty": "ADV",
"songID": 1,
"playtype": "Single",
"levelNum": 10,
"level": "10",
"data": {
"inGameID": 1,
"arcChartID": "EOZ7FixCDpv"
},
"isPrimary": true,
"versions": [
"booth",
"inf",
"gw",
"heaven",
"vivid",
"konaste"
]
}
@@ -1,13 +0,0 @@
{
"title": "ALBIDA Powerless Mix",
"artist": "無力P",
"id": 1,
"altTitles": [],
"searchTerms": [
"albida_muryoku",
"アルビダパワーレスミックス"
],
"data": {
"displayVersion": "booth"
}
}
@@ -1,17 +0,0 @@
{
"service": "e-amusement",
"game": "sdvx",
"scoreData": {
"score": 95000000,
"lamp": "EXCESSIVE CLEAR",
"percent": 95,
"grade": "AA+",
"esd": null,
"judgements": {},
"hitMeta": {}
},
"scoreMeta": {},
"timeAchieved": null,
"comment": null,
"importType": "file/batch-manual"
}