Show groups closer to own skill tier first

This commit is contained in:
Kalle
2023-10-11 19:16:01 +03:00
parent b94cb78d64
commit 69db988ef5
6 changed files with 110 additions and 26 deletions

View File

@@ -33,7 +33,7 @@ export function GroupCard({
hideWeapons = false,
hideNote: _hidenote = false,
}: {
group: LookingGroup;
group: Omit<LookingGroup, "createdAt">;
action?: "LIKE" | "UNLIKE" | "GROUP_UP" | "MATCH_UP";
ownRole?: GroupMemberType["role"];
ownGroup?: boolean;

View File

@@ -13,6 +13,7 @@ import type {
TieredSkill,
} from "~/features/mmr/tiered.server";
import type { RecentMatchPlayer } from "../queries/findRecentMatchPlayersByUserId.server";
import { TIERS } from "~/features/mmr/mmr-constants";
export function divideGroups({
groups,
@@ -165,6 +166,66 @@ export function censorGroups({
};
}
export function sortGroupsBySkill({
groups,
userSkills,
intervals,
}: {
groups: DividedGroups;
userSkills: Record<string, TieredSkill>;
intervals: SkillTierInterval[];
}): DividedGroups {
const ownGroupTier =
groups.own.tier?.name ??
resolveGroupSkill({
group: groups.own as LookingGroupWithInviteCode,
userSkills,
intervals,
})?.name;
const ownGroupTierIndex = TIERS.findIndex((t) => t.name === ownGroupTier);
const tierDiff = (otherGroupTierName?: string) => {
if (!otherGroupTierName) return 10;
const otherGroupTierIndex = TIERS.findIndex(
(t) => t.name === otherGroupTierName,
);
return Math.abs(ownGroupTierIndex - otherGroupTierIndex);
};
return {
...groups,
neutral: groups.neutral.sort((a, b) => {
const aTier =
a.tier?.name ??
resolveGroupSkill({
group: a as LookingGroupWithInviteCode,
userSkills,
intervals,
})?.name;
const bTier =
b.tier?.name ??
resolveGroupSkill({
group: b as LookingGroupWithInviteCode,
userSkills,
intervals,
})?.name;
const aTierDiff = tierDiff(aTier);
const bTierDiff = tierDiff(bTier);
// if same tier difference, show newer groups first
if (aTierDiff === bTierDiff) {
return b.createdAt - a.createdAt;
}
// show groups with smaller tier difference first
return aTierDiff - bTierDiff;
}),
};
}
export function addSkillsToGroups({
groups,
userSkills,
@@ -174,33 +235,16 @@ export function addSkillsToGroups({
userSkills: Record<string, TieredSkill>;
intervals: SkillTierInterval[];
}): DividedGroupsUncensored {
const resolveGroupSkill = (
group: LookingGroupWithInviteCode,
): TieredSkill["tier"] | undefined => {
if (group.members.length < FULL_GROUP_SIZE) return;
const skills = group.members
.map((m) => userSkills[String(m.id)])
.filter(Boolean);
const averageOrdinal =
skills.reduce((acc, s) => acc + s.ordinal, 0) / skills.length;
const tier = intervals.find(
(i) => i.neededOrdinal && averageOrdinal > i.neededOrdinal,
) ?? { isPlus: false, name: "IRON" };
// For Leviathan we don't specify if it's plus or not
return tier.name === "LEVIATHAN"
? { name: "LEVIATHAN", isPlus: false }
: { name: tier.name, isPlus: tier.isPlus };
};
const addSkill = (group: LookingGroupWithInviteCode) => ({
...group,
members: group.members?.map((m) => ({
...m,
skill: userSkills[String(m.id)],
})),
tier: resolveGroupSkill(group),
tier:
group.members.length === FULL_GROUP_SIZE
? resolveGroupSkill({ group, userSkills, intervals })
: undefined,
});
return {
@@ -214,6 +258,31 @@ export function membersNeededForFull(currentSize: number) {
return FULL_GROUP_SIZE - currentSize;
}
function resolveGroupSkill({
group,
userSkills,
intervals,
}: {
group: LookingGroupWithInviteCode;
userSkills: Record<string, TieredSkill>;
intervals: SkillTierInterval[];
}): TieredSkill["tier"] | undefined {
const skills = group.members
.map((m) => userSkills[String(m.id)])
.filter(Boolean);
const averageOrdinal =
skills.reduce((acc, s) => acc + s.ordinal, 0) / skills.length;
const tier = intervals.find(
(i) => i.neededOrdinal && averageOrdinal > i.neededOrdinal,
) ?? { isPlus: false, name: "IRON" };
// For Leviathan we don't specify if it's plus or not
return tier.name === "LEVIATHAN"
? { name: "LEVIATHAN", isPlus: false }
: { name: tier.name, isPlus: tier.isPlus };
}
export function groupExpiryStatus(
group: Pick<Group, "latestActionAt">,
): null | "EXPIRING_SOON" | "EXPIRED" {

View File

@@ -12,6 +12,7 @@ import type { GroupForMatch } from "./queries/groupForMatch.server";
export type LookingGroup = {
id: number;
mapListPreference?: Group["mapListPreference"];
createdAt: Group["createdAt"];
tier?: TieredSkill["tier"];
isReplay?: boolean;
isLiked?: boolean;

View File

@@ -34,6 +34,7 @@ const stm = sql.prepare(/* sql */ `
"q1"."id",
"q1"."mapListPreference",
"q1"."inviteCode",
"q1"."createdAt",
json_group_array(
json_object(
'id', "q1"."userId",
@@ -57,6 +58,7 @@ export function findPreparingGroup(
return {
id: row.id,
createdAt: row.createdAt,
mapListPreference: row.mapListPreference,
inviteCode: row.inviteCode,
members: parseDBJsonArray(row.members).map((member: any) => {

View File

@@ -46,6 +46,7 @@ const stm = sql.prepare(/* sql */ `
"q1"."id",
"q1"."mapListPreference",
"q1"."inviteCode",
"q1"."createdAt",
json_group_array(
json_object(
'id', "q1"."userId",
@@ -64,7 +65,6 @@ const stm = sql.prepare(/* sql */ `
) as "members"
from "q1"
group by "q1"."id"
order by "q1"."createdAt" desc
`);
export function findLookingGroups({
@@ -83,6 +83,7 @@ export function findLookingGroups({
id: row.id,
mapListPreference: row.mapListPreference,
inviteCode: row.inviteCode,
createdAt: row.createdAt,
members: parseDBJsonArray(row.members).map((member: any) => {
const weapons = parseDBArray(member.weapons);

View File

@@ -37,6 +37,7 @@ import {
filterOutGroupsWithIncompatibleMapListPreference,
groupExpiryStatus,
membersNeededForFull,
sortGroupsBySkill,
} from "../core/groups.server";
import { createMatchMemento, matchMapList } from "../core/match.server";
import { FULL_GROUP_SIZE } from "../q-constants";
@@ -324,9 +325,13 @@ export const loader = async ({ request }: LoaderArgs) => {
const season = currentOrPreviousSeason(new Date());
const { intervals, userSkills: calculatedUserSkills } = await userSkills(
season!.nth,
);
const groupsWithSkills = addSkillsToGroups({
groups: dividedGroups,
...(await userSkills(season!.nth)),
intervals,
userSkills: calculatedUserSkills,
});
const compatibleGroups = groupIsFull
@@ -347,12 +352,18 @@ export const loader = async ({ request }: LoaderArgs) => {
showInviteCode: hasGroupManagerPerms(currentGroup.role) && !groupIsFull,
});
return {
const sortedGroups = sortGroupsBySkill({
groups: censoredGroups,
intervals,
userSkills: calculatedUserSkills,
});
return {
groups: sortedGroups,
role: currentGroup.role,
chatCode:
// don't chat with yourself...
censoredGroups.own.members!.length > 1 ? currentGroup.chatCode : null,
sortedGroups.own.members!.length > 1 ? currentGroup.chatCode : null,
lastUpdated: new Date().getTime(),
expiryStatus: groupExpiryStatus(currentGroup),
trustedPlayers: hasGroupManagerPerms(currentGroup.role)