440 lines
12 KiB
TypeScript
440 lines
12 KiB
TypeScript
import { PointsTable } from '@core/racing/domain/value-objects/PointsTable';
|
|
import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
|
|
import type { SessionType } from '@core/racing/domain/types/SessionType';
|
|
import type { BonusRule } from '@core/racing/domain/types/BonusRule';
|
|
import type { DropScorePolicy } from '@core/racing/domain/types/DropScorePolicy';
|
|
import type { ChampionshipType } from '@core/racing/domain/types/ChampionshipType';
|
|
import { LeagueScoringConfig } from '@core/racing/domain/entities/LeagueScoringConfig';
|
|
|
|
export type LeagueScoringPresetPrimaryChampionshipType =
|
|
| 'driver'
|
|
| 'team'
|
|
| 'nations'
|
|
| 'trophy';
|
|
|
|
export interface LeagueScoringPresetTimingDefaults {
|
|
practiceMinutes: number;
|
|
qualifyingMinutes: number;
|
|
sprintRaceMinutes: number;
|
|
mainRaceMinutes: number;
|
|
sessionCount: number;
|
|
}
|
|
|
|
export interface LeagueScoringPreset {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
primaryChampionshipType: LeagueScoringPresetPrimaryChampionshipType;
|
|
dropPolicySummary: string;
|
|
sessionSummary: string;
|
|
bonusSummary: string;
|
|
defaultTimings: LeagueScoringPresetTimingDefaults;
|
|
createConfig: (options: { seasonId: string }) => LeagueScoringConfig;
|
|
}
|
|
|
|
const mainPointsSprintMain = new PointsTable({
|
|
1: 25,
|
|
2: 18,
|
|
3: 15,
|
|
4: 12,
|
|
5: 10,
|
|
6: 8,
|
|
7: 6,
|
|
8: 4,
|
|
9: 2,
|
|
10: 1,
|
|
});
|
|
|
|
const sprintPointsSprintMain = new PointsTable({
|
|
1: 8,
|
|
2: 7,
|
|
3: 6,
|
|
4: 5,
|
|
5: 4,
|
|
6: 3,
|
|
7: 2,
|
|
8: 1,
|
|
});
|
|
|
|
const clubMainPoints = new PointsTable({
|
|
1: 20,
|
|
2: 15,
|
|
3: 12,
|
|
4: 10,
|
|
5: 8,
|
|
6: 6,
|
|
7: 4,
|
|
8: 2,
|
|
9: 1,
|
|
});
|
|
|
|
const enduranceMainPoints = new PointsTable({
|
|
1: 50,
|
|
2: 36,
|
|
3: 30,
|
|
4: 24,
|
|
5: 20,
|
|
6: 16,
|
|
7: 12,
|
|
8: 8,
|
|
9: 4,
|
|
10: 2,
|
|
});
|
|
|
|
export const leagueScoringPresets: LeagueScoringPreset[] = [
|
|
{
|
|
id: 'sprint-main-driver',
|
|
name: 'Sprint + Main',
|
|
description:
|
|
'Short sprint race plus main race; sprint gives fewer points.',
|
|
primaryChampionshipType: 'driver',
|
|
dropPolicySummary: 'Best 6 results of 8 count towards the championship.',
|
|
sessionSummary: 'Sprint + Main',
|
|
bonusSummary: 'Fastest lap +1 point in main race if finishing P10 or better.',
|
|
defaultTimings: {
|
|
practiceMinutes: 20,
|
|
qualifyingMinutes: 30,
|
|
sprintRaceMinutes: 20,
|
|
mainRaceMinutes: 40,
|
|
sessionCount: 2,
|
|
},
|
|
createConfig: ({ seasonId }) => {
|
|
const fastestLapBonus: BonusRule = {
|
|
id: 'fastest-lap-main',
|
|
type: 'fastestLap',
|
|
points: 1,
|
|
requiresFinishInTopN: 10,
|
|
};
|
|
|
|
const sessionTypes: SessionType[] = ['sprint', 'main'];
|
|
|
|
const pointsTableBySessionType: Record<SessionType, PointsTable> = {
|
|
sprint: sprintPointsSprintMain,
|
|
main: mainPointsSprintMain,
|
|
practice: new PointsTable({}),
|
|
qualifying: new PointsTable({}),
|
|
q1: new PointsTable({}),
|
|
q2: new PointsTable({}),
|
|
q3: new PointsTable({}),
|
|
timeTrial: new PointsTable({}),
|
|
};
|
|
|
|
const bonusRulesBySessionType: Record<SessionType, BonusRule[]> = {
|
|
sprint: [],
|
|
main: [fastestLapBonus],
|
|
practice: [],
|
|
qualifying: [],
|
|
q1: [],
|
|
q2: [],
|
|
q3: [],
|
|
timeTrial: [],
|
|
};
|
|
|
|
const dropScorePolicy: DropScorePolicy = {
|
|
strategy: 'bestNResults',
|
|
count: 6,
|
|
};
|
|
|
|
const championship: ChampionshipConfig = {
|
|
id: 'driver-champ-sprint-main',
|
|
name: 'Driver Championship',
|
|
type: 'driver' as ChampionshipType,
|
|
sessionTypes,
|
|
pointsTableBySessionType,
|
|
bonusRulesBySessionType,
|
|
dropScorePolicy,
|
|
};
|
|
|
|
return LeagueScoringConfig.create({
|
|
id: `lsc-${seasonId}-sprint-main-driver`,
|
|
seasonId,
|
|
scoringPresetId: 'sprint-main-driver',
|
|
championships: [championship],
|
|
});
|
|
},
|
|
},
|
|
{
|
|
id: 'club-default',
|
|
name: 'Club ladder',
|
|
description:
|
|
'Simple club ladder with a single main race and no bonuses or drop scores.',
|
|
primaryChampionshipType: 'driver',
|
|
dropPolicySummary: 'All race results count, no drop scores.',
|
|
sessionSummary: 'Main race only',
|
|
bonusSummary: 'No bonus points.',
|
|
defaultTimings: {
|
|
practiceMinutes: 20,
|
|
qualifyingMinutes: 20,
|
|
sprintRaceMinutes: 0,
|
|
mainRaceMinutes: 40,
|
|
sessionCount: 1,
|
|
},
|
|
createConfig: ({ seasonId }) => {
|
|
const sessionTypes: SessionType[] = ['main'];
|
|
|
|
const pointsTableBySessionType: Record<SessionType, PointsTable> = {
|
|
sprint: new PointsTable({}),
|
|
main: clubMainPoints,
|
|
practice: new PointsTable({}),
|
|
qualifying: new PointsTable({}),
|
|
q1: new PointsTable({}),
|
|
q2: new PointsTable({}),
|
|
q3: new PointsTable({}),
|
|
timeTrial: new PointsTable({}),
|
|
};
|
|
|
|
const dropScorePolicy: DropScorePolicy = {
|
|
strategy: 'none',
|
|
};
|
|
|
|
const championship: ChampionshipConfig = {
|
|
id: 'driver-champ-club-default',
|
|
name: 'Driver Championship',
|
|
type: 'driver' as ChampionshipType,
|
|
sessionTypes,
|
|
pointsTableBySessionType,
|
|
dropScorePolicy,
|
|
};
|
|
|
|
return LeagueScoringConfig.create({
|
|
id: `lsc-${seasonId}-club-default`,
|
|
seasonId,
|
|
scoringPresetId: 'club-default',
|
|
championships: [championship],
|
|
});
|
|
},
|
|
},
|
|
{
|
|
id: 'endurance-main-double',
|
|
name: 'Endurance weekend',
|
|
description:
|
|
'Single main endurance race with double points and a simple drop policy.',
|
|
primaryChampionshipType: 'driver',
|
|
dropPolicySummary: 'Best 4 results of 6 count towards the championship.',
|
|
sessionSummary: 'Main race only',
|
|
bonusSummary: 'No bonus points.',
|
|
defaultTimings: {
|
|
practiceMinutes: 30,
|
|
qualifyingMinutes: 20,
|
|
sprintRaceMinutes: 0,
|
|
mainRaceMinutes: 120,
|
|
sessionCount: 1,
|
|
},
|
|
createConfig: ({ seasonId }) => {
|
|
const sessionTypes: SessionType[] = ['main'];
|
|
|
|
const pointsTableBySessionType: Record<SessionType, PointsTable> = {
|
|
sprint: new PointsTable({}),
|
|
main: enduranceMainPoints,
|
|
practice: new PointsTable({}),
|
|
qualifying: new PointsTable({}),
|
|
q1: new PointsTable({}),
|
|
q2: new PointsTable({}),
|
|
q3: new PointsTable({}),
|
|
timeTrial: new PointsTable({}),
|
|
};
|
|
|
|
const dropScorePolicy: DropScorePolicy = {
|
|
strategy: 'bestNResults',
|
|
count: 4,
|
|
};
|
|
|
|
const championship: ChampionshipConfig = {
|
|
id: 'driver-champ-endurance-main-double',
|
|
name: 'Driver Championship',
|
|
type: 'driver' as ChampionshipType,
|
|
sessionTypes,
|
|
pointsTableBySessionType,
|
|
dropScorePolicy,
|
|
};
|
|
|
|
return LeagueScoringConfig.create({
|
|
id: `lsc-${seasonId}-endurance-main-double`,
|
|
seasonId,
|
|
scoringPresetId: 'endurance-main-double',
|
|
championships: [championship],
|
|
});
|
|
},
|
|
},
|
|
{
|
|
id: 'sprint-main-team',
|
|
name: 'Sprint + Main (Teams)',
|
|
description: 'Teams championship using the Sprint + Main weekend format.',
|
|
primaryChampionshipType: 'team',
|
|
dropPolicySummary: 'Best 6 results of 8 count towards the championship.',
|
|
sessionSummary: 'Sprint + Main',
|
|
bonusSummary: 'Fastest lap +1 point in main race if finishing P10 or better.',
|
|
defaultTimings: {
|
|
practiceMinutes: 20,
|
|
qualifyingMinutes: 30,
|
|
sprintRaceMinutes: 20,
|
|
mainRaceMinutes: 40,
|
|
sessionCount: 2,
|
|
},
|
|
createConfig: ({ seasonId }) => {
|
|
const fastestLapBonus: BonusRule = {
|
|
id: 'fastest-lap-main-team',
|
|
type: 'fastestLap',
|
|
points: 1,
|
|
requiresFinishInTopN: 10,
|
|
};
|
|
|
|
const sessionTypes: SessionType[] = ['sprint', 'main'];
|
|
|
|
const pointsTableBySessionType: Record<SessionType, PointsTable> = {
|
|
sprint: sprintPointsSprintMain,
|
|
main: mainPointsSprintMain,
|
|
practice: new PointsTable({}),
|
|
qualifying: new PointsTable({}),
|
|
q1: new PointsTable({}),
|
|
q2: new PointsTable({}),
|
|
q3: new PointsTable({}),
|
|
timeTrial: new PointsTable({}),
|
|
};
|
|
|
|
const bonusRulesBySessionType: Record<SessionType, BonusRule[]> = {
|
|
sprint: [],
|
|
main: [fastestLapBonus],
|
|
practice: [],
|
|
qualifying: [],
|
|
q1: [],
|
|
q2: [],
|
|
q3: [],
|
|
timeTrial: [],
|
|
};
|
|
|
|
const dropScorePolicy: DropScorePolicy = {
|
|
strategy: 'bestNResults',
|
|
count: 6,
|
|
};
|
|
|
|
const championship: ChampionshipConfig = {
|
|
id: 'team-champ-sprint-main',
|
|
name: 'Team Championship',
|
|
type: 'team' as ChampionshipType,
|
|
sessionTypes,
|
|
pointsTableBySessionType,
|
|
bonusRulesBySessionType,
|
|
dropScorePolicy,
|
|
};
|
|
|
|
return LeagueScoringConfig.create({
|
|
id: `lsc-${seasonId}-sprint-main-team`,
|
|
seasonId,
|
|
scoringPresetId: 'sprint-main-team',
|
|
championships: [championship],
|
|
});
|
|
},
|
|
},
|
|
{
|
|
id: 'club-default-nations',
|
|
name: 'Club ladder (Nations)',
|
|
description: 'Nation vs nation ladder with a single main race and no bonuses.',
|
|
primaryChampionshipType: 'nations',
|
|
dropPolicySummary: 'All race results count, no drop scores.',
|
|
sessionSummary: 'Main race only',
|
|
bonusSummary: 'No bonus points.',
|
|
defaultTimings: {
|
|
practiceMinutes: 20,
|
|
qualifyingMinutes: 20,
|
|
sprintRaceMinutes: 0,
|
|
mainRaceMinutes: 40,
|
|
sessionCount: 1,
|
|
},
|
|
createConfig: ({ seasonId }) => {
|
|
const sessionTypes: SessionType[] = ['main'];
|
|
|
|
const pointsTableBySessionType: Record<SessionType, PointsTable> = {
|
|
sprint: new PointsTable({}),
|
|
main: clubMainPoints,
|
|
practice: new PointsTable({}),
|
|
qualifying: new PointsTable({}),
|
|
q1: new PointsTable({}),
|
|
q2: new PointsTable({}),
|
|
q3: new PointsTable({}),
|
|
timeTrial: new PointsTable({}),
|
|
};
|
|
|
|
const dropScorePolicy: DropScorePolicy = {
|
|
strategy: 'none',
|
|
};
|
|
|
|
const championship: ChampionshipConfig = {
|
|
id: 'nations-champ-club-default',
|
|
name: 'Nations Championship',
|
|
type: 'nations' as ChampionshipType,
|
|
sessionTypes,
|
|
pointsTableBySessionType,
|
|
dropScorePolicy,
|
|
};
|
|
|
|
return LeagueScoringConfig.create({
|
|
id: `lsc-${seasonId}-club-default-nations`,
|
|
seasonId,
|
|
scoringPresetId: 'club-default-nations',
|
|
championships: [championship],
|
|
});
|
|
},
|
|
},
|
|
{
|
|
id: 'endurance-main-trophy',
|
|
name: 'Endurance Trophy Event',
|
|
description: 'Trophy-style endurance event with a single long main race.',
|
|
primaryChampionshipType: 'trophy',
|
|
dropPolicySummary: 'Best 4 results of 6 count towards the championship.',
|
|
sessionSummary: 'Main race only',
|
|
bonusSummary: 'No bonus points.',
|
|
defaultTimings: {
|
|
practiceMinutes: 30,
|
|
qualifyingMinutes: 20,
|
|
sprintRaceMinutes: 0,
|
|
mainRaceMinutes: 120,
|
|
sessionCount: 1,
|
|
},
|
|
createConfig: ({ seasonId }) => {
|
|
const sessionTypes: SessionType[] = ['main'];
|
|
|
|
const pointsTableBySessionType: Record<SessionType, PointsTable> = {
|
|
sprint: new PointsTable({}),
|
|
main: enduranceMainPoints,
|
|
practice: new PointsTable({}),
|
|
qualifying: new PointsTable({}),
|
|
q1: new PointsTable({}),
|
|
q2: new PointsTable({}),
|
|
q3: new PointsTable({}),
|
|
timeTrial: new PointsTable({}),
|
|
};
|
|
|
|
const dropScorePolicy: DropScorePolicy = {
|
|
strategy: 'bestNResults',
|
|
count: 4,
|
|
};
|
|
|
|
const championship: ChampionshipConfig = {
|
|
id: 'trophy-champ-endurance-main',
|
|
name: 'Trophy Championship',
|
|
type: 'trophy' as ChampionshipType,
|
|
sessionTypes,
|
|
pointsTableBySessionType,
|
|
dropScorePolicy,
|
|
};
|
|
|
|
return LeagueScoringConfig.create({
|
|
id: `lsc-${seasonId}-endurance-main-trophy`,
|
|
seasonId,
|
|
scoringPresetId: 'endurance-main-trophy',
|
|
championships: [championship],
|
|
});
|
|
},
|
|
},
|
|
];
|
|
|
|
export function listLeagueScoringPresets(): LeagueScoringPreset[] {
|
|
return [...leagueScoringPresets];
|
|
}
|
|
|
|
export function getLeagueScoringPresetById(
|
|
id: string,
|
|
): LeagueScoringPreset | undefined {
|
|
return leagueScoringPresets.find((preset) => preset.id === id);
|
|
} |