Files
2026-09-06 16:57:28 +03:00

578 lines
10 KiB
TypeScript

import * as R from "remeda";
import type { BracketData } from "~/features/tournament-bracket/core/engine/types";
import type * as Progression from "../Progression";
import { Tournament } from "../Tournament";
import type { TournamentData } from "../Tournament.server";
export const tournamentCtxTeam = (
teamId: number,
partial?: Partial<TournamentData["ctx"]["teams"][0]>,
): TournamentData["ctx"]["teams"][0] => {
return {
checkIns: [{ checkedInAt: 1705858841, bracketIdx: null, isCheckOut: 0 }],
createdAt: 0,
id: teamId,
avgSeedingSkillOrdinal: null,
startingBracketIdx: null,
abDivision: null,
hasMapPool: false,
memberUserIds: [],
ownerUserId: null,
activeRosterUserIds: [],
logoUrl: null,
name: `Team ${teamId}`,
prefersNotToHost: 0,
droppedOut: 0,
seed: teamId + 1,
...partial,
};
};
const nTeams = (n: number, startingId: number) => {
const teams: TournamentData["ctx"]["teams"] = [];
for (let i = 0; i < n; i++) {
teams.push(tournamentCtxTeam(i + 1, tournamentCtxTeam(i + startingId)));
}
return teams;
};
export const testTournament = ({
data = {
match: [],
group: [],
round: [],
stage: [],
},
ctx,
}: {
data?: BracketData;
ctx?: Partial<TournamentData["ctx"]>;
}) => {
const participant = R.pipe(
data.match,
R.flatMap((m) => [m.opponent1?.id, m.opponent2?.id]),
R.filter(R.isTruthy),
R.unique<number[]>,
);
const tournamentCtx: TournamentData["ctx"] = {
eventId: 1,
id: 1,
tags: null,
organization: null,
tier: null,
tentativeTier: null,
hasRules: false,
logoUrl: "/test.avif",
discordUrl: null,
startsAt: 1705858842,
isFinalized: 0,
name: "test",
castTwitchAccounts: [],
bracketProgressionOverrides: [],
staff: [],
tieBreakerMapPool: [],
toSetMapPool: [],
latestTeamIdByDuplicatedUserId: {},
mapPickingStyle: "AUTO_SZ",
settings: {
bracketProgression: [
{
name: "Main Bracket",
type: "double_elimination",
requiresCheckIn: false,
settings: {},
},
],
},
castedMatchesInfo: null,
permissions: {
ADMIN: [1],
ORGANIZE: [1],
MANAGE_MATCHES: [1],
EDIT_EVENT_INFO: [1],
EDIT_IN_GAME_NAMES: [],
},
teams: nTeams(participant.length, Math.min(...participant)),
author: {
customUrl: null,
customAvatarUrl: null,
discordAvatar: null,
discordId: "123",
username: "test",
pronouns: null,
id: 1,
},
...ctx,
};
return new Tournament({
// engine created data has no stage names, the database assigns them from the bracket progression
data: {
...data,
stage: data.stage.map((stage, stageIdx) => ({
...stage,
name:
stage.name ??
tournamentCtx.settings.bracketProgression[stageIdx]?.name,
})),
},
ctx: tournamentCtx,
participatedUsers: [],
});
};
/** Combines brackets into one tournament's data, offsetting local ids the way the database does on adding a stage. */
export const mergeStages = (...brackets: BracketData[]): BracketData => {
const merged: BracketData = { stage: [], group: [], round: [], match: [] };
for (const bracket of brackets) {
const offsets = {
stage: merged.stage.length,
group: merged.group.length,
round: merged.round.length,
match: merged.match.length,
};
merged.stage.push(
...bracket.stage.map((stage) => ({
...stage,
id: stage.id + offsets.stage,
number: offsets.stage + 1,
})),
);
merged.group.push(
...bracket.group.map((group) => ({
...group,
id: group.id + offsets.group,
stageId: group.stageId + offsets.stage,
})),
);
merged.round.push(
...bracket.round.map((round) => ({
...round,
id: round.id + offsets.round,
stageId: round.stageId + offsets.stage,
groupId: round.groupId + offsets.group,
})),
);
merged.match.push(
...bracket.match.map((match) => ({
...match,
id: match.id + offsets.match,
stageId: match.stageId + offsets.stage,
groupId: match.groupId + offsets.group,
roundId: match.roundId + offsets.round,
})),
);
}
return merged;
};
const DEFAULT_PROGRESSION_ARGS = {
requiresCheckIn: false,
settings: {},
name: "Main Bracket",
};
export const progressions = {
singleElimination: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
},
],
roundRobinToSingleElimination: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "B1",
sources: [
{
bracketIdx: 0,
placements: [1, 2],
},
],
},
],
lowInk: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "swiss",
},
{
...DEFAULT_PROGRESSION_ARGS,
name: "B1",
type: "double_elimination",
sources: [
{
bracketIdx: 0,
placements: [3, 4],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
name: "B2",
type: "round_robin",
sources: [
{
bracketIdx: 0,
placements: [1, 2],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
name: "B3",
type: "double_elimination",
sources: [
{
bracketIdx: 2,
placements: [1, 2],
},
],
},
],
manyStartBrackets: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
name: "B1",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "B2",
sources: [
{
bracketIdx: 0,
placements: [1, 2],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "B3",
sources: [
{
bracketIdx: 1,
placements: [1, 2],
},
],
},
],
league: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
name: "Division 1",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Division 1 Playoffs",
sources: [
{
bracketIdx: 0,
placements: [1, 2],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
name: "Division 2",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Division 2 Playoffs",
sources: [
{
bracketIdx: 2,
placements: [1, 2],
},
],
},
],
swissOneGroup: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "swiss",
settings: {
groupCount: 1,
},
},
],
swissEarlyAdvance: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "swiss",
settings: {
advanceThreshold: 3,
},
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "B1",
sources: [
{
bracketIdx: 0,
placements: [],
},
],
},
],
doubleEliminationWithUnderground: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "double_elimination",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "double_elimination",
name: "Underground",
sources: [
{
bracketIdx: 0,
placements: [-1, -2],
},
],
},
],
singleEliminationWithUnderground: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Underground",
sources: [
{
bracketIdx: 0,
placements: [-1],
},
],
},
],
multiSourceTopCut: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Redemption",
sources: [
{
bracketIdx: 0,
placements: [3, 4],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Top Cut",
sources: [
{
bracketIdx: 1,
placements: [1, 2],
},
{
bracketIdx: 0,
placements: [1, 2],
},
],
},
],
multiSourceTopCutWithConsolation: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Redemption",
sources: [
{
bracketIdx: 0,
placements: [3, 4],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Top Cut",
sources: [
{
bracketIdx: 1,
placements: [1, 2],
},
{
bracketIdx: 0,
placements: [1, 2],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Consolation",
sources: [
{
bracketIdx: 0,
placements: [5, 6, 7, 8],
},
],
},
],
poolsToBracketsViaIntermediateBrackets: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
name: "Day 1 Pools",
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Redemption",
sources: [
{
bracketIdx: 0,
placements: [2, 3, 4],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Alpha",
sources: [
{
bracketIdx: 0,
placements: [1],
},
{
bracketIdx: 1,
placements: [1, 2, 3, 4, 5, 6, 7, 8],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Beta",
sources: [
{
bracketIdx: 1,
placements: [9, 10, 11, 12],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Gamma",
sources: [
{
bracketIdx: 0,
placements: [5, 6],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Delta",
sources: [
{
bracketIdx: 0,
placements: [7, 8],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "round_robin",
name: "Epsilon Seeding",
sources: [
{
bracketIdx: 0,
placements: [9, 10, 11],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Epsilon",
sources: [
{
bracketIdx: 6,
placements: [1, 2, 3, 4],
},
],
},
],
swissToTwoSingleEliminationsWithUnderground: [
{
...DEFAULT_PROGRESSION_ARGS,
type: "swiss",
settings: {
groupCount: 1,
},
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Alpha",
sources: [
{
bracketIdx: 0,
placements: [1, 2, 3, 4, 5, 6, 7, 8],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Beta",
sources: [
{
bracketIdx: 0,
placements: [9, 10, 11, 12, 13, 14, 15, 16],
},
],
},
{
...DEFAULT_PROGRESSION_ARGS,
type: "single_elimination",
name: "Alpha UG",
sources: [
{
bracketIdx: 1,
placements: [-1],
},
],
},
],
} satisfies Record<string, Progression.ParsedBracket[]>;