Port "Play final and consolation final in parallel"

4b61707c2d (diff-62b417fe480c807f60ec4a160e2c3a621587b08c6d674fc0ee5bdd8d8cd73f84)
This commit is contained in:
Kalle
2023-07-03 10:53:39 +03:00
parent 54940b12df
commit 3157efe375
6 changed files with 177 additions and 41 deletions

View File

@@ -108,7 +108,7 @@ export class BaseGetter {
return this.getPreviousMatchesLB(match, stage, roundNumber);
if (matchLocation === "final_group")
return this.getPreviousMatchesFinal(match, roundNumber);
return this.getPreviousMatchesFinal(match, stage, roundNumber);
if (roundNumber === 1) return []; // The match is in the first round of an upper bracket.
@@ -119,23 +119,89 @@ export class BaseGetter {
* Gets the matches leading to the given match, which is in a final group (consolation final or grand final).
*
* @param match The current match.
* @param stage The parent stage.
* @param roundNumber Number of the current round.
*/
private getPreviousMatchesFinal(match: Match, roundNumber: number): Match[] {
private getPreviousMatchesFinal(
match: Match,
stage: Stage,
roundNumber: number
): Match[] {
if (stage.type === "single_elimination")
return this.getPreviousMatchesFinalSingleElimination(match, stage);
return this.getPreviousMatchesFinalDoubleElimination(match, roundNumber);
}
/**
* Gets the matches leading to the given match, which is in a final group (consolation final).
*
* @param match The current match.
* @param stage The parent stage.
*/
private getPreviousMatchesFinalSingleElimination(
match: Match,
stage: Stage
): Match[] {
const upperBracket = this.getUpperBracket(match.stage_id);
const upperBracketRoundCount = helpers.getUpperBracketRoundCount(
stage.settings.size!
);
const semiFinalsRound = this.storage.selectFirst("round", {
group_id: upperBracket.id,
number: upperBracketRoundCount - 1, // Second to last round
});
if (!semiFinalsRound) throw Error("Semi finals round not found.");
const semiFinalMatches = this.storage.select("match", {
round_id: semiFinalsRound.id,
});
if (!semiFinalMatches) throw Error("Error getting semi final matches.");
// In single elimination, both the final and consolation final have the same previous matches.
return semiFinalMatches;
}
/**
* Gets the matches leading to the given match, which is in a final group (grand final).
*
* @param match The current match.
* @param stage The parent stage.
* @param roundNumber Number of the current round.
*/
private getPreviousMatchesFinalDoubleElimination(
match: Match,
roundNumber: number
): Match[] {
if (roundNumber > 1)
// Double grand final
return [this.findMatch(match.group_id, roundNumber - 1, 1)];
const upperBracket = this.getUpperBracket(match.stage_id);
const lastRound = this.getLastRound(upperBracket.id);
const winnerBracket = this.getUpperBracket(match.stage_id);
const lastRoundWB = this.getLastRound(winnerBracket.id);
const upperBracketFinalMatch = this.storage.selectFirst("match", {
round_id: lastRound.id,
const winnerBracketFinalMatch = this.storage.selectFirst("match", {
round_id: lastRoundWB.id,
number: 1,
});
if (upperBracketFinalMatch === null) throw Error("Match not found.");
if (!winnerBracketFinalMatch) throw Error("Match not found.");
return [upperBracketFinalMatch];
const loserBracket = this.getLoserBracket(match.stage_id);
if (!loserBracket) throw Error("Loser bracket not found.");
const lastRoundLB = this.getLastRound(loserBracket.id);
const loserBracketFinalMatch = this.storage.selectFirst("match", {
round_id: lastRoundLB.id,
number: 1,
});
if (!loserBracketFinalMatch) throw Error("Match not found.");
return [winnerBracketFinalMatch, loserBracketFinalMatch];
}
/**

View File

@@ -284,6 +284,8 @@ export class BaseUpdater extends BaseGetter {
*/
protected archiveMatches(matches: Match[]): void {
for (const match of matches) {
if (match.status === Status.Archived) continue;
match.status = Status.Archived;
this.applyMatchUpdate(match);
}
@@ -324,7 +326,13 @@ export class BaseUpdater extends BaseGetter {
roundNumber,
roundCount
);
if (nextMatches.length === 0) return;
if (nextMatches.length === 0) {
// Archive match if it doesn't have following matches and is completed.
// When the stage is fully complete, all matches should be archived.
if (match.status === Status.Completed) this.archiveMatches([match]);
return;
}
const winnerSide = helpers.getMatchResult(match);
const actualRoundNumber =

View File

@@ -160,18 +160,6 @@ ExtraFields("Extra fields when updating a match game", () => {
// @ts-expect-error incomplete types
foo: 42, // Extra field.
},
opponent2: {
score: 1,
result: "loss",
},
});
manager.update.matchGame({
id: 2,
opponent1: {
score: 3,
result: "win",
},
opponent2: {
score: 1,
result: "loss",
@@ -182,7 +170,7 @@ ExtraFields("Extra fields when updating a match game", () => {
assert.equal(storage.select<any>("match_game", 0).weather, "rainy");
assert.equal(storage.select<any>("match_game", 1).opponent1.foo, 42);
assert.equal(storage.select<any>("match_game", 2).opponent2.info, {
assert.equal(storage.select<any>("match_game", 1).opponent2.info, {
replacements: [1, 2],
});
});

View File

@@ -288,6 +288,21 @@ MatchUpdateDoubleElimination("should determine matches in grand final", () => {
storage.select<any>("match", 6).opponent2.id, // Determined opponent for the grand final (round 2)
storage.select<any>("match", 1).opponent2.id // Winner of LB Final
);
assert.equal(storage.select<any>("match", 2).status, Status.Archived);
assert.equal(storage.select<any>("match", 4).status, Status.Archived);
assert.equal(storage.select<any>("match", 5).status, Status.Completed); // Grand final (round 1)
assert.equal(storage.select<any>("match", 6).status, Status.Ready); // Grand final (round 2)
manager.update.match({
id: 6, // Grand Final round 2
opponent1: { score: 16, result: "win" },
opponent2: { score: 10 },
});
assert.equal(storage.select<any>("match", 5).status, Status.Archived); // Grand final (round 1)
assert.equal(storage.select<any>("match", 6).status, Status.Archived); // Grand final (round 2)
});
MatchUpdateDoubleElimination(

View File

@@ -45,9 +45,9 @@ CreateSingleEliminationStage("should create a single elimination stage", () => {
assert.equal(stage.name, example.name);
assert.equal(stage.type, example.type);
assert.equal(storage.select("group")!.length, 1);
assert.equal(storage.select("round")!.length, 4);
assert.equal(storage.select("match")!.length, 15);
assert.equal(storage.select<any>("group")!.length, 1);
assert.equal(storage.select<any>("round")!.length, 4);
assert.equal(storage.select<any>("match")!.length, 15);
});
CreateSingleEliminationStage(
@@ -97,9 +97,9 @@ CreateSingleEliminationStage(
settings: { consolationFinal: true, seedOrdering: ["natural"] },
});
assert.equal(storage.select("group")!.length, 2);
assert.equal(storage.select("round")!.length, 4);
assert.equal(storage.select("match")!.length, 8);
assert.equal(storage.select<any>("group")!.length, 2);
assert.equal(storage.select<any>("round")!.length, 4);
assert.equal(storage.select<any>("match")!.length, 8);
}
);
@@ -152,10 +152,10 @@ CreateSingleEliminationStage(
settings: { seedOrdering: ["natural"], matchesChildCount: 3 },
});
assert.equal(storage.select("group")!.length, 1);
assert.equal(storage.select("round")!.length, 3);
assert.equal(storage.select("match")!.length, 7);
assert.equal(storage.select("match_game")!.length, 7 * 3);
assert.equal(storage.select<any>("group")!.length, 1);
assert.equal(storage.select<any>("round")!.length, 3);
assert.equal(storage.select<any>("match")!.length, 7);
assert.equal(storage.select<any>("match_game")!.length, 7 * 3);
}
);
@@ -320,12 +320,6 @@ PreviousAndNextMatchUpdate(
opponent2: { score: 16, result: "win" },
});
manager.update.match({
id: 2, // Final
opponent1: { score: 16, result: "win" },
opponent2: { score: 9 },
});
assert.equal(
storage.select<any>("match", 3).opponent1.id, // Determined opponent for the consolation final
storage.select<any>("match", 0).opponent2.id // Loser of Semi 1
@@ -335,6 +329,70 @@ PreviousAndNextMatchUpdate(
storage.select<any>("match", 3).opponent2.id, // Determined opponent for the consolation final
storage.select<any>("match", 1).opponent1.id // Loser of Semi 2
);
assert.equal(storage.select<any>("match", 2).status, Status.Ready);
assert.equal(storage.select<any>("match", 3).status, Status.Ready);
}
);
PreviousAndNextMatchUpdate(
"should play both the final and consolation final in parallel",
() => {
manager.create({
name: "Example",
tournamentId: 0,
type: "single_elimination",
seeding: ["Team 1", "Team 2", "Team 3", "Team 4"],
settings: { consolationFinal: true },
});
manager.update.match({
id: 0, // First match of round 1
opponent1: { score: 16, result: "win" },
opponent2: { score: 12 },
});
manager.update.match({
id: 1, // Second match of round 1
opponent1: { score: 13 },
opponent2: { score: 16, result: "win" },
});
manager.update.match({
id: 2, // Final
opponent1: { score: 12 },
opponent2: { score: 9 },
});
assert.equal(storage.select<any>("match", 2).status, Status.Running);
assert.equal(storage.select<any>("match", 3).status, Status.Ready);
manager.update.match({
id: 3, // Consolation final
opponent1: { score: 12 },
opponent2: { score: 9 },
});
assert.equal(storage.select<any>("match", 2).status, Status.Running);
assert.equal(storage.select<any>("match", 3).status, Status.Running);
manager.update.match({
id: 3, // Consolation final
opponent1: { score: 16, result: "win" },
opponent2: { score: 9 },
});
assert.equal(storage.select<any>("match", 2).status, Status.Running);
assert.equal(storage.select<any>("match", 3).status, Status.Archived);
manager.update.match({
id: 2, // Final
opponent1: { score: 16, result: "win" },
opponent2: { score: 9 },
});
assert.equal(storage.select<any>("match", 2).status, Status.Archived);
assert.equal(storage.select<any>("match", 3).status, Status.Archived);
}
);
@@ -374,7 +432,8 @@ PreviousAndNextMatchUpdate("should archive previous matches", () => {
opponent2: { score: 9 },
});
assert.equal(storage.select<any>("match", 2).status, Status.Archived);
assert.equal(storage.select<any>("match", 2).status, Status.Archived); // Final
assert.equal(storage.select<any>("match", 3).status, Status.Archived); // Consolation final
});
CreateSingleEliminationStage.run();

View File

@@ -489,7 +489,7 @@ UpdateMatchGames(
});
finalMatchStatus = storage.select<any>("match", 2).status;
assert.equal(finalMatchStatus, Status.Completed);
assert.equal(finalMatchStatus, Status.Archived);
assert.equal(finalMatchStatus, storage.select<any>("match_game", 4).status);
const semi1Status = storage.select<any>("match", 0).status;
@@ -742,7 +742,7 @@ UpdateMatchGames(
manager.update.matchGame({ id: 0, opponent1: { result: "win" } });
manager.update.matchGame({ id: 1, opponent1: { result: "win" } });
assert.equal(storage.select<any>("match", 0).status, Status.Completed);
assert.equal(storage.select<any>("match", 0).status, Status.Archived); // Completed, but single match in the stage.
manager.reset.matchGameResults(0);
assert.equal(storage.select<any>("match", 0).status, Status.Running);