Files
gridpilot.gg/adapters/bootstrap/LeagueScoringPresets.ts
2025-12-27 11:58:35 +01:00

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);
}