Take ownership of brackets-model

This commit is contained in:
Kalle 2023-05-20 12:04:58 +03:00
parent 0a346175b6
commit 0e28189336
22 changed files with 392 additions and 32 deletions

View File

@ -6,7 +6,7 @@ import type {
Group as GroupType,
Round as RoundType,
Match as MatchType,
} from "brackets-model";
} from "~/modules/brackets-model";
import { sql } from "~/db/sql";
import type {
Tournament,

View File

@ -6,5 +6,4 @@ const globalForEmitter = global as unknown as {
export const emitter = globalForEmitter.emitter ?? new EventEmitter();
// xxx: test behavior when deployed, do we need if (process.env.NODE_ENV !== 'production') ?
globalForEmitter.emitter = emitter;

View File

@ -1,4 +1,4 @@
import type { Match } from "brackets-model";
import type { Match } from "~/modules/brackets-model";
import { sql } from "~/db/sql";
import type { TournamentMatch } from "~/db/types";

View File

@ -1,4 +1,4 @@
import type { Stage } from "brackets-model";
import type { Stage } from "~/modules/brackets-model";
import type {
TournamentFormat,
TournamentMatch,
@ -86,7 +86,7 @@ export function resolveTournamentStageSettings(
case "DE":
return {
grandFinal: "double",
seedOrdering: ["space_between"] as any,
seedOrdering: ["space_between"],
};
default: {
assertUnreachable(format);

View File

@ -8,7 +8,7 @@ import type {
Stage,
StageType,
GroupType,
} from "brackets-model";
} from "~/modules/brackets-model";
import type { RoundPositionalInfo } from "../types";
import type { Create } from "../create";
import * as helpers from "../helpers";

View File

@ -4,8 +4,8 @@ import type {
Seeding,
Stage,
GroupType,
} from "brackets-model";
import { Status } from "brackets-model";
} 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";

View File

@ -8,7 +8,7 @@ import type {
Seeding,
SeedOrdering,
Stage,
} from "brackets-model";
} from "~/modules/brackets-model";
import { defaultMinorOrdering, ordering } from "./ordering";
import type {
Duel,

View File

@ -1,4 +1,4 @@
import type { Group, Match, MatchGame } from "brackets-model";
import type { Group, Match, MatchGame } from "~/modules/brackets-model";
import { BaseGetter } from "./base/getter";
import * as helpers from "./helpers";

View File

@ -5,8 +5,8 @@ import type {
Match,
MatchGame,
Participant,
} from "brackets-model";
import { Status } from "brackets-model";
} from "~/modules/brackets-model";
import { Status } from "~/modules/brackets-model";
import type { Database, FinalStandingsItem, ParticipantSlot } from "./types";
import { BaseGetter } from "./base/getter";
import * as helpers from "./helpers";

View File

@ -13,8 +13,8 @@ import type {
Stage,
StageType,
GroupType,
} from "brackets-model";
import { Status } from "brackets-model";
} from "~/modules/brackets-model";
import { Status } from "~/modules/brackets-model";
import type {
Database,

View File

@ -5,7 +5,7 @@ import type {
Storage,
Table,
} from "./types";
import type { InputStage, Stage } from "brackets-model";
import type { InputStage, Stage } from "~/modules/brackets-model";
import { create } from "./create";
import { Get } from "./get";
import { Update } from "./update";

View File

@ -1,6 +1,6 @@
// https://web.archive.org/web/20200601102344/https://tl.net/forum/sc2-tournaments/202139-superior-double-elimination-losers-bracket-seeding
import type { SeedOrdering } from "brackets-model";
import type { SeedOrdering } from "~/modules/brackets-model";
import type { OrderingMap } from "./types";
import invariant from "tiny-invariant";

View File

@ -1,4 +1,4 @@
import { Status } from "brackets-model";
import { Status } from "~/modules/brackets-model";
import { BaseUpdater } from "./base/updater";
import * as helpers from "./helpers";

View File

@ -6,7 +6,7 @@ import type {
Round,
SeedOrdering,
Stage,
} from "brackets-model";
} from "~/modules/brackets-model";
/**
* Type of an object implementing every ordering method.

View File

@ -4,8 +4,8 @@ import type {
Round,
Seeding,
SeedOrdering,
} from "brackets-model";
import { Status } from "brackets-model";
} from "~/modules/brackets-model";
import { Status } from "~/modules/brackets-model";
import { ordering } from "./ordering";
import { BaseUpdater } from "./base/updater";
import type { ChildCountLevel, DeepPartial } from "./types";

View File

@ -0,0 +1,4 @@
export * from "./unions";
export * from "./input";
export * from "./storage";
export * from "./other";

View File

@ -0,0 +1,131 @@
/*------------------------------------------------------------|
* Contains everything which is given by the user as input.
*-----------------------------------------------------------*/
import type { Participant } from "./storage";
import type {
GrandFinalType,
RoundRobinMode,
SeedOrdering,
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.
*
* Each element represents a participant, which can be:
* - A full object, with possibly extra fields.
* - Its name.
* - Its ID.
* - Or a BYE: `null`.
*/
export type Seeding = (CustomParticipant | string | number | null)[];
/**
* Used to create a stage.
*/
export interface InputStage {
/**
* ID of the parent tournament.
*
* Used to determine the `number` property of a stage related to a tournament.
*/
tournamentId: number;
/** Name of the stage. */
name: string;
/** Type of stage. */
type: StageType;
/** The number of the stage in its tournament. Is determined if not given. */
number?: number;
/** Contains participants or `null` for BYEs. */
seeding?: Seeding;
/** Contains optional settings specific to each stage type. */
settings?: StageSettings;
}
/**
* The possible settings for a stage.
*/
export interface StageSettings {
/**
* The number of participants.
*/
size?: number;
/**
* A list of ordering methods to apply to the seeding.
*
* - For a round-robin stage: 1 item required (**with** `"groups."` prefix).
* - Used to distribute in groups.
* - For a simple elimination stage, 1 item required (**without** `"groups."` prefix).
* - Used to distribute in round 1.
* - For a double elimination stage, 1 item required, 3+ items supported (**without** `"groups."` prefix).
* - Item 1 (required) - Used to distribute in WB round 1.
* - Item 2 - Used to distribute WB losers in LB round 1.
* - Items 3+ - Used to distribute WB losers in LB minor rounds (1 per round).
*/
seedOrdering?: SeedOrdering[];
/**
* Whether to balance BYEs in the seeding of an elimination stage.
*
* This prevents having BYE against BYE in matches.
*/
balanceByes?: boolean;
/**
* All matches of the stage will have this child count. This can later be overridden for certain groups, rounds or matches.
*/
matchesChildCount?: number;
/**
* Number of groups in a round-robin stage.
*/
groupCount?: number;
/**
* The mode for the round-robin stage.
*
* - If `simple`, each participant plays each opponent once.
* - If `double`, each participant plays each opponent twice, once at home and once away.
*/
roundRobinMode?: RoundRobinMode;
/**
* A list of seeds per group for a round-robin stage to be manually ordered.
*
* Seed ordering is ignored if this property is given.
*/
manualOrdering?: number[][];
/**
* Optional final between semi-final losers.
*/
consolationFinal?: boolean;
/**
* Whether to skip the first round of the WB of a double elimination stage.
*/
skipFirstRound?: boolean;
/**
* Optional grand final between WB and LB winners.
*
* - If `none`, there is no grand final.
* - If `simple`, the final is a single match. The winner is the winner of the stage.
* - If `double`, if the WB winner wins, he's the winner of the stage. But if he loses, the final is reset and there is a very last match.
* It might be fairer since it gives the WB winner the right to lose once during the stage...
*/
grandFinal?: GrandFinalType;
}

View File

@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------|
* Contains the rest of the types which doesn't belong to the other files.
*--------------------------------------------------------------------------*/
import type { Result } from "./unions";
/**
* The possible status for a match.
*/
export enum Status {
/** The two matches leading to this one are not completed yet. */
Locked = 0,
/** One participant is ready and waiting for the other one. */
Waiting = 1,
/** Both participants are ready to start. */
Ready = 2,
/** The match is running. */
Running = 3,
/** The match is completed. */
Completed = 4,
/** At least one participant completed his following match. */
Archived = 5,
}
/**
* The results of a participant in a match.
*/
export interface ParticipantResult {
/** If `null`, the participant is to be determined. */
id: number | null;
/** Indicates where the participant comes from. */
position?: number;
/** If this participant forfeits, the other automatically wins. */
forfeit?: boolean;
/** The current score of the participant. */
score?: number;
/** Tells what is the result of a duel for this participant. */
result?: Result;
}
/**
* Only contains information about match status and results.
*/
export interface MatchResults {
/** Status of the match. */
status: Status;
/** First opponent of the match. */
opponent1: ParticipantResult | null;
/** Second opponent of the match. */
opponent2: ParticipantResult | null;
}

View File

@ -0,0 +1,117 @@
/*-----------------------------------------------------------------|
* Contains the types which are persisted in the chosen storage.
*----------------------------------------------------------------*/
import type { StageSettings } from "./input";
import type { MatchResults } from "./other";
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.
*/
export interface Stage {
/** ID of the stage. */
id: number;
/** ID of the tournament this stage belongs to. */
tournament_id: number;
/** Name of the stage. */
name: string;
/** Type of the stage. */
type: StageType;
/** Settings of the stage. */
settings: StageSettings;
/** The number of the stage in its tournament. */
number: number;
}
/**
* A group of a stage.
*/
export interface Group {
/** ID of the group. */
id: number;
/** ID of the parent stage. */
stage_id: number;
/** The number of the group in its stage. */
number: number;
}
// The next levels don't have a `name` property. They are automatically named with their `number` and their context (parent levels).
/**
* A round of a group.
*/
export interface Round {
/** ID of the round. */
id: number;
/** ID of the parent stage. */
stage_id: number;
/** ID of the parent group. */
group_id: number;
/** The number of the round in its group. */
number: number;
}
/**
* A match of a round.
*/
export interface Match extends MatchResults {
/** ID of the match. */
id: number;
/** ID of the parent stage. */
stage_id: number;
/** ID of the parent group. */
group_id: number;
/** ID of the parent round. */
round_id: number;
/** The number of the match in its round. */
number: number;
/** The count of match games this match has. Can be `0` if it's a simple match, or a positive number for "Best Of" matches. */
child_count: number;
}
/**
* A game of a match.
*/
export interface MatchGame extends MatchResults {
/** ID of the match game. */
id: number;
/** ID of the parent stage. */
stage_id: number;
/** ID of the parent match. */
parent_id: number;
/** The number of the match game in its parent match. */
number: number;
}

View File

@ -0,0 +1,59 @@
/*----------------------------------------|
* Contains all the string union types.
*---------------------------------------*/
/**
* The only supported types of stage.
*/
export type StageType =
| "round_robin"
| "single_elimination"
| "double_elimination";
/**
* All the possible types of group in an elimination stage.
*
* - `single_bracket` for single elimination.
* - `winner_bracket` and `loser_bracket` for double elimination.
* - `final_group` for both single and double elimination.
*/
export type GroupType =
| "single_bracket"
| "winner_bracket"
| "loser_bracket"
| "final_group";
/**
* The possible types for a double elimination stage's grand final.
*/
export type GrandFinalType = "none" | "simple" | "double";
/**
* The possible types of final for an elimination stage.
*/
export type FinalType = "consolation_final" | "grand_final";
/**
* The possible modes for a round-robin stage.
*/
export type RoundRobinMode = "simple" | "double";
/**
* Used to order seeds.
*/
export type SeedOrdering =
| "natural"
| "reverse"
| "half_shift"
| "reverse_half_shift"
| "pair_flip"
| "inner_outer"
| "space_between"
| "groups.effort_balanced"
| "groups.seed_optimized"
| "groups.bracket_optimized";
/**
* The possible results of a duel for a participant.
*/
export type Result = "win" | "draw" | "loss";

11
package-lock.json generated
View File

@ -20,7 +20,6 @@
"@tldraw/tldraw": "^1.29.2",
"aws-sdk": "^2.1354.0",
"better-sqlite3": "^8.3.0",
"brackets-model": "^1.4.0",
"cachified": "^3.1.0",
"clsx": "^1.2.1",
"compressorjs": "^1.2.1",
@ -5732,11 +5731,6 @@
"node": ">=8"
}
},
"node_modules/brackets-model": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/brackets-model/-/brackets-model-1.4.0.tgz",
"integrity": "sha512-bDsM2VTLkhkCZPis7vXNhVB96gYmjO5P7qJg81+7DF0c2OygwqLm5XSeogV+w2i2rVFFjLoUCQEkBI2lr7qZhA=="
},
"node_modules/browser-fs-access": {
"version": "0.31.1",
"license": "Apache-2.0"
@ -19937,11 +19931,6 @@
"fill-range": "^7.0.1"
}
},
"brackets-model": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/brackets-model/-/brackets-model-1.4.0.tgz",
"integrity": "sha512-bDsM2VTLkhkCZPis7vXNhVB96gYmjO5P7qJg81+7DF0c2OygwqLm5XSeogV+w2i2rVFFjLoUCQEkBI2lr7qZhA=="
},
"browser-fs-access": {
"version": "0.31.1"
},

View File

@ -49,7 +49,6 @@
"@tldraw/tldraw": "^1.29.2",
"aws-sdk": "^2.1354.0",
"better-sqlite3": "^8.3.0",
"brackets-model": "^1.4.0",
"cachified": "^3.1.0",
"clsx": "^1.2.1",
"compressorjs": "^1.2.1",