Files
gridpilot.gg/adapters/bootstrap/racing/RacingRaceFactory.ts
2025-12-30 12:25:45 +01:00

112 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { League } from '@core/racing/domain/entities/League';
import { Race } from '@core/racing/domain/entities/Race';
import { Track } from '@core/racing/domain/entities/Track';
import { seedId } from './SeedIdHelper';
export class RacingRaceFactory {
constructor(
private readonly baseDate: Date,
private readonly persistence: 'postgres' | 'inmemory' = 'inmemory',
) {}
create(leagues: League[], tracks: Track[]): Race[] {
const cars = ['GT3 Porsche 911', 'GT3 BMW M4', 'LMP3 Prototype', 'GT4 Alpine', 'Touring Civic'];
const leagueIds = leagues.map((l) => l.id.toString());
const trackIds = tracks.map((t) => t.id);
const demoLeagueId = 'league-5';
const races: Race[] = [];
// Create races with systematic coverage of different statuses and scenarios
for (let i = 1; i <= 500; i++) {
const leagueId = leagueIds[(i - 1) % leagueIds.length] ?? demoLeagueId;
const trackId = trackIds[(i - 1) % trackIds.length]!;
const track = tracks.find(t => t.id === trackId)!;
// Determine status systematically to ensure good coverage
let status: 'scheduled' | 'running' | 'completed' | 'cancelled';
let scheduledAt: Date;
// Use modulo to create a balanced distribution across 500 races
const statusMod = i % 20; // 20 different patterns
if (statusMod === 1 || statusMod === 2 || statusMod === 3) {
// 15% running (3 out of 20)
status = 'running';
scheduledAt = this.addDays(this.baseDate, -1 + (statusMod * 0.5)); // Recent past/current
} else if (statusMod === 4 || statusMod === 5 || statusMod === 6 || statusMod === 7) {
// 20% cancelled (4 out of 20)
status = 'cancelled';
scheduledAt = this.addDays(this.baseDate, -30 + (statusMod * 2));
} else if (statusMod === 8 || statusMod === 9 || statusMod === 10 || statusMod === 11 || statusMod === 12) {
// 25% completed (5 out of 20)
status = 'completed';
scheduledAt = this.addDays(this.baseDate, -50 + (statusMod * 3));
} else {
// 40% scheduled (8 out of 20)
status = 'scheduled';
scheduledAt = this.addDays(this.baseDate, 1 + ((statusMod - 13) * 2));
}
const base = {
id: seedId(`race-${i}`, this.persistence),
leagueId,
scheduledAt,
track: track.name.toString(),
trackId: track.id,
car: cars[(i - 1) % cars.length]!,
};
// Create race based on status with appropriate data
if (status === 'running') {
races.push(
Race.create({
...base,
status: 'running',
strengthOfField: 40 + (i % 60), // Valid SOF: 0-100
registeredCount: 10 + (i % 15), // Varying registration counts
maxParticipants: 20 + (i % 8), // 20-28 participants
}),
);
} else if (status === 'completed') {
races.push(
Race.create({
...base,
status: 'completed',
strengthOfField: 30 + (i % 70), // Valid SOF: 0-100
registeredCount: 6 + (i % 12),
maxParticipants: 16 + (i % 10),
}),
);
} else if (status === 'scheduled') {
const hasRegistrations = i % 3 !== 0; // 66% have registrations
races.push(
Race.create({
...base,
status: 'scheduled',
...(hasRegistrations && {
strengthOfField: 35 + (i % 65), // Valid SOF: 0-100
registeredCount: 4 + (i % 12),
maxParticipants: 14 + (i % 12),
}),
}),
);
} else if (status === 'cancelled') {
races.push(
Race.create({
...base,
status: 'cancelled',
}),
);
}
}
return races;
}
private addDays(date: Date, days: number): Date {
return new Date(date.getTime() + days * 24 * 60 * 60 * 1000);
}
}