From 68184ee9fe87e6b64b687006c4ef26d2fe68bd68 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sat, 26 Mar 2022 10:34:16 +0200 Subject: [PATCH] Recalculate skills script --- package.json | 2 +- scripts/recalculateSkills.ts | 82 ++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 scripts/recalculateSkills.ts diff --git a/package.json b/package.json index 7d53b21aa..dd83ec0cd 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "cy:run": "npx cypress run", "test:unit": "uvu -r tsm -r tsconfig-paths/register -i cypress", "tests": "npm run lint:styles && npm run lint:ts && npm run prettier:check && npm run typecheck && npm run test:unit", - "matches": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/matchesWithoutDetails.ts" + "recalculate-skills": "node --experimental-specifier-resolution=node --loader ts-node/esm -r tsconfig-paths/register scripts/recalculateSkills.ts" }, "dependencies": { "@dnd-kit/core": "^5.0.3", diff --git a/scripts/recalculateSkills.ts b/scripts/recalculateSkills.ts new file mode 100644 index 000000000..d246f30d3 --- /dev/null +++ b/scripts/recalculateSkills.ts @@ -0,0 +1,82 @@ +import { Prisma, PrismaClient } from "@prisma/client"; +import { rating } from "openskill"; +import { adjustSkills } from "~/core/mmr/utils"; +import { groupsToWinningAndLosingPlayerIds } from "~/core/play/utils"; + +const prisma = new PrismaClient(); + +type SkillWithDetails = { + mu: number; + sigma: number; + userId: string; + matchId: string; +}; +type UserId = string; +type CurrentSkills = Record>; + +async function main() { + const matches = (await allMatches()).filter((m) => + m.stages.some((s) => s.winnerGroupId) + ); + + const newSkills: SkillWithDetails[] = []; + const currentSkills: CurrentSkills = {}; + + for (const match of matches) { + const adjustedSkills = adjustSkills({ + skills: skillsPerUser({ + currentSkills, + userIds: match.groups.flatMap((g) => + g.members.flatMap((m) => m.memberId) + ), + }), + playerIds: groupsToWinningAndLosingPlayerIds({ + groups: match.groups.map((g) => ({ + id: g.id, + members: g.members.map((m) => ({ user: { id: m.memberId } })), + })), + winnerGroupIds: match.stages.flatMap((s) => + s.winnerGroupId ? [s.winnerGroupId] : [] + ), + }), + }); + + newSkills.push(...adjustedSkills.map((s) => ({ ...s, matchId: match.id }))); + + for (const skill of adjustedSkills) { + const { userId, ...rest } = skill; + currentSkills[userId] = rest; + } + } + + await prisma.$transaction([ + prisma.skill.deleteMany({}), + prisma.skill.createMany({ data: newSkills }), + ]); +} + +export type AllMatches = Prisma.PromiseReturnType; +function allMatches() { + return prisma.lfgGroupMatch.findMany({ + include: { stages: true, groups: { include: { members: true } } }, + orderBy: { createdAt: "asc" }, + }); +} + +function skillsPerUser({ + currentSkills, + userIds, +}: { + currentSkills: CurrentSkills; + userIds: string[]; +}): Omit[] { + return userIds.map((id) => { + const skill = currentSkills[id] ? currentSkills[id] : rating(); + return { userId: id, ...skill }; + }); +} + +main() + // eslint-disable-next-line no-console + .then(() => console.log("done")) + .catch((e) => console.error(e));