Fix Swiss not counting every meeting between tied teams for the head-to-head tiebreaker

This commit is contained in:
Kalle
2026-08-04 21:46:04 +03:00
parent e422ceb422
commit d0654a3201
2 changed files with 93 additions and 13 deletions

View File

@@ -215,6 +215,87 @@ describe("swiss standings - cross group ties", () => {
});
});
describe("swiss standings - rematches between tied teams", () => {
// 4-team Swiss with 5 rounds, so rounds 4 and 5 are forced rematches. Teams 1
// and 2 finish 4-1, having met twice and split 1-1 (team 1 won the round 1
// meeting, team 2 the round 4 one), so head-to-head is even and opponent set
// win % should decide: team 2's opponents won 12 of 25 sets (48%), team 1's
// 10 of 25 (40%).
const swissTournamentWithRematches = () => {
const data = Engine.create({
type: "swiss",
seeding: [1, 2, 3, 4],
settings: { groupCount: 1, roundCount: 5 },
});
const roundResults: Array<Array<[winnerId: number, loserId: number]>> = [
[
[1, 2],
[3, 4],
],
[
[1, 3],
[2, 4],
],
[
[1, 4],
[2, 3],
],
[
[2, 1],
[3, 4],
],
[
[1, 4],
[2, 3],
],
];
data.match = roundResults.flatMap((results, roundIdx) =>
results.map(
([winnerId, loserId], matchIdx): MatchData => ({
id: roundIdx * 2 + matchIdx,
stageId: data.stage[0].id,
groupId: data.group[0].id,
roundId: data.round[roundIdx].id,
number: matchIdx + 1,
opponent1: { id: winnerId, score: 2 },
opponent2: { id: loserId, score: 0 },
winnerSide: "opponent1",
}),
),
);
return testTournament({
ctx: {
settings: {
bracketProgression: [
{
type: "swiss",
name: "Swiss",
requiresCheckIn: false,
settings: { groupCount: 1, roundCount: 5 },
sources: [],
},
],
},
},
data,
});
};
it("counts every meeting between tied teams for the head-to-head tiebreaker", () => {
const standings = swissTournamentWithRematches().bracketByIdx(0)!.standings;
const team1 = standings.find((s) => s.team.id === 1)!;
const team2 = standings.find((s) => s.team.id === 2)!;
expect(team2.placement).toBe(1);
expect(team1.stats?.lossesAgainstTied).toBe(1);
expect(team2.stats?.winsAgainstTied).toBe(1);
});
});
describe("round robin standings", () => {
it("should sort teams primarily by set wins (per group) in paddling pool 255", () => {
const tournamentPP255 = new Tournament(PADDLING_POOL_255());

View File

@@ -330,7 +330,7 @@ export class SwissBracket extends Bracket {
// they are different teams and are tied, let's check who won
const finishedMatchBetweenTeams = matches.find((match) => {
const finishedMatchesBetweenTeams = matches.filter((match) => {
const isBetweenTeams =
(match.opponent1?.id === team.id &&
match.opponent2?.id === team2.id) ||
@@ -342,19 +342,18 @@ export class SwissBracket extends Bracket {
return isBetweenTeams && isFinished;
});
// they did not play each other
if (!finishedMatchBetweenTeams) continue;
for (const finishedMatchBetweenTeams of finishedMatchesBetweenTeams) {
const wonTheirMatch =
(finishedMatchBetweenTeams.opponent1!.id === team.id &&
finishedMatchBetweenTeams.winnerSide === "opponent1") ||
(finishedMatchBetweenTeams.opponent2!.id === team.id &&
finishedMatchBetweenTeams.winnerSide === "opponent2");
const wonTheirMatch =
(finishedMatchBetweenTeams.opponent1!.id === team.id &&
finishedMatchBetweenTeams.winnerSide === "opponent1") ||
(finishedMatchBetweenTeams.opponent2!.id === team.id &&
finishedMatchBetweenTeams.winnerSide === "opponent2");
if (wonTheirMatch) {
team.winsAgainstTied++;
} else {
team.lossesAgainstTied++;
if (wonTheirMatch) {
team.winsAgainstTied++;
} else {
team.lossesAgainstTied++;
}
}
}
}