checkpoint: insanity ascends (35)

127 errors to go
This commit is contained in:
zkldi
2023-01-19 16:39:42 +00:00
parent 2b0e65f0c8
commit 5cbb2d5f8a
131 changed files with 2106 additions and 1886 deletions
+13 -1
View File
@@ -13,7 +13,19 @@ import {
import { GoalsOnChartReturn, GoalsOnFolderReturn } from "types/api-returns";
export function GetPBs(scoreInfo: SessionScoreInfo[]) {
return scoreInfo.filter((e) => e.isNewScore === true || e.lampDelta > 0 || e.scoreDelta > 0);
return scoreInfo.filter((e) => {
if (e.isNewScore) {
return true;
}
for (const v of Object.values(e.deltas)) {
if (v >= 0) {
return true;
}
}
return false;
});
}
export function CreateSongMap<G extends Game = Game>(songs: SongDocument<G>[]) {
+18 -79
View File
@@ -3,20 +3,16 @@ import toast from "react-hot-toast";
import { useHistory } from "react-router-dom";
import {
APIPermissions,
AnyProfileRatingAlg,
AnySessionRatingAlg,
ChartDocument,
Game,
GamePTConfig,
GetGamePTConfig,
Grades,
GPTString,
Playtype,
ProfileRatingLookup,
QuestDocument,
QuestSubscriptionDocument,
ScoreCalculatedDataLookup,
ScoreDocument,
SessionCalculatedDataLookup,
SessionDocument,
integer,
} from "tachi-common";
@@ -56,29 +52,10 @@ export function FormatTables(tables: { table: string; level: string }[]) {
return tables.map((e) => `${e.table}${e.level}`).join(", ");
}
export function FormatGPTRating(
game: Game,
playtype: Playtype,
key: ScoreCalculatedDataLookup[GPTString],
value: number | null
) {
if (value === null) {
return "No Data.";
}
const gptConfig = GetGamePTConfig(game, playtype);
if (gptConfig.scoreRatingAlgFormatters[key]) {
return gptConfig.scoreRatingAlgFormatters[key]!(value);
}
return value.toFixed(2);
}
export function FormatGPTProfileRating(
game: Game,
playtype: Playtype,
key: ProfileRatingLookup[GPTString],
key: AnyProfileRatingAlg,
value: number | null
) {
if (value === null) {
@@ -87,8 +64,8 @@ export function FormatGPTProfileRating(
const gptConfig = GetGamePTConfig(game, playtype);
if (gptConfig.profileRatingAlgFormatters[key]) {
return gptConfig.profileRatingAlgFormatters[key]!(value);
if (gptConfig.profileRatingAlgs[key].formatter) {
return gptConfig.profileRatingAlgs[key].formatter!(value);
}
return value.toFixed(2);
@@ -97,7 +74,7 @@ export function FormatGPTProfileRating(
export function FormatGPTSessionRating(
game: Game,
playtype: Playtype,
key: SessionCalculatedDataLookup[GPTString],
key: AnySessionRatingAlg,
value: number | null | undefined
) {
if (value === null || value === undefined) {
@@ -106,8 +83,8 @@ export function FormatGPTSessionRating(
const gptConfig = GetGamePTConfig(game, playtype);
if (gptConfig.sessionRatingAlgFormatters[key]) {
return gptConfig.sessionRatingAlgFormatters[key]!(value);
if (gptConfig.sessionRatingAlgs[key].formatter) {
return gptConfig.sessionRatingAlgs[key].formatter!(value);
}
return value.toFixed(2);
@@ -191,27 +168,14 @@ export function SelectRightChart(
chartID: string,
charts: ChartDocument[]
) {
if (gptConfig.difficulties.includes(chartID as any)) {
for (const chart of charts) {
if (chart.difficulty === chartID && chart.isPrimary) {
return chart;
}
// try matching difficulty names, if it's not any of those, it's probably a chartID.
for (const chart of charts) {
if (chart.difficulty === chartID && chart.isPrimary) {
return chart;
}
} else if (gptConfig.gptString === "itg:Stamina") {
for (const chart of charts) {
if (
(chart as ChartDocument<"itg:Stamina">).data.hashGSV3 === chartID &&
chart.isPrimary
) {
return chart;
}
}
} else {
// else, its a chart ID.
for (const chart of charts) {
if (chartID === chart.chartID) {
return chart;
}
if (chartID === chart.chartID) {
return chart;
}
}
@@ -287,7 +251,7 @@ export function FormatScoreRating(
return "No Data.";
}
const formatter = GetGamePTConfig(game, playtype).scoreRatingAlgFormatters[rating];
const formatter = GetGamePTConfig(game, playtype).scoreRatingAlgs[rating].formatter;
if (!formatter) {
return value.toFixed(2);
@@ -299,14 +263,14 @@ export function FormatScoreRating(
export function FormatSessionRating(
game: Game,
playtype: Playtype,
rating: keyof SessionDocument["calculatedData"],
rating: AnySessionRatingAlg,
value: number | null | undefined
) {
if (value === null || value === undefined) {
return "No Data.";
}
const formatter = GetGamePTConfig(game, playtype).sessionRatingAlgFormatters[rating];
const formatter = GetGamePTConfig(game, playtype).sessionRatingAlgs[rating].formatter;
if (!formatter) {
return value.toFixed(2);
@@ -315,31 +279,6 @@ export function FormatSessionRating(
return formatter(value);
}
export function GetGradeFromPercent<GPT extends GPTString = GPTString>(
game: Game,
playtype: Playtype,
percent: number
): Grades[GPT] {
const gptConfig = GetGamePTConfig(game, playtype);
const boundaries = gptConfig.gradeBoundaries;
const grades = gptConfig.grades;
if (!boundaries) {
throw new Error(
`Invalid call to GetGradeFromPercent! GPT ${game}:${playtype} does not use grade boundaries.`
);
}
// (hey, this for loop is backwards!)
for (let i = boundaries.length; i >= 0; i--) {
if (percent >= boundaries[i]) {
return grades[i] as Grades[GPT];
}
}
throw new Error(`Could not resolve grade for percent ${percent} on game ${game}`);
}
export function ConditionalLeadingSpace(maybeStr: string | null) {
if (maybeStr === null) {
return "";
+17 -31
View File
@@ -1,46 +1,32 @@
import { Game, GetGamePTConfig, Playtypes } from "tachi-common";
import { Game, GetGamePTConfig, GetScoreMetricConf, Playtypes } from "tachi-common";
export function HumanFriendlyStrToGradeIndex(game: Game, playtype: Playtypes[Game]) {
export function HumanFriendlyStrToEnumIndex(
game: Game,
playtype: Playtypes[Game],
enumMetric: string
) {
const gptConfig = GetGamePTConfig(game, playtype);
const lowerGrades = gptConfig.grades.map((e) => e.toLowerCase());
const conf = GetScoreMetricConf(gptConfig, enumMetric);
if (conf.type !== "ENUM") {
return () => 0; // wut
}
const lowerValues = conf.values.map((e) => e.toLowerCase());
return (str: string) => {
const lowerStr = str.toLowerCase();
let partialMatch: number | null = null;
for (let i = 0; i < gptConfig.grades.length; i++) {
const grade = lowerGrades[i];
for (let i = 0; i < conf.values.length; i++) {
const value = lowerValues[i];
if (grade === lowerStr) {
if (value === lowerStr) {
return i;
}
if (grade.startsWith(lowerStr) && partialMatch === null) {
partialMatch = i;
}
}
return partialMatch;
};
}
export function HumanFriendlyStrToLampIndex(game: Game, playtype: Playtypes[Game]) {
const gptConfig = GetGamePTConfig(game, playtype);
const lowerLamps = gptConfig.lamps.map((e) => e.toLowerCase());
return (str: string) => {
const lowerStr = str.toLowerCase();
let partialMatch: number | null = null;
for (let i = 0; i < gptConfig.lamps.length; i++) {
const lamp = lowerLamps[i];
if (lamp === lowerStr) {
return i;
}
if (lamp.startsWith(lowerStr) && partialMatch === null) {
if (value.startsWith(lowerStr) && partialMatch === null) {
partialMatch = i;
}
}
+37 -39
View File
@@ -1,4 +1,4 @@
import { HumanFriendlyStrToGradeIndex, HumanFriendlyStrToLampIndex } from "util/str-to-num";
import { HumanFriendlyStrToEnumIndex } from "util/str-to-num";
import { ValueGetterOrHybrid } from "util/ztable/search";
import {
BMS_TABLES,
@@ -7,7 +7,9 @@ import {
GamePTConfig,
GetGamePTConfig,
GPTString,
PBScoreDocument,
Playtype,
ScoreDocument,
} from "tachi-common";
import { ComparePBsDataset, FolderDataset, PBDataset, ScoreDataset } from "types/tables";
@@ -32,17 +34,8 @@ export function CreateDefaultScoreSearchParams<GPT extends GPTString = GPTString
title: (x) => x.__related.song.title,
difficulty: (x) => x.__related.chart.difficulty,
level: (x) => x.__related.chart.levelNum,
score: (x) => x.scoreData.score,
percent: (x) => x.scoreData.percent,
highlight: (x) => !!x.highlight,
lamp: {
valueGetter: (x) => [x.scoreData.lamp, x.scoreData.lamp.index],
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
},
grade: {
valueGetter: (x) => [x.scoreData.grade.string, x.scoreData.grade.string],
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
},
...GetMetricSearchParams(game, playtype),
...CreateCalcDataSearchFns(gptConfig),
};
@@ -53,6 +46,33 @@ export function CreateDefaultScoreSearchParams<GPT extends GPTString = GPTString
return searchFunctions;
}
export function GetMetricSearchParams(game: Game, playtype: Playtype) {
const searchFns: Record<string, ValueGetterOrHybrid<PBScoreDocument | ScoreDocument>> = {};
const gptConfig = GetGamePTConfig(game, playtype);
for (const [metric, conf] of Object.entries({
...gptConfig.providedMetrics,
...gptConfig.derivedMetrics,
})) {
switch (conf.type) {
case "ENUM":
searchFns[metric] = {
// @ts-expect-error lol this is fine pls
valueGetter: (x) => x.scoreData[metric],
strToNum: HumanFriendlyStrToEnumIndex(game, playtype, metric),
};
break;
case "INTEGER":
case "DECIMAL":
// @ts-expect-error lol this is fine pls
searchFns[metric] = (x) => x.scoreData[metric];
}
}
return searchFns;
}
export function CreateDefaultPBSearchParams<GPT extends GPTString = GPTString>(
game: Game,
playtype: Playtype
@@ -64,20 +84,11 @@ export function CreateDefaultPBSearchParams<GPT extends GPTString = GPTString>(
title: (x) => x.__related.song.title,
difficulty: (x) => x.__related.chart.difficulty,
level: (x) => x.__related.chart.levelNum,
score: (x) => x.scoreData.score,
percent: (x) => x.scoreData.percent,
ranking: (x) => x.rankingData.rank,
rivalRanking: (x) => x.rankingData.rivalRank,
highlight: (x) => !!x.highlight,
username: (x) => x.__related.user?.username ?? null,
lamp: {
valueGetter: (x) => [x.scoreData.lamp, x.scoreData.lamp.index],
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
},
grade: {
valueGetter: (x) => [x.scoreData.grade.string, x.scoreData.grade.string],
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
},
...GetMetricSearchParams(game, playtype),
...CreateCalcDataSearchFns(gptConfig),
};
@@ -117,26 +128,11 @@ export function CreateDefaultFolderSearchParams<GPT extends GPTString = GPTStrin
title: (x) => x.__related.song.title,
difficulty: (x) => x.difficulty,
level: (x) => x.levelNum,
score: (x) => x.__related.pb?.scoreData.score ?? null,
percent: (x) => x.__related.pb?.scoreData.percent ?? null,
ranking: (x) => x.__related.pb?.rankingData.rank ?? null,
rivalRanking: (x) => x.__related.pb?.rankingData.rivalRank ?? null,
highlight: (x) => !!x.__related.pb?.highlight,
played: (x) => !!x.__related.pb,
lamp: {
valueGetter: (x) =>
x.__related.pb
? [x.__related.pb.scoreData.lamp, x.__related.pb.scoreData.lamp.index]
: null,
strToNum: HumanFriendlyStrToLampIndex(game, playtype),
},
grade: {
valueGetter: (x) =>
x.__related.pb
? [x.__related.pb.scoreData.grade.string, x.__related.pb.scoreData.grade.string]
: null,
strToNum: HumanFriendlyStrToGradeIndex(game, playtype),
},
...GetMetricSearchParams(game, playtype),
...CreateFolderCalcDataSearchFns(gptConfig),
};
@@ -149,8 +145,9 @@ export function CreateDefaultFolderSearchParams<GPT extends GPTString = GPTStrin
function CreateFolderCalcDataSearchFns(gptConfig: GamePTConfig) {
return Object.fromEntries(
gptConfig.scoreRatingAlgs.map((e) => [
Object.keys(gptConfig.scoreRatingAlgs).map((e) => [
e.toLowerCase(),
// @ts-expect-error this is fine please leave me alone
(x: FolderDataset[0]) => x.__related.pb?.calculatedData[e] ?? null,
])
);
@@ -158,7 +155,8 @@ function CreateFolderCalcDataSearchFns(gptConfig: GamePTConfig) {
function CreateCalcDataSearchFns(gptConfig: GamePTConfig) {
return Object.fromEntries(
gptConfig.scoreRatingAlgs.map(
Object.keys(gptConfig.scoreRatingAlgs).map(
// @ts-expect-error this is fine please leave me alone
(e) => [e.toLowerCase(), (x: PBDataset[0]) => x.calculatedData[e]] ?? null
)
);
-73
View File
@@ -1,73 +0,0 @@
import {
Game,
GPTTierlists,
GPTString,
IIDX_LAMPS,
ScoreDocument,
SDVX_LAMPS,
Playtype,
} from "tachi-common";
type ScaleAchievedFns = {
[I in GPTString]: {
[K in GPTTierlists[GPT]]: ((k: ScoreDocument<GPT>) => boolean) | null;
};
};
const ScaleNameAchievedFns: ScaleAchievedFns = {
"bms:14K": {
"sgl-EC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EASY_CLEAR,
"sgl-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
},
"bms:7K": {
"sgl-EC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EASY_CLEAR,
"sgl-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
},
"pms:Controller": {
"sgl-EC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EASY_CLEAR,
"sgl-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
},
"pms:Keyboard": {
"sgl-EC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EASY_CLEAR,
"sgl-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
},
"chunithm:Single": {},
"gitadora:Dora": {},
"gitadora:Gita": {},
"iidx:DP": {
"dp-tier": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.CLEAR,
},
"iidx:SP": {
"kt-NC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.CLEAR,
"kt-HC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.HARD_CLEAR,
"kt-EXHC": (k) => k.scoreData.lamp.index >= IIDX_LAMPS.EX_HARD_CLEAR,
},
"maimaidx:Single": {},
"museca:Single": {
"tachi-score": null,
},
"sdvx:Single": {
clear: (k) => k.scoreData.lamp.index >= SDVX_LAMPS.CLEAR,
},
"usc:Controller": {},
"usc:Keyboard": {},
"wacca:Single": {},
"popn:9B": {},
"jubeat:Single": {},
"itg:Stamina": {},
};
export function GetScaleAchievedFn(
game: Game,
playtype: Playtype,
tierlist: GPTTierlists[GPTString]
) {
const scl = ScaleNameAchievedFns[`${game}:${playtype}` as GPTString];
if (!scl) {
throw new Error(`Invalid game + pt combination ${game}:${playtype}. Can't find scale.`);
}
// @ts-expect-error hack haha
return scl[tierlist];
}