mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-24 12:11:56 -05:00
Make seed padding implementation detail
This commit is contained in:
parent
d4d01d9bc3
commit
3e87dc0adc
|
|
@ -9,7 +9,6 @@ import type { Tables } from "~/db/tables";
|
|||
import type { Bracket as BracketType } from "~/features/tournament-bracket/core/Bracket";
|
||||
import * as Engine from "~/features/tournament-bracket/core/engine";
|
||||
import type { TournamentManagerDataSet } from "~/features/tournament-bracket/core/engine/types";
|
||||
import { fillWithNullTillPowerOfTwo } from "~/features/tournament-bracket/tournament-bracket-utils";
|
||||
import styles from "../bracket-test.module.css";
|
||||
|
||||
type FormatType = Tables["TournamentStage"]["type"];
|
||||
|
|
@ -292,9 +291,6 @@ function generateBracketData(
|
|||
});
|
||||
}
|
||||
|
||||
const seeding =
|
||||
format === "round_robin" ? teamIds : fillWithNullTillPowerOfTwo(teamIds);
|
||||
|
||||
const settings =
|
||||
format === "single_elimination"
|
||||
? { consolationFinal: false }
|
||||
|
|
@ -309,7 +305,7 @@ function generateBracketData(
|
|||
tournamentId: 1,
|
||||
name: "Test Bracket",
|
||||
type: format,
|
||||
seeding,
|
||||
seeding: teamIds,
|
||||
settings,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,7 @@ import {
|
|||
tournamentFromDB,
|
||||
} from "../core/Tournament.server";
|
||||
import { bracketSchema } from "../tournament-bracket-schemas.server";
|
||||
import {
|
||||
fillWithNullTillPowerOfTwo,
|
||||
tournamentWebsocketRoom,
|
||||
} from "../tournament-bracket-utils";
|
||||
import { tournamentWebsocketRoom } from "../tournament-bracket-utils";
|
||||
|
||||
export const action: ActionFunction = async ({ params, request }) => {
|
||||
const user = requireUser();
|
||||
|
|
@ -109,11 +106,7 @@ export const action: ActionFunction = async ({ params, request }) => {
|
|||
// xxx: will we really need name?
|
||||
name: bracket.name,
|
||||
type: bracket.type,
|
||||
// xxx: this could be implementation detail
|
||||
seeding:
|
||||
bracket.type === "round_robin" || bracket.type === "swiss"
|
||||
? seeding
|
||||
: fillWithNullTillPowerOfTwo(seeding),
|
||||
seeding,
|
||||
settings,
|
||||
abDivisions,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import type {
|
|||
} from "~/features/tournament-bracket/core/engine/types";
|
||||
import invariant from "~/utils/invariant";
|
||||
import { logger } from "~/utils/logger";
|
||||
import { fillWithNullTillPowerOfTwo } from "../../tournament-bracket-utils";
|
||||
import * as AbDivisions from "../AbDivisions";
|
||||
import * as Engine from "../engine";
|
||||
import * as Progression from "../Progression";
|
||||
|
|
@ -308,10 +307,7 @@ export abstract class Bracket {
|
|||
tournamentId: virtualTournamentId,
|
||||
name: "Virtual",
|
||||
type: this.type,
|
||||
seeding:
|
||||
this.type === "round_robin"
|
||||
? teams
|
||||
: fillWithNullTillPowerOfTwo(teams),
|
||||
seeding: teams,
|
||||
settings: abDivisions
|
||||
? settings
|
||||
: {
|
||||
|
|
|
|||
|
|
@ -29,10 +29,7 @@ import {
|
|||
import invariant from "~/utils/invariant";
|
||||
import { logger } from "~/utils/logger";
|
||||
import { assertUnreachable } from "~/utils/types";
|
||||
import {
|
||||
fillWithNullTillPowerOfTwo,
|
||||
groupNumberToLetters,
|
||||
} from "../tournament-bracket-utils";
|
||||
import { groupNumberToLetters } from "../tournament-bracket-utils";
|
||||
import { type Bracket, createBracket } from "./Bracket";
|
||||
import * as Engine from "./engine";
|
||||
import { getRounds } from "./rounds";
|
||||
|
|
@ -396,7 +393,7 @@ export class Tournament {
|
|||
TournamentStage["type"],
|
||||
"round_robin" | "swiss"
|
||||
>,
|
||||
seeding: fillWithNullTillPowerOfTwo(candidateTeams),
|
||||
seeding: candidateTeams,
|
||||
settings: this.bracketManagerSettings(
|
||||
settings,
|
||||
bracket.type,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,12 @@ import type {
|
|||
StandardBracketResults,
|
||||
} from "../types";
|
||||
import { MatchStatus } from "../types";
|
||||
import { balanceByes, defaultMinorOrdering, ordering } from "./seeding";
|
||||
import {
|
||||
balanceByes,
|
||||
defaultMinorOrdering,
|
||||
ordering,
|
||||
padSeedingToPowerOfTwo,
|
||||
} from "./seeding";
|
||||
|
||||
/**
|
||||
* Accumulates the rows of a stage being created. Pure port of the old
|
||||
|
|
@ -32,7 +37,11 @@ export class StageCreator {
|
|||
constructor(input: CreateBracketInput) {
|
||||
this.input = input;
|
||||
this.settings = structuredClone(input.settings) ?? {};
|
||||
this.seeding = input.seeding ? [...input.seeding] : undefined;
|
||||
const seeding = input.seeding ? [...input.seeding] : undefined;
|
||||
this.seeding =
|
||||
seeding && input.type !== "round_robin"
|
||||
? padSeedingToPowerOfTwo(seeding)
|
||||
: seeding;
|
||||
this.seedOrdering = this.settings.seedOrdering || [];
|
||||
this.data = { stage: [], group: [], round: [], match: [] };
|
||||
|
||||
|
|
|
|||
|
|
@ -175,6 +175,15 @@ export function balanceByes(
|
|||
return setArraySize(flat, size, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads the seeding with BYEs (`null`) until its length is a power of two.
|
||||
*
|
||||
* @param seeding The seeding of the stage.
|
||||
*/
|
||||
export function padSeedingToPowerOfTwo(seeding: Seeding): Seeding {
|
||||
return setArraySize(seeding, getNearestPowerOfTwo(seeding.length), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of an array with a placeholder if the size is bigger.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -141,18 +141,20 @@ describe("Special cases", () => {
|
|||
).toThrow("You must provide a tournament id for the stage.");
|
||||
});
|
||||
|
||||
test("should throw if the participant count of a stage is not a power of two", () => {
|
||||
expect(() =>
|
||||
bracket.create({
|
||||
name: "Example",
|
||||
tournamentId: 0,
|
||||
type: "single_elimination",
|
||||
seeding: [1, 2, 3, 4, 5, 6, 7],
|
||||
}),
|
||||
).toThrow(
|
||||
"The library only supports a participant count which is a power of two.",
|
||||
);
|
||||
test("should pad the seeding with BYEs to the next power of two", () => {
|
||||
bracket.create({
|
||||
name: "Example",
|
||||
tournamentId: 0,
|
||||
type: "single_elimination",
|
||||
seeding: [1, 2, 3, 4, 5, 6, 7],
|
||||
settings: { seedOrdering: ["natural"] },
|
||||
});
|
||||
|
||||
expect(bracket.match(3).opponent1?.id).toBe(7);
|
||||
expect(bracket.match(3).opponent2).toBe(null);
|
||||
});
|
||||
|
||||
test("should throw if the size of a stage is not a power of two", () => {
|
||||
expect(() =>
|
||||
bracket.create({
|
||||
name: "Example",
|
||||
|
|
|
|||
|
|
@ -1,34 +1,9 @@
|
|||
import { describe, expect, test } from "vitest";
|
||||
import {
|
||||
fillWithNullTillPowerOfTwo,
|
||||
groupNumberToLetters,
|
||||
validateBadgeReceivers,
|
||||
} from "./tournament-bracket-utils";
|
||||
|
||||
const powerOfTwoParamsToResults: [
|
||||
amountOfTeams: number,
|
||||
expectedNullCount: number,
|
||||
][] = [
|
||||
[32, 0],
|
||||
[16, 0],
|
||||
[8, 0],
|
||||
[31, 1],
|
||||
[0, 0],
|
||||
[17, 15],
|
||||
];
|
||||
|
||||
describe("fillWithNullTillPowerOfTwo()", () => {
|
||||
for (const [amountOfTeams, expectedNullCount] of powerOfTwoParamsToResults) {
|
||||
test(`amountOfTeams=${amountOfTeams} -> ${expectedNullCount}`, () => {
|
||||
expect(
|
||||
fillWithNullTillPowerOfTwo(Array(amountOfTeams).fill("team")).filter(
|
||||
(x) => x === null,
|
||||
).length,
|
||||
).toBe(expectedNullCount);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const groupNumberToLettersParamsToResult = [
|
||||
{ groupNumber: 1, expected: "A" },
|
||||
{ groupNumber: 26, expected: "Z" },
|
||||
|
|
|
|||
|
|
@ -5,13 +5,6 @@ import type { Standing } from "./core/Bracket";
|
|||
export const tournamentWebsocketRoom = (tournamentId: number) =>
|
||||
`tournament__${tournamentId}`;
|
||||
|
||||
export function fillWithNullTillPowerOfTwo<T>(arr: T[]) {
|
||||
const nextPowerOfTwo = 2 ** Math.ceil(Math.log2(arr.length));
|
||||
const nullsToAdd = nextPowerOfTwo - arr.length;
|
||||
|
||||
return [...arr, ...new Array(nullsToAdd).fill(null)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a group number to its corresponding letter representation.
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user