Fix team MMR calculation

This commit is contained in:
Kalle
2022-02-27 10:49:49 +02:00
parent b9faa00c57
commit 244f6aab8a
8 changed files with 71 additions and 34 deletions

View File

@@ -14,5 +14,6 @@ module.exports = {
rules: {
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
"no-constant-condition": ["error", { checkLoops: false }],
"no-console": ["error", { allow: ["warn", "error"] }],
},
};

View File

@@ -1,9 +1,15 @@
import { suite } from "uvu";
import { adjustSkills, muSigmaToSP, resolveOwnMMR } from "./utils";
import {
adjustSkills,
muSigmaToSP,
resolveOwnMMR,
teamSkillToExactMMR,
} from "./utils";
import * as assert from "uvu/assert";
const AdjustSkills = suite("adjustSkills()");
const ResolveOwnMMR = suite("resolveOwnMMR()");
const TeamSkillToExactMMR = suite("TeamSkillToExactMMR()");
const MU_AT_START = 20;
const SIGMA_AT_START = 4;
@@ -93,5 +99,34 @@ ResolveOwnMMR("Hides topX if not good", () => {
assert.not.ok(own?.topX);
});
TeamSkillToExactMMR("Sums up MMR's", () => {
const MU = 20;
const SIGMA = 7;
const skills = new Array(4).fill(null).map((_) => ({ mu: MU, sigma: SIGMA }));
const teamMMR = teamSkillToExactMMR(
skills.map((s) => ({ user: { skill: [{ mu: s.mu, sigma: s.sigma }] } }))
);
assert.equal(teamMMR, muSigmaToSP({ mu: MU, sigma: SIGMA }) * 4);
});
TeamSkillToExactMMR("Pads team MMR", () => {
const MU = 20;
const SIGMA = 7;
const skills = new Array(3).fill(null).map((_) => ({ mu: MU, sigma: SIGMA }));
const teamMMR = teamSkillToExactMMR(
skills.map((s) => ({ user: { skill: [{ mu: s.mu, sigma: s.sigma }] } }))
);
assert.equal(teamMMR, muSigmaToSP({ mu: MU, sigma: SIGMA }) * 3 + 1000);
const teamMMRNoSkills = teamSkillToExactMMR([]);
assert.equal(teamMMRNoSkills, 4 * 1000);
});
AdjustSkills.run();
ResolveOwnMMR.run();
TeamSkillToExactMMR.run();

View File

@@ -1,5 +1,6 @@
import clone from "just-clone";
import { expose, rate, Rating } from "ts-trueskill";
import { MMR_TOPX_VISIBILITY_CUTOFF } from "~/constants";
import { LFG_GROUP_FULL_SIZE, MMR_TOPX_VISIBILITY_CUTOFF } from "~/constants";
import { PlayFrontPageLoader } from "~/routes/play/index";
/** Get first skill object of the array (should be ordered so that most recent skill is first) and convert it into MMR. */
@@ -31,7 +32,28 @@ interface TeamSkill {
export function teamSkillToExactMMR(teamSkills: TeamSkill[]) {
let sum = 0;
for (const { user } of teamSkills) {
const teamSkillsClone = clone(teamSkills);
while (teamSkillsClone.length < LFG_GROUP_FULL_SIZE) {
teamSkillsClone.push({ user: { skill: [] } });
}
const defaultRating = new Rating();
const skillsWithDefaults = teamSkillsClone.reduce((acc: TeamSkill[], cur) => {
if (cur.user.skill.length === 0) {
return [
{
user: {
skill: [{ mu: defaultRating.mu, sigma: defaultRating.sigma }],
},
},
...acc,
];
}
return [cur, ...acc];
}, []);
for (const { user } of skillsWithDefaults) {
const MMR = skillArrayToMMR(user.skill);
if (!MMR) continue;
@@ -53,19 +75,6 @@ function toTwoDecimals(value: number) {
return Number(value.toFixed(2));
}
export function teamHasSkill(teamSkills: TeamSkill[]) {
let hasSkill = false;
for (const { user } of teamSkills) {
if (user.skill.length > 0) {
hasSkill = true;
break;
}
}
return hasSkill;
}
interface AdjustSkill {
mu: number;
sigma: number;

View File

@@ -4,11 +4,7 @@ import * as LFGGroup from "~/models/LFGGroup.server";
import { PlayFrontPageLoader } from "~/routes/play/index";
import { LookingLoaderData } from "~/routes/play/looking";
import { Unpacked } from "~/utils";
import {
skillArrayToMMR,
teamHasSkill,
teamSkillToApproximateMMR,
} from "../mmr/utils";
import { skillArrayToMMR, teamSkillToApproximateMMR } from "../mmr/utils";
import { canUniteWithGroup } from "./validators";
export interface UniteGroupInfoArg {
@@ -167,7 +163,7 @@ export function otherGroupsForResponse({
}),
ranked: ranked(),
teamMMR:
lookingForMatch && group.ranked && teamHasSkill(group.members)
lookingForMatch && group.ranked
? {
exact: false,
value: teamSkillToApproximateMMR(group.members),

View File

@@ -27,9 +27,9 @@ export const useEvents = (
`/events?${new URLSearchParams(target).toString()}`
);
source.addEventListener("open", () => {
console.log("SSE opened!");
});
// source.addEventListener("open", () => {
// console.log("SSE opened!");
// });
source.addEventListener("message", (e) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument

View File

@@ -16,11 +16,7 @@ import { GroupCard } from "~/components/play/GroupCard";
import { LookingInfoText } from "~/components/play/LookingInfoText";
import { Tab } from "~/components/Tab";
import { LFG_GROUP_FULL_SIZE } from "~/constants";
import {
skillArrayToMMR,
teamHasSkill,
teamSkillToExactMMR,
} from "~/core/mmr/utils";
import { skillArrayToMMR, teamSkillToExactMMR } from "~/core/mmr/utils";
import { addInfoFromOldSendouInk } from "~/core/play/playerInfos/playerInfos.server";
import {
groupExpirationStatus,
@@ -279,9 +275,7 @@ export const loader: LoaderFunction = async ({ context }) => {
}),
ranked: ownGroup.ranked ?? undefined,
teamMMR:
lookingForMatch &&
isRanked &&
teamHasSkill(ownGroupWithMembers.members)
lookingForMatch && isRanked
? {
exact: true,
value: teamSkillToExactMMR(ownGroupWithMembers.members),

View File

@@ -23,7 +23,8 @@
"typecheck": "tsc --noEmit",
"cy:open": "npx cypress open",
"cy:run": "npx cypress run",
"test:unit": "uvu -r tsm -r tsconfig-paths/register -i cypress"
"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"
},
"dependencies": {
"@dnd-kit/core": "^5.0.1",

View File

@@ -10,6 +10,7 @@ const variation = SeedVariationsSchema.optional().parse(maybeVariation);
seed(variation)
.then(() => {
// eslint-disable-next-line no-console
console.log(
`🌱 All done with seeding${variation ? ` (variation: ${variation})` : ""}`
);