Remove unused participant code + slim down tournament response

This commit is contained in:
Kalle
2024-06-09 23:53:31 +03:00
parent 4248cca5b1
commit dcf738f6d3
29 changed files with 225 additions and 2794 deletions

View File

@@ -1,107 +1,11 @@
import type {
Match,
Seeding,
Stage,
GroupType,
} from "~/modules/brackets-model";
import type { GroupType, Match, Stage } from "~/modules/brackets-model";
import { Status } from "~/modules/brackets-model";
import type { DeepPartial, ParticipantSlot, Side } from "../types";
import type { SetNextOpponent } from "../helpers";
import { ordering } from "../ordering";
import { Create } from "../create";
import { BaseGetter } from "./getter";
import { Get } from "../get";
import * as helpers from "../helpers";
import type { DeepPartial, Side } from "../types";
import { BaseGetter } from "./getter";
export class BaseUpdater extends BaseGetter {
/**
* Updates or resets the seeding of a stage.
*
* @param stageId ID of the stage.
* @param seeding A new seeding or `null` to reset the existing seeding.
*/
protected updateSeeding(stageId: number, seeding: Seeding | null): void {
const stage = this.storage.select("stage", stageId);
if (!stage) throw Error("Stage not found.");
const create = new Create(this.storage, {
name: stage.name,
tournamentId: stage.tournament_id,
type: stage.type,
settings: stage.settings,
seeding: seeding || undefined,
});
create.setExisting(stageId, false);
const method = BaseGetter.getSeedingOrdering(stage.type, create);
const slots = create.getSlots();
const matches = this.getSeedingMatches(stage.id, stage.type);
if (!matches)
throw Error("Error getting matches associated to the seeding.");
const ordered = ordering[method](slots);
BaseUpdater.assertCanUpdateSeeding(matches, ordered);
create.run();
}
/**
* Confirms the current seeding of a stage.
*
* @param stageId ID of the stage.
*/
protected confirmCurrentSeeding(stageId: number): void {
const stage = this.storage.select("stage", stageId);
if (!stage) throw Error("Stage not found.");
const get = new Get(this.storage);
const currentSeeding = get.seeding(stageId);
const newSeeding = helpers.convertSlotsToSeeding(
currentSeeding.map(helpers.convertTBDtoBYE),
);
const create = new Create(this.storage, {
name: stage.name,
tournamentId: stage.tournament_id,
type: stage.type,
settings: stage.settings,
seeding: newSeeding,
});
create.setExisting(stageId, true);
create.run();
}
/**
* Throws an error if a match is locked and the new seeding will change this match's participants.
*
* @param matches The matches stored in the database.
* @param slots The slots to check from the new seeding.
*/
protected static assertCanUpdateSeeding(
matches: Match[],
slots: ParticipantSlot[],
): void {
let index = 0;
for (const match of matches) {
const opponent1 = slots[index++];
const opponent2 = slots[index++];
const locked = helpers.isMatchParticipantLocked(match);
if (!locked) continue;
if (
match.opponent1?.id !== opponent1?.id ||
match.opponent2?.id !== opponent2?.id
)
throw Error("A match is locked.");
}
}
/**
* Updates the matches related (previous and next) to a match.
*

View File

@@ -2,10 +2,9 @@ import type {
Group,
InputStage,
Match,
Participant,
Round,
Seeding,
SeedOrdering,
Seeding,
Stage,
} from "~/modules/brackets-model";
import { defaultMinorOrdering, ordering } from "./ordering";
@@ -569,37 +568,7 @@ export class Create {
this.stage.settings.size,
);
if (helpers.isSeedingWithIds(this.stage.seeding))
return this.getSlotsUsingIds(this.stage.seeding, positions);
return this.getSlotsUsingNames(this.stage.seeding, positions);
}
/**
* Returns the list of slots with a seeding containing names. Participants may be added to database.
*
* @param seeding The seeding (names).
* @param positions An optional list of positions (seeds) for a manual ordering.
*/
private getSlotsUsingNames(
seeding: Seeding,
positions?: number[],
): ParticipantSlot[] {
const participants = helpers.extractParticipantsFromSeeding(
this.stage.tournamentId,
seeding,
);
if (!this.registerParticipants(participants))
throw Error("Error registering the participants.");
// Get participants back with IDs.
const added = this.storage.select("participant", {
tournament_id: this.stage.tournamentId,
});
if (!added) throw Error("Error getting registered participant.");
return helpers.mapParticipantsNamesToDatabase(seeding, added, positions);
return this.getSlotsUsingIds(this.stage.seeding, positions);
}
/**
@@ -612,16 +581,21 @@ export class Create {
seeding: Seeding,
positions?: number[],
): ParticipantSlot[] {
const participants = this.storage.select("participant", {
tournament_id: this.stage.tournamentId,
});
if (!participants) throw Error("No available participants.");
if (positions && positions.length !== seeding.length) {
throw Error(
"Not enough seeds in at least one group of the manual ordering.",
);
}
return helpers.mapParticipantsIdsToDatabase(
seeding,
participants,
positions,
);
const slots = seeding.map((slot, i) => {
if (slot === null) return null; // BYE.
return { id: slot, position: i + 1 };
});
if (!positions) return slots;
return positions.map((position) => slots[position - 1]);
}
/**
@@ -851,31 +825,6 @@ export class Create {
return existing.id;
}
/**
* Inserts missing participants.
*
* @param participants The list of participants to process.
*/
private registerParticipants(participants: OmitId<Participant>[]): boolean {
const existing = this.storage.select("participant", {
tournament_id: this.stage.tournamentId,
});
// Insert all if nothing.
if (!existing || existing.length === 0)
return this.storage.insert("participant", participants);
// Insert only missing otherwise.
for (const participant of participants) {
if (existing.some((value) => value.name === participant.name)) continue;
const result = this.storage.insert("participant", participant);
if (result === -1) return false;
}
return true;
}
/**
* Creates a new stage.
*/

View File

@@ -1,12 +1,6 @@
import type {
Stage,
Group,
Round,
Match,
Participant,
} from "~/modules/brackets-model";
import type { Stage, Group, Round, Match } from "~/modules/brackets-model";
import { Status } from "~/modules/brackets-model";
import type { Database, FinalStandingsItem, ParticipantSlot } from "./types";
import type { Database, ParticipantSlot } from "./types";
import { BaseGetter } from "./base/getter";
import * as helpers from "./helpers";
@@ -19,17 +13,11 @@ export class Get extends BaseGetter {
public stageData(stageId: number): Database {
const stageData = this.getStageSpecificData(stageId);
const participants = this.storage.select("participant", {
tournament_id: stageData.stage.tournament_id,
});
if (!participants) throw Error("Error getting participants.");
return {
stage: [stageData.stage],
group: stageData.groups,
round: stageData.rounds,
match: stageData.matches,
participant: participants,
};
}
@@ -48,11 +36,6 @@ export class Get extends BaseGetter {
this.getStageSpecificData(stage.id),
);
const participants = this.storage.select("participant", {
tournament_id: tournamentId,
});
if (!participants) throw Error("Error getting participants.");
return {
stage: stages,
group: stagesData.reduce(
@@ -67,7 +50,6 @@ export class Get extends BaseGetter {
(acc, data) => [...acc, ...data.matches],
[] as Match[],
),
participant: participants,
};
}
@@ -214,30 +196,6 @@ export class Get extends BaseGetter {
return this.eliminationSeeding(stage).map(pickRelevantProps);
}
/**
* Returns the final standings of a stage.
*
* @param stageId ID of the stage.
*/
public finalStandings(stageId: number): FinalStandingsItem[] {
const stage = this.storage.select("stage", stageId);
if (!stage) throw Error("Stage not found.");
switch (stage.type) {
case "round_robin":
throw Error("A round-robin stage does not have standings.");
case "single_elimination":
return this.singleEliminationStandings(stageId);
case "double_elimination":
if (stage.settings.size === 2) {
return this.singleEliminationStandings(stageId);
}
return this.doubleEliminationStandings(stageId);
default:
throw Error("Unknown stage type.");
}
}
/**
* Returns the seeding of a round-robin stage.
*
@@ -282,134 +240,6 @@ export class Get extends BaseGetter {
return helpers.convertMatchesToSeeding(matches);
}
/**
* Returns the final standings of a single elimination stage.
*
* @param stageId ID of the stage.
*/
private singleEliminationStandings(stageId: number): FinalStandingsItem[] {
const grouped: Participant[][] = [];
const {
stage: stages,
group: groups,
match: matches,
participant: participants,
} = this.stageData(stageId);
const [stage] = stages;
const [singleBracket, finalGroup] = groups;
const final = matches
.filter((match) => match.group_id === singleBracket.id)
.pop();
if (!final) throw Error("Final not found.");
// 1st place: Final winner.
grouped[0] = [
helpers.findParticipant(participants, getFinalWinnerIfDefined(final)),
];
// Rest: every loser in reverse order.
const losers = helpers.getLosers(
participants,
matches.filter((match) => match.group_id === singleBracket.id),
);
grouped.push(...losers.reverse());
if (stage.settings?.consolationFinal) {
const consolationFinal = matches
.filter((match) => match.group_id === finalGroup.id)
.pop();
if (!consolationFinal) throw Error("Consolation final not found.");
const consolationFinalWinner = helpers.findParticipant(
participants,
getFinalWinnerIfDefined(consolationFinal),
);
const consolationFinalLoser = helpers.findParticipant(
participants,
helpers.getLoser(consolationFinal),
);
// Overwrite semi-final losers with the consolation final results.
grouped.splice(2, 1, [consolationFinalWinner], [consolationFinalLoser]);
}
return helpers.makeFinalStandings(grouped);
}
/**
* Returns the final standings of a double elimination stage.
*
* @param stageId ID of the stage.
*/
private doubleEliminationStandings(stageId: number): FinalStandingsItem[] {
const grouped: Participant[][] = [];
const {
stage: stages,
group: groups,
match: matches,
participant: participants,
} = this.stageData(stageId);
const [stage] = stages;
const [winnerBracket, loserBracket, finalGroup] = groups;
if (stage.settings?.grandFinal === "none") {
const finalWB = matches
.filter((match) => match.group_id === winnerBracket.id)
.pop();
if (!finalWB) throw Error("WB final not found.");
const finalLB = matches
.filter((match) => match.group_id === loserBracket.id)
.pop();
if (!finalLB) throw Error("LB final not found.");
// 1st place: WB Final winner.
grouped[0] = [
helpers.findParticipant(participants, getFinalWinnerIfDefined(finalWB)),
];
// 2nd place: LB Final winner.
grouped[1] = [
helpers.findParticipant(participants, getFinalWinnerIfDefined(finalLB)),
];
} else {
const grandFinalMatches = matches.filter(
(match) => match.group_id === finalGroup.id,
);
const decisiveMatch = helpers.getGrandFinalDecisiveMatch(
stage.settings?.grandFinal || "none",
grandFinalMatches,
);
// 1st place: Grand Final winner.
grouped[0] = [
helpers.findParticipant(
participants,
getFinalWinnerIfDefined(decisiveMatch),
),
];
// 2nd place: Grand Final loser.
grouped[1] = [
helpers.findParticipant(participants, helpers.getLoser(decisiveMatch)),
];
}
// Rest: every loser in reverse order.
const losers = helpers.getLosers(
participants,
matches.filter((match) => match.group_id === loserBracket.id),
);
grouped.push(...losers.reverse());
return helpers.makeFinalStandings(grouped);
}
/**
* Returns only the data specific to the given stage (without the participants).
*
@@ -441,9 +271,3 @@ export class Get extends BaseGetter {
};
}
}
const getFinalWinnerIfDefined = (match: Match): ParticipantSlot => {
const winner = helpers.getWinner(match);
if (!winner) throw Error("The final match does not have a winner.");
return winner;
};

View File

@@ -2,9 +2,7 @@ import type {
GrandFinalType,
Match,
MatchResults,
Participant,
ParticipantResult,
CustomParticipant,
Result,
RoundRobinMode,
Seeding,
@@ -19,16 +17,15 @@ import type {
Database,
DeepPartial,
Duel,
FinalStandingsItem,
IdMapping,
Nullable,
OmitId,
ParitySplit,
ParticipantSlot,
Scores,
Side,
} from "./types";
import { ordering } from "./ordering";
import invariant from "~/utils/invariant";
/**
* Splits an array of objects based on their values at a given key.
@@ -234,7 +231,6 @@ export function balanceByes(
*/
export function normalizeIds(data: Database): Database {
const mappings = {
participant: makeNormalizedIdMapping(data.participant),
stage: makeNormalizedIdMapping(data.stage),
group: makeNormalizedIdMapping(data.group),
round: makeNormalizedIdMapping(data.round),
@@ -242,10 +238,6 @@ export function normalizeIds(data: Database): Database {
};
return {
participant: data.participant.map((value) => ({
...value,
id: mappings.participant[value.id],
})),
stage: data.stage.map((value) => ({
...value,
id: mappings.stage[value.id],
@@ -267,8 +259,8 @@ export function normalizeIds(data: Database): Database {
stage_id: mappings.stage[value.stage_id],
group_id: mappings.group[value.group_id],
round_id: mappings.round[value.round_id],
opponent1: normalizeParticipant(value.opponent1, mappings.participant),
opponent2: normalizeParticipant(value.opponent2, mappings.participant),
opponent1: value.opponent1,
opponent2: value.opponent2,
})),
};
}
@@ -912,11 +904,8 @@ export function getOriginPosition(match: Match, side: Side): number {
* @param participants The list of participants.
* @param matches A list of matches to get losers of.
*/
export function getLosers(
participants: Participant[],
matches: Match[],
): Participant[][] {
const losers: Participant[][] = [];
export function getLosers(matches: Match[]): number[][] {
const losers: number[][] = [];
let currentRound: number | null = null;
let roundIndex = -1;
@@ -931,38 +920,13 @@ export function getLosers(
const loser = getLoser(match);
if (loser === null) continue;
losers[roundIndex].push(findParticipant(participants, loser));
invariant(loser.id, "Loser id not found");
losers[roundIndex].push(loser.id);
}
return losers;
}
/**
* Makes final standings based on participants grouped by ranking.
*
* @param grouped A list of participants grouped by ranking.
*/
export function makeFinalStandings(
grouped: Participant[][],
): FinalStandingsItem[] {
const standings: FinalStandingsItem[] = [];
let rank = 1;
for (const group of grouped) {
for (const participant of group) {
standings.push({
id: participant.id,
name: participant.name,
rank,
});
}
rank++;
}
return standings;
}
/**
* Returns the decisive match of a Grand Final.
*
@@ -986,24 +950,6 @@ export function getGrandFinalDecisiveMatch(
throw Error("The Grand Final is disabled.");
}
/**
* Finds a participant in a list.
*
* @param participants The list of participants.
* @param slot The slot of the participant to find.
*/
export function findParticipant(
participants: Participant[],
slot: ParticipantSlot,
): Participant {
if (!slot) throw Error("Cannot find a BYE participant.");
const participant = participants.find(
(participant) => participant.id === slot?.id,
);
if (!participant) throw Error("Participant not found.");
return participant;
}
/**
* Gets the side the winner of the current match will go to in the next match.
*
@@ -1226,115 +1172,6 @@ export function setResults(
}
}
/**
* Indicates if a seeding is filled with participants' IDs.
*
* @param seeding The seeding.
*/
export function isSeedingWithIds(seeding: Seeding): boolean {
return seeding.some((value) => typeof value === "number");
}
/**
* Extracts participants from a seeding, without the BYEs.
*
* @param tournamentId ID of the tournament.
* @param seeding The seeding (no IDs).
*/
export function extractParticipantsFromSeeding(
tournamentId: number,
seeding: Seeding,
): OmitId<Participant>[] {
const withoutByes = seeding.filter(
(name): name is /* number */ string | CustomParticipant => name !== null,
);
const participants = withoutByes.map<OmitId<Participant>>((item) => {
if (typeof item === "string") {
return {
tournament_id: tournamentId,
name: item,
};
}
return {
...item,
tournament_id: tournamentId,
name: item.name,
};
});
return participants;
}
/**
* Returns participant slots mapped to the instances stored in the database thanks to their name.
*
* @param seeding The seeding.
* @param database The participants stored in the database.
* @param positions An optional list of positions (seeds) for a manual ordering.
*/
export function mapParticipantsNamesToDatabase(
seeding: Seeding,
database: Participant[],
positions?: number[],
): ParticipantSlot[] {
return mapParticipantsToDatabase("name", seeding, database, positions);
}
/**
* Returns participant slots mapped to the instances stored in the database thanks to their id.
*
* @param seeding The seeding.
* @param database The participants stored in the database.
* @param positions An optional list of positions (seeds) for a manual ordering.
*/
export function mapParticipantsIdsToDatabase(
seeding: Seeding,
database: Participant[],
positions?: number[],
): ParticipantSlot[] {
return mapParticipantsToDatabase("id", seeding, database, positions);
}
/**
* Returns participant slots mapped to the instances stored in the database thanks to a property of theirs.
*
* @param prop The property to search participants with.
* @param seeding The seeding.
* @param database The participants stored in the database.
* @param positions An optional list of positions (seeds) for a manual ordering.
*/
export function mapParticipantsToDatabase(
prop: "id" | "name",
seeding: Seeding,
database: Participant[],
positions?: number[],
): ParticipantSlot[] {
const slots = seeding.map((slot, i) => {
if (slot === null) return null; // BYE.
const found = database.find((participant) =>
typeof slot === "object"
? participant[prop] === slot[prop]
: participant[prop] === slot,
);
if (!found) throw Error(`Participant ${prop} not found in database.`);
return { id: found.id, position: i + 1 };
});
if (!positions) return slots;
if (positions.length !== slots.length)
throw Error(
"Not enough seeds in at least one group of the manual ordering.",
);
return positions.map((position) => slots[position - 1]); // Because `position` is `i + 1`.
}
/**
* Converts a list of matches to a seeding.
*

View File

@@ -78,11 +78,6 @@ export class BracketsManager {
public import(data: Database, normalizeIds = false): void {
if (normalizeIds) data = helpers.normalizeIds(data);
if (!this.storage.delete("participant"))
throw Error("Could not empty the participant table.");
if (!this.storage.insert("participant", data.participant))
throw Error("Could not import participants.");
if (!this.storage.delete("stage"))
throw Error("Could not empty the stage table.");
if (!this.storage.insert("stage", data.stage))
@@ -108,9 +103,6 @@ export class BracketsManager {
* Exports data from the database.
*/
public export(): Database {
const participants = this.storage.select("participant");
if (!participants) throw Error("Error getting participants.");
const stages = this.storage.select("stage");
if (!stages) throw Error("Error getting stages.");
@@ -124,7 +116,6 @@ export class BracketsManager {
if (!matches) throw Error("Error getting matches.");
return {
participant: participants,
stage: stages,
group: groups,
round: rounds,

View File

@@ -51,13 +51,4 @@ export class Reset extends BaseUpdater {
if (!helpers.isRoundRobin(stage) && !helpers.isSwiss(stage))
this.updateRelatedMatches(stored, true, true);
}
/**
* Resets the seeding of a stage.
*
* @param stageId ID of the stage.
*/
public seeding(stageId: number): void {
this.updateSeeding(stageId, null);
}
}

View File

@@ -2,68 +2,10 @@ import * as assert from "uvu/assert";
import { BracketsManager } from "../manager";
import { InMemoryDatabase } from "~/modules/brackets-memory-db";
import { suite } from "uvu";
import type { InputStage } from "~/modules/brackets-model";
const storage = new InMemoryDatabase();
const manager = new BracketsManager(storage);
const createTournament = (tournamentType: any): InputStage => ({
name: "Amateur",
tournamentId: 0,
type: tournamentType,
seeding: [
{ name: "Team 1", nationality: "US", tournament_id: 0, id: 0 },
{ name: "Team 2", nationality: "US", tournament_id: 0, id: 1 },
{ name: "Team 3", nationality: "US", tournament_id: 0, id: 2 },
{ name: "Team 4", nationality: "US", tournament_id: 0, id: 3 },
{ name: "Team 5", nationality: "US", tournament_id: 0, id: 4 },
{ name: "Team 6", nationality: "US", tournament_id: 0, id: 5 },
{ name: "Team 7", nationality: "US", tournament_id: 0, id: 6 },
{ name: "Team 8", nationality: "US", tournament_id: 0, id: 7 },
{ name: "Team 9", nationality: "US", tournament_id: 0, id: 8 },
{ name: "Team 10", nationality: "US", tournament_id: 0, id: 9 },
{ name: "Team 11", nationality: "US", tournament_id: 0, id: 10 },
{ name: "Team 12", nationality: "US", tournament_id: 0, id: 11 },
{ name: "Team 13", nationality: "US", tournament_id: 0, id: 12 },
{ name: "Team 14", nationality: "US", tournament_id: 0, id: 13 },
{ name: "Team 15", nationality: "US", tournament_id: 0, id: 14 },
{ name: "Team 16", nationality: "US", tournament_id: 0, id: 15 },
],
settings:
tournamentType === "round_robin"
? { groupCount: 2 }
: { seedOrdering: ["natural"] },
});
const CustomSeeding = suite("Create tournaments with custom seeding");
CustomSeeding.before.each(() => {
storage.reset();
});
CustomSeeding("should create single elimination with custom seeding", () => {
manager.create(createTournament("single_elimination"));
const stageData = manager.get.stageData(0);
assert.equal((stageData.participant[0] as any).nationality, "US");
assert.equal(stageData.participant.length, 16);
});
CustomSeeding("should create double elimination with custom seeding", () => {
manager.create(createTournament("double_elimination"));
const stageData = manager.get.stageData(0);
assert.equal((stageData.participant[0] as any).nationality, "US");
assert.equal(stageData.participant.length, 16);
});
CustomSeeding("should create round robin with custom seeding", () => {
manager.create(createTournament("round_robin"));
const stageData = manager.get.stageData(0);
assert.equal((stageData.participant[0] as any).nationality, "US");
assert.equal(stageData.participant.length, 16);
});
const ExtraFields = suite("Update results with extra fields");
ExtraFields.before.each(() => {
@@ -75,7 +17,7 @@ ExtraFields("Extra fields when updating a match", () => {
name: "Amateur",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
});
manager.update.match({
@@ -127,5 +69,4 @@ ExtraFields("Extra fields when updating a match", () => {
});
});
CustomSeeding.run();
ExtraFields.run();

View File

@@ -17,7 +17,7 @@ DeleteStage("should delete a stage and all its linked data", () => {
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
});
manager.delete.stage(0);
@@ -38,14 +38,14 @@ DeleteStage("should delete one stage and only its linked data", () => {
name: "Example 1",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
});
manager.create({
name: "Example 2",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
});
manager.delete.stage(0);
@@ -72,14 +72,14 @@ DeleteStage("should delete all stages of the tournament", () => {
name: "Example 1",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
});
manager.create({
name: "Example 2",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
});
manager.delete.tournament(0);

View File

@@ -18,24 +18,7 @@ CreateDoubleElimination("should create a double elimination stage", () => {
name: "Amateur",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
"Team 9",
"Team 10",
"Team 11",
"Team 12",
"Team 13",
"Team 14",
"Team 15",
"Team 16",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
settings: { seedOrdering: ["natural"], grandFinal: "simple" },
});
@@ -48,30 +31,6 @@ CreateDoubleElimination("should create a double elimination stage", () => {
assert.equal(storage.select<any>("match")!.length, 30);
});
CreateDoubleElimination(
"should create a double elimination stage with only two participants",
() => {
// This is an edge case. No lower bracket nor grand final will be created.
manager.create({
name: "Example",
tournamentId: 0,
type: "double_elimination",
settings: { size: 2 },
});
assert.equal(storage.select<any>("group")!.length, 1);
assert.equal(storage.select<any>("round")!.length, 1);
assert.equal(storage.select<any>("match")!.length, 1);
// Ensure update works.
manager.update.seeding(0, ["Team 1", "Team 2"]);
manager.update.match({
id: 0,
opponent1: { result: "win" },
});
},
);
CreateDoubleElimination(
"should create a tournament with 256+ tournaments",
() => {
@@ -91,16 +50,7 @@ CreateDoubleElimination(
name: "Example with double grand final",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: { grandFinal: "double", seedOrdering: ["natural"] },
});
@@ -125,24 +75,7 @@ MatchUpdateDoubleElimination(
name: "Amateur",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
"Team 9",
"Team 10",
"Team 11",
"Team 12",
"Team 13",
"Team 14",
"Team 15",
"Team 16",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
settings: { seedOrdering: ["natural"], grandFinal: "simple" },
});
@@ -196,7 +129,7 @@ MatchUpdateDoubleElimination(
name: "Example",
tournamentId: 0,
type: "double_elimination",
seeding: ["Team 1", "Team 2", "Team 3", null],
seeding: [1, 2, 3, null],
settings: { grandFinal: "simple" },
});
@@ -234,7 +167,7 @@ MatchUpdateDoubleElimination("should determine matches in grand final", () => {
name: "Example",
tournamentId: 0,
type: "double_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: { grandFinal: "double" },
});
@@ -306,7 +239,7 @@ MatchUpdateDoubleElimination(
name: "Example",
tournamentId: 0,
type: "double_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: { grandFinal: "double" },
});
@@ -338,24 +271,7 @@ MatchUpdateDoubleElimination(
name: "Amateur",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
"Team 9",
"Team 10",
"Team 11",
"Team 12",
"Team 13",
"Team 14",
"Team 15",
"Team 16",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
settings: {
seedOrdering: ["natural", "reverse", "reverse"],
grandFinal: "simple",
@@ -397,16 +313,7 @@ MatchUpdateDoubleElimination(
name: "Example with inner_outer loser ordering",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: {
seedOrdering: ["inner_outer", "inner_outer"],
},
@@ -423,7 +330,7 @@ MatchUpdateDoubleElimination(
opponent1: { result: "win" }, // Loser id: 7.
});
assert.equal(storage.select<any>("match", 7).opponent1.id, 7);
assert.equal(storage.select<any>("match", 7).opponent1.id, 8);
// Match of position 2.
manager.update.match({
@@ -431,7 +338,7 @@ MatchUpdateDoubleElimination(
opponent1: { result: "win" }, // Loser id: 4.
});
assert.equal(storage.select<any>("match", 8).opponent1.id, 4);
assert.equal(storage.select<any>("match", 8).opponent1.id, 5);
// Match of position 3.
manager.update.match({
@@ -439,7 +346,7 @@ MatchUpdateDoubleElimination(
opponent1: { result: "win" }, // Loser id: 6.
});
assert.equal(storage.select<any>("match", 8).opponent2.id, 6);
assert.equal(storage.select<any>("match", 8).opponent2.id, 7);
// Match of position 4.
manager.update.match({
@@ -447,7 +354,7 @@ MatchUpdateDoubleElimination(
opponent1: { result: "win" }, // Loser id: 5.
});
assert.equal(storage.select<any>("match", 7).opponent2.id, 5);
assert.equal(storage.select<any>("match", 7).opponent2.id, 6);
},
);
@@ -460,24 +367,7 @@ SkipFirstRoundDoubleElimination.before.each(() => {
name: "Example with double grand final",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
"Team 9",
"Team 10",
"Team 11",
"Team 12",
"Team 13",
"Team 14",
"Team 15",
"Team 16",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
settings: {
seedOrdering: ["natural"],
skipFirstRound: true,
@@ -498,8 +388,8 @@ SkipFirstRoundDoubleElimination(
assert.equal(storage.select<any>("round", 0).number, 1); // Even though the "real" first round is skipped, the stored first round's number should be 1.
assert.equal(storage.select<any>("match", 0).opponent1.id, 0); // First match of WB.
assert.equal(storage.select<any>("match", 7).opponent1.id, 1); // First match of LB.
assert.equal(storage.select<any>("match", 0).opponent1.id, 1); // First match of WB.
assert.equal(storage.select<any>("match", 7).opponent1.id, 2); // First match of LB.
},
);
@@ -507,31 +397,31 @@ SkipFirstRoundDoubleElimination(
"should choose the correct previous and next matches",
() => {
manager.update.match({ id: 0, opponent1: { result: "win" } });
assert.equal(storage.select<any>("match", 7).opponent1.id, 1); // First match of LB Round 1 (must stay).
assert.equal(storage.select<any>("match", 12).opponent1.id, 2); // First match of LB Round 2 (must be updated).
assert.equal(storage.select<any>("match", 7).opponent1.id, 2); // First match of LB Round 1 (must stay).
assert.equal(storage.select<any>("match", 12).opponent1.id, 3); // First match of LB Round 2 (must be updated).
manager.update.match({ id: 1, opponent1: { result: "win" } });
assert.equal(storage.select<any>("match", 7).opponent2.id, 3); // First match of LB Round 1 (must stay).
assert.equal(storage.select<any>("match", 11).opponent1.id, 6); // Second match of LB Round 2 (must be updated).
assert.equal(storage.select<any>("match", 7).opponent2.id, 4); // First match of LB Round 1 (must stay).
assert.equal(storage.select<any>("match", 11).opponent1.id, 7); // Second match of LB Round 2 (must be updated).
manager.update.match({ id: 4, opponent1: { result: "win" } }); // First match of WB Round 2.
assert.equal(storage.select<any>("match", 18).opponent1.id, 4); // First match of LB Round 4.
assert.equal(storage.select<any>("match", 18).opponent1.id, 5); // First match of LB Round 4.
manager.update.match({ id: 7, opponent1: { result: "win" } }); // First match of LB Round 1.
assert.equal(storage.select<any>("match", 11).opponent2.id, 1); // First match of LB Round 2.
assert.equal(storage.select<any>("match", 11).opponent2.id, 2); // First match of LB Round 2.
for (let i = 2; i < 21; i++)
manager.update.match({ id: i, opponent1: { result: "win" } });
assert.equal(storage.select<any>("match", 15).opponent1.id, 6); // First match of LB Round 3.
assert.equal(storage.select<any>("match", 15).opponent1.id, 7); // First match of LB Round 3.
assert.equal(storage.select<any>("match", 21).opponent1.id, 0); // GF Round 1.
assert.equal(storage.select<any>("match", 21).opponent2.id, 8); // GF Round 1.
assert.equal(storage.select<any>("match", 21).opponent1.id, 1); // GF Round 1.
assert.equal(storage.select<any>("match", 21).opponent2.id, 9); // GF Round 1.
manager.update.match({ id: 21, opponent2: { result: "win" } });
assert.equal(storage.select<any>("match", 21).opponent1.id, 0); // GF Round 2.
assert.equal(storage.select<any>("match", 22).opponent2.id, 8); // GF Round 2.
assert.equal(storage.select<any>("match", 21).opponent1.id, 1); // GF Round 2.
assert.equal(storage.select<any>("match", 22).opponent2.id, 9); // GF Round 2.
manager.update.match({ id: 22, opponent2: { result: "win" } });
},

View File

@@ -19,16 +19,7 @@ FindSingleElimination("should find previous matches", () => {
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
});
const beforeFirst = manager.find.previousMatches(0);
@@ -55,16 +46,7 @@ FindSingleElimination("should find next matches", () => {
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
});
const afterFirst = manager.find.nextMatches(0);
@@ -86,33 +68,24 @@ FindSingleElimination(
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: {
seedOrdering: ["natural"],
},
});
manager.update.match({ id: 0, opponent1: { result: "loss" } });
const afterFirstEliminated = manager.find.nextMatches(0, 0);
const afterFirstEliminated = manager.find.nextMatches(0, 1);
assert.equal(afterFirstEliminated.length, 0);
const afterFirstContinued = manager.find.nextMatches(0, 1);
const afterFirstContinued = manager.find.nextMatches(0, 2);
assert.equal(afterFirstContinued.length, 1);
manager.update.match({ id: 1, opponent1: { result: "win" } });
const beforeSemi1Up = manager.find.previousMatches(4, 1);
const beforeSemi1Up = manager.find.previousMatches(4, 2);
assert.equal(beforeSemi1Up.length, 1);
assert.equal(beforeSemi1Up[0].id, 0);
const beforeSemi1Down = manager.find.previousMatches(4, 2);
const beforeSemi1Down = manager.find.previousMatches(4, 3);
assert.equal(beforeSemi1Down.length, 1);
assert.equal(beforeSemi1Down[0].id, 1);
},
@@ -131,16 +104,7 @@ FindDoubleElimination("should find previous matches", () => {
name: "Example",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
});
const beforeFirstWB = manager.find.previousMatches(0);
@@ -187,16 +151,7 @@ FindDoubleElimination("should find next matches", () => {
name: "Example",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
});
const afterFirstWB = manager.find.nextMatches(0);
@@ -236,33 +191,33 @@ FindDoubleElimination(
name: "Example",
tournamentId: 0,
type: "double_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: {
seedOrdering: ["natural"],
},
});
manager.update.match({ id: 0, opponent1: { result: "loss" } });
const afterFirstEliminated = manager.find.nextMatches(0, 0);
const afterFirstEliminated = manager.find.nextMatches(0, 1);
assert.equal(afterFirstEliminated.length, 1);
assert.equal(afterFirstEliminated[0].id, 3);
const afterFirstContinued = manager.find.nextMatches(0, 1);
const afterFirstContinued = manager.find.nextMatches(0, 2);
assert.equal(afterFirstContinued.length, 1);
assert.equal(afterFirstContinued[0].id, 2);
manager.update.match({ id: 1, opponent1: { result: "win" } });
const beforeSemi1Up = manager.find.previousMatches(2, 1);
const beforeSemi1Up = manager.find.previousMatches(2, 2);
assert.equal(beforeSemi1Up.length, 1);
assert.equal(beforeSemi1Up[0].id, 0);
const beforeSemi1Down = manager.find.previousMatches(2, 2);
const beforeSemi1Down = manager.find.previousMatches(2, 3);
assert.equal(beforeSemi1Down.length, 1);
assert.equal(beforeSemi1Down[0].id, 1);
manager.update.match({ id: 3, opponent1: { result: "loss" } });
const afterLowerBracketEliminated = manager.find.nextMatches(3, 0);
const afterLowerBracketEliminated = manager.find.nextMatches(3, 1);
assert.equal(afterLowerBracketEliminated.length, 0);
const afterLowerBracketContinued = manager.find.nextMatches(3, 3);
const afterLowerBracketContinued = manager.find.nextMatches(3, 4);
assert.equal(afterLowerBracketContinued.length, 1);
assert.equal(afterLowerBracketContinued[0].id, 4);

View File

@@ -17,11 +17,11 @@ BYEHandling("should propagate BYEs through the brackets", () => {
name: "Example with BYEs",
tournamentId: 0,
type: "double_elimination",
seeding: ["Team 1", null, null, null],
seeding: [1, null, null, null],
settings: { seedOrdering: ["natural"], grandFinal: "simple" },
});
assert.equal(storage.select<any>("match", 2).opponent1.id, 0);
assert.equal(storage.select<any>("match", 2).opponent1.id, 1);
assert.equal(storage.select<any>("match", 2).opponent2, null);
assert.equal(storage.select<any>("match", 3).opponent1, null);
@@ -30,7 +30,7 @@ BYEHandling("should propagate BYEs through the brackets", () => {
assert.equal(storage.select<any>("match", 4).opponent1, null);
assert.equal(storage.select<any>("match", 4).opponent2, null);
assert.equal(storage.select<any>("match", 5).opponent1.id, 0);
assert.equal(storage.select<any>("match", 5).opponent1.id, 1);
assert.equal(storage.select<any>("match", 5).opponent2, null);
});
@@ -39,7 +39,7 @@ BYEHandling("should handle incomplete seeding during creation", () => {
name: "Example with BYEs",
tournamentId: 0,
type: "double_elimination",
seeding: ["Team 1", "Team 2"],
seeding: [1, 2],
settings: {
seedOrdering: ["natural"],
balanceByes: false, // Default value.
@@ -47,8 +47,8 @@ BYEHandling("should handle incomplete seeding during creation", () => {
},
});
assert.equal(storage.select<any>("match", 0).opponent1.id, 0);
assert.equal(storage.select<any>("match", 0).opponent2.id, 1);
assert.equal(storage.select<any>("match", 0).opponent1.id, 1);
assert.equal(storage.select<any>("match", 0).opponent2.id, 2);
assert.equal(storage.select<any>("match", 1).opponent1, null);
assert.equal(storage.select<any>("match", 1).opponent2, null);
@@ -59,7 +59,7 @@ BYEHandling("should balance BYEs in the seeding", () => {
name: "Example with BYEs",
tournamentId: 0,
type: "double_elimination",
seeding: ["Team 1", "Team 2"],
seeding: [1, 2],
settings: {
seedOrdering: ["natural"],
balanceByes: true,
@@ -67,10 +67,10 @@ BYEHandling("should balance BYEs in the seeding", () => {
},
});
assert.equal(storage.select<any>("match", 0).opponent1.id, 0);
assert.equal(storage.select<any>("match", 0).opponent1.id, 1);
assert.equal(storage.select<any>("match", 0).opponent2, null);
assert.equal(storage.select<any>("match", 1).opponent1.id, 1);
assert.equal(storage.select<any>("match", 1).opponent1.id, 2);
assert.equal(storage.select<any>("match", 1).opponent2, null);
});
@@ -131,42 +131,6 @@ SpecialCases.before.each(() => {
storage.reset();
});
SpecialCases(
"should create a stage and add participants IDs in seeding",
() => {
const teams = [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
];
const participants = teams.map((name) => ({
tournament_id: 0,
name,
}));
// Simulation of external database filling for participants.
storage.insert("participant", participants);
manager.create({
name: "Example",
tournamentId: 0,
type: "single_elimination",
settings: { size: 8 },
});
// Update seeding with already existing IDs.
manager.update.seeding(0, [0, 1, 2, 3, 4, 5, 6, 7]);
assert.equal(storage.select<any>("match", 0).opponent1.id, 0);
},
);
SpecialCases("should throw if the name of the stage is not provided", () => {
assert.throws(
() =>
@@ -203,15 +167,7 @@ SpecialCases(
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
],
seeding: [1, 2, 3, 4, 5, 6, 7],
}),
"The library only supports a participant count which is a power of two.",
);
@@ -267,24 +223,7 @@ SeedingAndOrderingInElimination.before.each(() => {
name: "Amateur",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
"Team 9",
"Team 10",
"Team 11",
"Team 12",
"Team 13",
"Team 14",
"Team 15",
"Team 16",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
settings: {
seedOrdering: [
"inner_outer",
@@ -429,7 +368,7 @@ ResetMatchAndMatchGames("should reset results of a match", () => {
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2"],
seeding: [1, 2],
settings: {
seedOrdering: ["natural"],
size: 8,
@@ -478,7 +417,7 @@ ResetMatchAndMatchGames(
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: {
seedOrdering: ["natural"],
},
@@ -517,7 +456,7 @@ ImportExport("should import data in the storage", () => {
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: {
seedOrdering: ["natural"],
},
@@ -552,90 +491,12 @@ ImportExport("should import data in the storage", () => {
assert.equal(storage.select<any>("match", 1).opponent1.result, undefined);
});
ImportExport("should import data in the storage with normalized IDs", () => {
storage.insert("participant", { name: "Unused team" });
manager.create({
name: "Example 1",
tournamentId: 0,
type: "round_robin",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
settings: {
groupCount: 1,
},
});
manager.create({
name: "Example 2",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 5", "Team 6", "Team 7", "Team 8"],
settings: {
seedOrdering: ["natural"],
},
});
const initialData = manager.get.stageData(1);
assert.equal(initialData.stage[0].id, 1);
assert.equal(initialData.participant[0], {
id: 1,
tournament_id: 0,
name: "Team 1",
});
assert.equal(initialData.group[0], { id: 1, stage_id: 1, number: 1 });
assert.equal(initialData.round[0], {
id: 3,
stage_id: 1,
group_id: 1,
number: 1,
});
assert.equal(initialData.match[0], {
id: 6,
stage_id: 1,
group_id: 1,
round_id: 3,
opponent1: { id: 5, position: 1 },
opponent2: { id: 6, position: 2 },
number: 1,
status: 2,
});
manager.import(initialData, true);
const data = manager.get.stageData(0);
assert.equal(data.stage[0].id, 0);
assert.equal(data.participant[0], {
id: 0,
tournament_id: 0,
name: "Team 1",
});
assert.equal(data.group[0], { id: 0, stage_id: 0, number: 1 });
assert.equal(data.round[0], {
id: 0,
stage_id: 0,
group_id: 0,
number: 1,
});
assert.equal(data.match[0], {
id: 0,
stage_id: 0,
group_id: 0,
round_id: 0,
opponent1: { id: 4, position: 1 },
opponent2: { id: 5, position: 2 },
number: 1,
status: 2,
});
});
ImportExport("should export data from the storage", () => {
manager.create({
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: {
seedOrdering: ["natural"],
},
@@ -643,11 +504,10 @@ ImportExport("should export data from the storage", () => {
const data = manager.export();
for (const key of ["participant", "stage", "group", "round", "match"]) {
for (const key of ["stage", "group", "round", "match"]) {
assert.ok(Object.keys(data).includes(key));
}
assert.equal(storage.select<any>("participant"), data.participant);
assert.equal(storage.select<any>("stage"), data.stage);
assert.equal(storage.select<any>("group"), data.group);
assert.equal(storage.select<any>("round"), data.round);

View File

@@ -6,195 +6,6 @@ import * as assert from "uvu/assert";
const storage = new InMemoryDatabase();
const manager = new BracketsManager(storage);
const GetFinalStandings = suite("Get final standings");
GetFinalStandings.before.each(() => {
storage.reset();
});
GetFinalStandings(
"should get the final standings for a single elimination stage with consolation final",
() => {
manager.create({
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
settings: { consolationFinal: true },
});
for (let i = 0; i < 8; i++) {
manager.update.match({
id: i,
...(i % 2 === 0
? { opponent1: { result: "win" } }
: { opponent2: { result: "win" } }),
});
}
const finalStandings = manager.get.finalStandings(0);
assert.equal(finalStandings, [
{ id: 0, name: "Team 1", rank: 1 },
{ id: 5, name: "Team 6", rank: 2 },
// The consolation final has inverted those ones (rank 3).
{ id: 1, name: "Team 2", rank: 3 },
{ id: 4, name: "Team 5", rank: 4 },
{ id: 7, name: "Team 8", rank: 5 },
{ id: 3, name: "Team 4", rank: 5 },
{ id: 6, name: "Team 7", rank: 5 },
{ id: 2, name: "Team 3", rank: 5 },
]);
},
);
GetFinalStandings(
"should get the final standings for a single elimination stage without consolation final",
() => {
manager.create({
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
settings: { consolationFinal: false },
});
for (let i = 0; i < 7; i++) {
manager.update.match({
id: i,
...(i % 2 === 0
? { opponent1: { result: "win" } }
: { opponent2: { result: "win" } }),
});
}
const finalStandings = manager.get.finalStandings(0);
assert.equal(finalStandings, [
{ id: 0, name: "Team 1", rank: 1 },
{ id: 5, name: "Team 6", rank: 2 },
// Here, they are not inverted (rank 3).
{ id: 4, name: "Team 5", rank: 3 },
{ id: 1, name: "Team 2", rank: 3 },
{ id: 7, name: "Team 8", rank: 4 },
{ id: 3, name: "Team 4", rank: 4 },
{ id: 6, name: "Team 7", rank: 4 },
{ id: 2, name: "Team 3", rank: 4 },
]);
},
);
GetFinalStandings(
"should get the final standings for a double elimination stage with a grand final",
() => {
manager.create({
name: "Example",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
settings: { grandFinal: "double" },
});
for (let i = 0; i < 15; i++) {
manager.update.match({
id: i,
...(i % 2 === 0
? { opponent1: { result: "win" } }
: { opponent2: { result: "win" } }),
});
}
const finalStandings = manager.get.finalStandings(0);
assert.equal(finalStandings, [
{ id: 0, name: "Team 1", rank: 1 },
{ id: 5, name: "Team 6", rank: 2 },
{ id: 4, name: "Team 5", rank: 3 },
{ id: 3, name: "Team 4", rank: 4 },
{ id: 1, name: "Team 2", rank: 5 },
{ id: 6, name: "Team 7", rank: 5 },
{ id: 7, name: "Team 8", rank: 6 },
{ id: 2, name: "Team 3", rank: 6 },
]);
},
);
GetFinalStandings(
"should get the final standings for a double elimination stage without a grand final",
() => {
manager.create({
name: "Example",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
settings: { grandFinal: "none" },
});
for (let i = 0; i < 13; i++) {
manager.update.match({
id: i,
// The parity is reversed here, just to have different results.
...(i % 2 === 1
? { opponent1: { result: "win" } }
: { opponent2: { result: "win" } }),
});
}
const finalStandings = manager.get.finalStandings(0);
assert.equal(finalStandings, [
{ id: 6, name: "Team 7", rank: 1 },
{ id: 2, name: "Team 3", rank: 2 },
{ id: 3, name: "Team 4", rank: 3 },
{ id: 5, name: "Team 6", rank: 4 },
{ id: 0, name: "Team 1", rank: 5 },
{ id: 7, name: "Team 8", rank: 5 },
{ id: 4, name: "Team 5", rank: 6 },
{ id: 1, name: "Team 2", rank: 6 },
]);
},
);
const GetSeeding = suite("Get seeding");
GetSeeding("should get the seeding of a round-robin stage", () => {
@@ -228,44 +39,13 @@ GetSeeding("should get the seeding of a round-robin stage with BYEs", () => {
groupCount: 2,
size: 8,
},
seeding: ["Team 1", null, null, null, null, null, null, null],
seeding: [1, null, null, null, null, null, null, null],
});
const seeding = manager.get.seeding(0);
assert.equal(seeding.length, 8);
});
GetSeeding(
"should get the seeding of a round-robin stage with BYEs after update",
() => {
storage.reset();
manager.create({
name: "Example",
tournamentId: 0,
type: "round_robin",
settings: {
groupCount: 2,
size: 8,
},
});
manager.update.seeding(0, [
"Team 1",
null,
null,
null,
null,
null,
null,
null,
]);
const seeding = manager.get.seeding(0);
assert.equal(seeding.length, 8);
},
);
GetSeeding("should get the seeding of a single elimination stage", () => {
storage.reset();
@@ -289,16 +69,7 @@ GetSeeding("should get the seeding with BYEs", () => {
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
null,
"Team 3",
"Team 4",
"Team 5",
null,
null,
"Team 8",
],
seeding: [1, null, 2, 3, 4, null, null, 5],
settings: {
seedOrdering: ["inner_outer"],
},
@@ -307,16 +78,15 @@ GetSeeding("should get the seeding with BYEs", () => {
const seeding = manager.get.seeding(0);
assert.equal(seeding.length, 8);
assert.equal(seeding, [
{ id: 0, position: 1 },
{ id: 1, position: 1 },
null,
{ id: 1, position: 3 },
{ id: 2, position: 4 },
{ id: 3, position: 5 },
{ id: 2, position: 3 },
{ id: 3, position: 4 },
{ id: 4, position: 5 },
null,
null,
{ id: 4, position: 8 },
{ id: 5, position: 8 },
]);
});
GetFinalStandings.run();
GetSeeding.run();

View File

@@ -17,16 +17,7 @@ CreateRoundRobinStage("should create a round-robin stage", () => {
name: "Example",
tournamentId: 0,
type: "round_robin",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: { groupCount: 2 },
} as any;
@@ -48,16 +39,7 @@ CreateRoundRobinStage(
name: "Example",
tournamentId: 0,
type: "round_robin",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: {
groupCount: 2,
manualOrdering: [
@@ -92,16 +74,7 @@ CreateRoundRobinStage(
name: "Example",
tournamentId: 0,
type: "round_robin",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: {
groupCount: 2,
manualOrdering: [[1, 4, 6, 7]],
@@ -116,16 +89,7 @@ CreateRoundRobinStage(
name: "Example",
tournamentId: 0,
type: "round_robin",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: {
groupCount: 2,
manualOrdering: [
@@ -146,16 +110,7 @@ CreateRoundRobinStage(
name: "Example",
tournamentId: 0,
type: "round_robin",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
null,
null,
null,
],
seeding: [1, 2, 3, 4, 5, null, null, null],
settings: { groupCount: 2 },
} as any;
@@ -192,24 +147,15 @@ CreateRoundRobinStage(
name: "Example with effort balanced",
tournamentId: 0,
type: "round_robin",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: {
groupCount: 2,
seedOrdering: ["groups.seed_optimized"],
},
});
assert.equal(storage.select<any>("match", 0).opponent1.id, 0);
assert.equal(storage.select<any>("match", 0).opponent2.id, 7);
assert.equal(storage.select<any>("match", 0).opponent1.id, 1);
assert.equal(storage.select<any>("match", 0).opponent2.id, 8);
},
);
@@ -253,7 +199,7 @@ UpdateRoundRobinScores.before.each(() => {
name: "Example scores",
tournamentId: 0,
type: "round_robin",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: { groupCount: 1 },
});
});
@@ -269,7 +215,7 @@ ExampleUseCase.before.each(() => {
name: "Example scores",
tournamentId: 0,
type: "round_robin",
seeding: ["POCEBLO", "twitch.tv/mrs_fly", "Ballec Squad", "AQUELLEHEURE?!"],
seeding: [1, 2, 3, 4],
settings: { groupCount: 1 },
});
});

View File

@@ -18,24 +18,7 @@ CreateSingleEliminationStage("should create a single elimination stage", () => {
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
"Team 9",
"Team 10",
"Team 11",
"Team 12",
"Team 13",
"Team 14",
"Team 15",
"Team 16",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
settings: { seedOrdering: ["natural"] },
} as any;
@@ -57,20 +40,11 @@ CreateSingleEliminationStage(
name: "Example with BYEs",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
null,
"Team 3",
"Team 4",
null,
null,
"Team 7",
"Team 8",
],
seeding: [1, null, 3, 4, null, null, 7, 8],
settings: { seedOrdering: ["natural"] },
});
assert.equal(storage.select<any>("match", 4).opponent1.id, 0); // Determined because of opponent's BYE.
assert.equal(storage.select<any>("match", 4).opponent1.id, 1); // Determined because of opponent's BYE.
assert.equal(storage.select<any>("match", 4).opponent2.id, null); // To be determined.
assert.equal(storage.select<any>("match", 5).opponent1, null); // BYE propagated.
assert.equal(storage.select<any>("match", 5).opponent2.id, null); // To be determined.
@@ -84,16 +58,7 @@ CreateSingleEliminationStage(
name: "Example with consolation final",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: { consolationFinal: true, seedOrdering: ["natural"] },
});
@@ -110,21 +75,12 @@ CreateSingleEliminationStage(
name: "Example with consolation final and BYEs",
tournamentId: 0,
type: "single_elimination",
seeding: [
null,
null,
null,
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [null, null, null, 4, 5, 6, 7, 8],
settings: { consolationFinal: true, seedOrdering: ["natural"] },
});
assert.equal(storage.select<any>("match", 4).opponent1, null);
assert.equal(storage.select<any>("match", 4).opponent2.id, 0);
assert.equal(storage.select<any>("match", 4).opponent2.id, 4);
// Consolation final
assert.equal(storage.select<any>("match", 7).opponent1, null);
@@ -139,16 +95,7 @@ CreateSingleEliminationStage(
name: "Example with Bo3 matches",
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8],
settings: { seedOrdering: ["natural"] },
});
@@ -258,10 +205,10 @@ CreateSingleEliminationStage(
tournamentId: 0,
type: "single_elimination",
seeding: [
"Team 1",
"Team 1", // Duplicate
"Team 3",
"Team 4",
1,
1, // Duplicate
3,
4,
],
}),
"The seeding has a duplicate participant.",
@@ -276,7 +223,7 @@ CreateSingleEliminationStage(
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
});
assert.throws(
@@ -303,7 +250,7 @@ PreviousAndNextMatchUpdate(
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: { consolationFinal: true },
});
@@ -341,7 +288,7 @@ PreviousAndNextMatchUpdate(
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: { consolationFinal: true },
});

View File

@@ -11,24 +11,7 @@ const example = {
name: "Amateur",
tournamentId: 0,
type: "double_elimination",
seeding: [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
"Team 9",
"Team 10",
"Team 11",
"Team 12",
"Team 13",
"Team 14",
"Team 15",
"Team 16",
],
seeding: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
settings: { seedOrdering: ["natural"] },
} as any;
@@ -67,7 +50,7 @@ UpdateMatches(
assert.equal(after.opponent1.score, 2);
// Name should stay. It shouldn't be overwritten.
assert.equal(after.opponent1.id, 0);
assert.equal(after.opponent1.id, 1);
},
);
@@ -94,7 +77,7 @@ UpdateMatches(
opponent1: { result: "win" },
});
assert.equal(storage.select<any>("match", 8).opponent1.id, 0);
assert.equal(storage.select<any>("match", 8).opponent1.id, 1);
manager.update.match({
id: 0,
@@ -108,7 +91,7 @@ UpdateMatches(
const nextMatch = storage.select<any>("match", 8);
assert.equal(nextMatch.status, Status.Waiting);
assert.equal(nextMatch.opponent1.id, 1);
assert.equal(nextMatch.opponent1.id, 2);
},
);
@@ -241,7 +224,7 @@ GiveOpponentIds.before.each(() => {
name: "Amateur",
tournamentId: 0,
type: "double_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
seeding: [1, 2, 3, 4],
settings: { seedOrdering: ["natural"] },
});
});
@@ -250,11 +233,11 @@ GiveOpponentIds("should update the right opponents based on their IDs", () => {
manager.update.match({
id: 0,
opponent1: {
id: 1,
id: 2,
score: 10,
},
opponent2: {
id: 0,
id: 1,
score: 5,
},
});
@@ -271,7 +254,7 @@ GiveOpponentIds(
manager.update.match({
id: 0,
opponent1: {
id: 1,
id: 2,
score: 10,
},
});
@@ -291,7 +274,7 @@ GiveOpponentIds(
manager.update.match({
id: 0,
opponent1: {
id: 2, // Belongs to match id 1.
id: 3, // Belongs to match id 1.
score: 10,
},
}),
@@ -330,387 +313,6 @@ LockedMatches(
},
);
const Seeding = suite("Seeding");
Seeding.before.each(() => {
storage.reset();
manager.create({
name: "Without participants",
tournamentId: 0,
type: "double_elimination",
settings: {
size: 8,
seedOrdering: ["natural"],
},
});
});
Seeding("should update the seeding in a stage without any participant", () => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]);
assert.equal(storage.select<any>("match", 0).opponent1.id, 0);
assert.equal(storage.select<any>("participant")!.length, 8);
});
Seeding("should update the seeding to remove participants", () => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
null,
"Team 4",
"Team 5",
"Team 6",
"Team 7",
null,
]);
assert.equal(storage.select<any>("match", 0).opponent1.id, 0);
// In this context, a `null` value is not a BYE, but a TDB (to be determined)
// because we consider the tournament might have been started.
// If it's not and you prefer BYEs, just recreate the tournament.
assert.equal(storage.select<any>("match", 1).opponent1.id, null);
assert.equal(storage.select<any>("match", 3).opponent2.id, null);
});
Seeding("should handle incomplete seeding during seeding update", () => {
manager.update.seeding(0, ["Team 1", "Team 2"]);
assert.equal(storage.select<any>("match", 0).opponent1.id, 0);
assert.equal(storage.select<any>("match", 0).opponent2.id, 1);
// Same here, see comments above.
assert.equal(storage.select<any>("match", 1).opponent1.id, null);
assert.equal(storage.select<any>("match", 1).opponent2.id, null);
});
Seeding("should update BYE to TBD during seeding update", () => {
storage.reset();
manager.create({
name: "With participants and BYEs",
tournamentId: 0,
type: "double_elimination",
seeding: [
null,
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
],
settings: {
seedOrdering: ["natural"],
},
});
assert.equal(storage.select<any>("match", 0).opponent1, null);
manager.update.seeding(0, [
null,
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]);
// To stay consistent with the fact that `update.seeding()` uses TBD and not BYE,
// the BYE should be updated to TDB here.
assert.equal(storage.select<any>("match", 0).opponent1.id, null);
});
Seeding("should reset the seeding of a stage", () => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]);
manager.reset.seeding(0);
assert.equal(storage.select<any>("match", 0).opponent1.id, null);
assert.equal(storage.select<any>("participant")!.length, 8); // Participants aren't removed.
});
Seeding(
"should update the seeding in a stage with participants already",
() => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]);
manager.update.seeding(0, [
"Team A",
"Team B",
"Team C",
"Team D",
"Team E",
"Team F",
"Team G",
"Team H",
]);
assert.equal(storage.select<any>("match", 0).opponent1.id, 8);
assert.equal(storage.select<any>("participant")!.length, 16);
},
);
Seeding(
"should update the seeding in a stage by registering only one missing participant",
() => {
manager.update.seeding(0, [
"Team A",
"Team B",
"Team C",
"Team D",
"Team E",
"Team F",
"Team G",
"Team H",
]);
manager.update.seeding(0, [
"Team A",
"Team B", // Match 0.
"Team C",
"Team D", // Match 1.
"Team E",
"Team F", // Match 2.
"Team G",
"Team Z", // Match 3.
]);
assert.equal(storage.select<any>("match", 0).opponent1.id, 0);
assert.equal(storage.select<any>("match", 3).opponent2.id, 8);
assert.equal(storage.select<any>("participant")!.length, 9);
},
);
Seeding("should update the seeding in a stage on non-locked matches", () => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]);
manager.update.match({
id: 2, // Any match id.
opponent1: { score: 1 },
opponent2: { score: 0 },
});
manager.update.seeding(0, [
"Team A",
"Team B", // Match 0.
"Team C",
"Team D", // Match 1.
"Team 5",
"Team 6", // Match 2. NO CHANGE.
"Team G",
"Team H", // Match 3.
]);
assert.equal(storage.select<any>("match", 0).opponent1.id, 8); // New id.
assert.equal(storage.select<any>("match", 2).opponent1.id, 4); // Still old id.
assert.equal(storage.select<any>("participant")!.length, 8 + 6);
});
Seeding(
"should update the seeding and keep completed matches completed",
() => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]);
manager.update.match({
id: 0,
opponent1: { score: 1, result: "win" },
opponent2: { score: 0 },
});
manager.update.seeding(0, [
"Team 1",
"Team 2", // Keep this pair.
"Team 4",
"Team 3",
"Team 6",
"Team 5",
"Team 8",
"Team 7",
]);
const match = storage.select<any>("match", 0);
assert.equal(match.opponent1.result, "win");
assert.equal(match.status, Status.Completed);
},
);
Seeding(
"should throw if a match is completed and would have to be changed",
() => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]);
manager.update.match({
id: 0,
opponent1: { score: 1, result: "win" },
opponent2: { score: 0 },
});
assert.throws(
() =>
manager.update.seeding(0, [
"Team 2",
"Team 1", // Change this pair.
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]),
"A match is locked.",
);
},
);
Seeding(
"should throw if a match is locked and would have to be changed",
() => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]);
manager.update.match({
id: 2, // Any match id.
opponent1: { score: 1 },
opponent2: { score: 0 },
});
assert.throws(
() =>
manager.update.seeding(0, [
"Team A",
"Team B", // Match 0.
"Team C",
"Team D", // Match 1.
"WILL",
"THROW", // Match 2.
"Team G",
"Team H", // Match 3.
]),
"A match is locked.",
);
},
);
Seeding("should throw if the seeding has duplicate participants", () => {
assert.throws(
() =>
manager.update.seeding(0, [
"Team 1",
"Team 1", // Duplicate
"Team 3",
"Team 4",
"Team 5",
"Team 6",
"Team 7",
"Team 8",
]),
"The seeding has a duplicate participant.",
);
});
Seeding("should confirm the current seeding", () => {
manager.update.seeding(0, [
"Team 1",
"Team 2",
null,
"Team 4",
"Team 5",
null,
null,
null,
]);
assert.equal(storage.select<any>("match", 1).opponent1.id, null); // First, is a TBD.
assert.equal(storage.select<any>("match", 2).opponent2.id, null);
assert.equal(storage.select<any>("match", 3).opponent1.id, null);
assert.equal(storage.select<any>("match", 3).opponent2.id, null);
manager.update.confirmSeeding(0);
assert.equal(storage.select<any>("participant")!.length, 4);
assert.equal(storage.select<any>("match", 1).opponent1, null); // Should become a BYE.
assert.equal(storage.select<any>("match", 2).opponent2, null);
assert.equal(storage.select<any>("match", 3).opponent1, null);
assert.equal(storage.select<any>("match", 3).opponent2, null);
assert.equal(storage.select<any>("match", 5).opponent2, null); // A BYE should be propagated here.
assert.equal(storage.select<any>("match", 7).opponent2, null); // All of these too (in loser bracket).
assert.equal(storage.select<any>("match", 8).opponent1, null);
assert.equal(storage.select<any>("match", 8).opponent2, null);
assert.equal(storage.select<any>("match", 9).opponent1, null);
assert.equal(storage.select<any>("match", 10).opponent2, null);
});
UpdateMatches.run();
GiveOpponentIds.run();
LockedMatches.run();
Seeding.run();

View File

@@ -1,7 +1,6 @@
import type {
Group,
Match,
Participant,
Round,
SeedOrdering,
Stage,
@@ -90,7 +89,6 @@ export interface DataTypes {
group: Group;
round: Round;
match: Match;
participant: Participant;
}
/**

View File

@@ -1,9 +1,4 @@
import type {
Match,
Round,
Seeding,
SeedOrdering,
} from "~/modules/brackets-model";
import type { Match, Round, SeedOrdering } from "~/modules/brackets-model";
import { Status } from "~/modules/brackets-model";
import { ordering } from "./ordering";
import { BaseUpdater } from "./base/updater";
@@ -65,27 +60,6 @@ export class Update extends BaseUpdater {
this.updateRoundOrdering(round, method);
}
/**
* Updates the seeding of a stage.
*
* @param stageId ID of the stage.
* @param seeding The new seeding.
*/
public seeding(stageId: number, seeding: Seeding): void {
this.updateSeeding(stageId, seeding);
}
/**
* Confirms the seeding of a stage.
*
* This will convert TBDs to BYEs and propagate them.
*
* @param stageId ID of the stage.
*/
public confirmSeeding(stageId: number): void {
this.confirmCurrentSeeding(stageId);
}
/**
* Update the seed ordering of a round.
*

View File

@@ -8,7 +8,6 @@ import type {
export class InMemoryDatabase implements CrudInterface {
protected data: Database = {
participant: [],
stage: [],
group: [],
round: [],
@@ -40,7 +39,6 @@ export class InMemoryDatabase implements CrudInterface {
*/
reset(): void {
this.data = {
participant: [],
stage: [],
group: [],
round: [],

View File

@@ -2,7 +2,6 @@
* Contains everything which is given by the user as input.
*-----------------------------------------------------------*/
import type { Participant } from "./storage";
import type {
GrandFinalType,
RoundRobinMode,
@@ -10,12 +9,6 @@ import type {
StageType,
} from "./unions";
/**
* A participant as it would be persisted in the storage, but with extra fields.
*/
export type CustomParticipant<ExtraFields = Record<string, unknown>> =
Participant & ExtraFields;
/**
* The seeding for a stage.
*
@@ -25,7 +18,7 @@ export type CustomParticipant<ExtraFields = Record<string, unknown>> =
* - Its ID.
* - Or a BYE: `null`.
*/
export type Seeding = (CustomParticipant | string | number | null)[];
export type Seeding = (number | null)[];
/**
* Used to create a stage.

View File

@@ -10,16 +10,6 @@ import type { StageType } from "./unions";
/**
* A participant of a stage (team or individual).
*/
export interface Participant {
/** ID of the participant. */
id: number;
/** ID of the tournament this participant belongs to. */
tournament_id: number;
/** Name of the participant. */
name: string;
}
/**
* A stage, which can be a round-robin stage or a single/double elimination stage.