mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-11 05:36:10 -05:00
Fix tournament team page score ordering edge cases
This commit is contained in:
@@ -66,4 +66,23 @@ describe("findByTournamentTeamId", () => {
|
||||
finalMatch.id,
|
||||
]);
|
||||
});
|
||||
|
||||
test("resolves which side of the match the team is on", async () => {
|
||||
const tournament = await TournamentFactory.createPlayed(
|
||||
{ authorId: users.id(1), minMembersPerTeam: 1 },
|
||||
{ teamRosters: [[users.id(1)], [users.id(2)]] },
|
||||
);
|
||||
const match = tournament.matches[0];
|
||||
|
||||
const [winnerSet] = await TournamentMatchRepository.findByTournamentTeamId(
|
||||
match.winnerTeamId,
|
||||
);
|
||||
const [loserSet] = await TournamentMatchRepository.findByTournamentTeamId(
|
||||
match.loserTeamId,
|
||||
);
|
||||
|
||||
expect(winnerSet.teamSide).toBe(winnerSet.winnerSide);
|
||||
expect(loserSet.teamSide).not.toBe(loserSet.winnerSide);
|
||||
expect(winnerSet.teamSide).not.toBe(loserSet.teamSide);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -431,6 +431,10 @@ export function findByTournamentTeamId(tournamentTeamId: number) {
|
||||
)
|
||||
.select(({ eb }) => [
|
||||
"TournamentMatch.id as tournamentMatchId",
|
||||
"TournamentMatch.winnerSide",
|
||||
sql<Side>`iif(${opponentOneId} = ${tournamentTeamId}, 'opponent1', 'opponent2')`.as(
|
||||
"teamSide",
|
||||
),
|
||||
opponentOneScore.as("opponentOneScore"),
|
||||
opponentTwoScore.as("opponentTwoScore"),
|
||||
"otherTeam.name as otherTeamName",
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { winCounts } from "./sets.server";
|
||||
import type { FindByTournamentTeamIdItem } from "~/features/tournament-match/TournamentMatchRepository.server";
|
||||
import {
|
||||
type AllRoundsItem,
|
||||
tournamentTeamSets,
|
||||
winCounts,
|
||||
} from "./sets.server";
|
||||
|
||||
describe("winCounts", () => {
|
||||
test("returns 0% (not NaN) when there are no played sets", () => {
|
||||
@@ -8,4 +13,124 @@ describe("winCounts", () => {
|
||||
expect(result.sets.percentage).toBe(0);
|
||||
expect(result.maps.percentage).toBe(0);
|
||||
});
|
||||
|
||||
test("counts a set the team won on the bracket but lost on maps as a win", () => {
|
||||
// e.g. a set the opponent forfeited after winning games, awarded 2-1 by the
|
||||
// organizer — the bracket, and so the set score, says the team won it
|
||||
const result = winCounts([
|
||||
{
|
||||
tournamentMatchId: 1,
|
||||
score: [2, 1],
|
||||
result: "win",
|
||||
round: { type: "winners", round: 1 },
|
||||
stageName: "Main bracket",
|
||||
maps: [
|
||||
{ stageId: 1, modeShort: "SZ", result: "loss", source: "BOTH" },
|
||||
{ stageId: 2, modeShort: "TC", result: "loss", source: "BOTH" },
|
||||
{ stageId: 3, modeShort: "RM", result: "win", source: "BOTH" },
|
||||
],
|
||||
opponent: { id: 2, name: "Opponent", roster: [] },
|
||||
},
|
||||
]);
|
||||
|
||||
expect(result.sets.won).toBe(1);
|
||||
});
|
||||
|
||||
test("counts a set that ended early with the maps split as a win", () => {
|
||||
const result = winCounts([
|
||||
{
|
||||
tournamentMatchId: 1,
|
||||
score: [1, 1],
|
||||
result: "win",
|
||||
round: { type: "winners", round: 1 },
|
||||
stageName: "Main bracket",
|
||||
maps: [
|
||||
{ stageId: 1, modeShort: "SZ", result: "win", source: "BOTH" },
|
||||
{ stageId: 2, modeShort: "TC", result: "loss", source: "BOTH" },
|
||||
],
|
||||
opponent: { id: 2, name: "Opponent", roster: [] },
|
||||
},
|
||||
]);
|
||||
|
||||
expect(result.sets.won).toBe(1);
|
||||
});
|
||||
|
||||
test("counts a set the team lost on the bracket but won on maps as a loss", () => {
|
||||
const result = winCounts([
|
||||
{
|
||||
tournamentMatchId: 1,
|
||||
score: [2, 1],
|
||||
result: "loss",
|
||||
round: { type: "winners", round: 1 },
|
||||
stageName: "Main bracket",
|
||||
maps: [
|
||||
{ stageId: 1, modeShort: "SZ", result: "win", source: "BOTH" },
|
||||
{ stageId: 2, modeShort: "TC", result: "loss", source: "BOTH" },
|
||||
{ stageId: 3, modeShort: "RM", result: "win", source: "BOTH" },
|
||||
],
|
||||
opponent: { id: 2, name: "Opponent", roster: [] },
|
||||
},
|
||||
]);
|
||||
|
||||
expect(result.sets.won).toBe(0);
|
||||
expect(result.maps.won).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
const ALL_ROUNDS: AllRoundsItem[] = [
|
||||
{
|
||||
stageId: 1,
|
||||
stageName: "Main bracket",
|
||||
stageType: "single_elimination",
|
||||
roundNumber: 1,
|
||||
groupNumber: 1,
|
||||
},
|
||||
];
|
||||
|
||||
/** A played Bo3 the team being viewed lost 1-2 on the maps. */
|
||||
function playedSetRow(
|
||||
overrides: Partial<FindByTournamentTeamIdItem>,
|
||||
): FindByTournamentTeamIdItem {
|
||||
return {
|
||||
tournamentMatchId: 1,
|
||||
winnerSide: "opponent1",
|
||||
teamSide: "opponent1",
|
||||
opponentOneScore: 1,
|
||||
opponentTwoScore: 2,
|
||||
otherTeamId: 2,
|
||||
otherTeamName: "Opponent",
|
||||
roundNumber: 1,
|
||||
stageId: 1,
|
||||
groupNumber: 1,
|
||||
matches: [
|
||||
{ mode: "SZ", stageId: 1, source: "BOTH", wasWinner: 1 },
|
||||
{ mode: "TC", stageId: 2, source: "BOTH", wasWinner: 0 },
|
||||
{ mode: "RM", stageId: 3, source: "BOTH", wasWinner: 0 },
|
||||
],
|
||||
players: [],
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("tournamentTeamSets", () => {
|
||||
test("orders the score by the slot the team being viewed is in", () => {
|
||||
const [set] = tournamentTeamSets({
|
||||
sets: [playedSetRow({ teamSide: "opponent2" })],
|
||||
allRounds: ALL_ROUNDS,
|
||||
});
|
||||
|
||||
expect(set.score).toEqual([2, 1]);
|
||||
});
|
||||
|
||||
test("takes the set result from the bracket winner even when the maps disagree", () => {
|
||||
// organizer overrode the winner after the games were reported, so the team
|
||||
// won the set on the bracket while losing 1-2 on the maps
|
||||
const [set] = tournamentTeamSets({
|
||||
sets: [playedSetRow({ teamSide: "opponent1", winnerSide: "opponent1" })],
|
||||
allRounds: ALL_ROUNDS,
|
||||
});
|
||||
|
||||
expect(set.result).toBe("win");
|
||||
expect(set.score).toEqual([1, 2]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,12 @@ export interface AllRoundsItem {
|
||||
export interface PlayedSet {
|
||||
tournamentMatchId: number;
|
||||
score: [teamBeingViewed: number, opponent: number];
|
||||
/**
|
||||
* Who won the set according to the bracket. Can disagree with the maps and
|
||||
* the score e.g. when an organizer overrode the winner after games were
|
||||
* already reported.
|
||||
*/
|
||||
result: "win" | "loss";
|
||||
round: {
|
||||
type: "winners" | "losers" | "single_elim" | "round_robin" | "swiss";
|
||||
round: number | "finals" | "grand_finals" | "bracket_reset";
|
||||
@@ -63,7 +69,7 @@ export function winCounts(sets: PlayedSet[]) {
|
||||
}
|
||||
|
||||
totalSets++;
|
||||
if (mapsWonThisSet > totalMapsThisSet / 2) {
|
||||
if (set.result === "win") {
|
||||
setsWon++;
|
||||
}
|
||||
|
||||
@@ -140,7 +146,8 @@ export function tournamentTeamSets({
|
||||
result: match.wasWinner ? "win" : "loss",
|
||||
source: parseMaplistSource(match.source),
|
||||
})),
|
||||
score: flipScoreIfNeeded(set),
|
||||
result: set.winnerSide === set.teamSide ? "win" : "loss",
|
||||
score: scoreFromTeamPerspective(set),
|
||||
opponent: {
|
||||
id: set.otherTeamId,
|
||||
name: set.otherTeamName,
|
||||
@@ -150,24 +157,12 @@ export function tournamentTeamSets({
|
||||
});
|
||||
}
|
||||
|
||||
function flipScoreIfNeeded(set: FindByTournamentTeamIdItem): [number, number] {
|
||||
const score: [number, number] = [
|
||||
set.opponentOneScore ?? 0,
|
||||
set.opponentTwoScore ?? 0,
|
||||
];
|
||||
|
||||
const wonTheSet =
|
||||
set.matches.reduce((acc, cur) => cur.wasWinner + acc, 0) >
|
||||
set.matches.length / 2;
|
||||
|
||||
if (
|
||||
(wonTheSet && score[0] < score[1]) ||
|
||||
(!wonTheSet && score[0] > score[1])
|
||||
) {
|
||||
return [score[1], score[0]];
|
||||
}
|
||||
|
||||
return score;
|
||||
function scoreFromTeamPerspective(
|
||||
set: FindByTournamentTeamIdItem,
|
||||
): [number, number] {
|
||||
return set.teamSide === "opponent1"
|
||||
? [set.opponentOneScore ?? 0, set.opponentTwoScore ?? 0]
|
||||
: [set.opponentTwoScore ?? 0, set.opponentOneScore ?? 0];
|
||||
}
|
||||
|
||||
function resolveRoundType({
|
||||
|
||||
Reference in New Issue
Block a user