Files
gridpilot.gg/core/racing/application/utils/RaceResultGenerator.ts
2025-12-23 15:38:50 +01:00

131 lines
4.1 KiB
TypeScript

import { Result } from '../../domain/entities/result/Result';
/**
* Enhanced race result generator with detailed incident types
*/
export class RaceResultGenerator {
/**
* Generate realistic race results with detailed incidents
*/
static generateRaceResults(
raceId: string,
driverIds: string[],
driverRatings: Map<string, number>
): Result[] {
// Create driver performance data
const driverPerformances = driverIds.map(driverId => ({
driverId,
rating: driverRatings.get(driverId) ?? 1500, // Default rating
randomFactor: Math.random() - 0.5, // -0.5 to +0.5 randomization
}));
// Sort by performance (rating + randomization)
driverPerformances.sort((a, b) => {
const perfA = a.rating + (a.randomFactor * 200); // ±100 rating points randomization
const perfB = b.rating + (b.randomFactor * 200);
return perfB - perfA; // Higher performance first
});
// Generate qualifying results for start positions (similar but different from race results)
const qualiPerformances = driverPerformances.map(p => ({
...p,
randomFactor: Math.random() - 0.5, // New randomization for quali
}));
qualiPerformances.sort((a, b) => {
const perfA = a.rating + (a.randomFactor * 150);
const perfB = b.rating + (b.randomFactor * 150);
return perfB - perfA;
});
// Generate results
const results: Result[] = [];
for (const [index, performance] of driverPerformances.entries()) {
const driverId = performance.driverId;
const position = index + 1;
const startPosition =
qualiPerformances.findIndex(p => p.driverId === driverId) + 1;
// Generate realistic lap times (90-120 seconds for a lap)
const baseLapTime = 90000 + Math.random() * 30000;
const positionBonus = (position - 1) * 500; // Winners are faster
const fastestLap = Math.round(baseLapTime + positionBonus + Math.random() * 5000);
// Generate detailed incidents
const incidents = this.generateDetailedIncidents(position, driverPerformances.length);
results.push(
Result.create({
id: `${raceId}-${driverId}`,
raceId,
driverId,
position,
startPosition,
fastestLap,
incidents,
})
);
}
return results;
}
/**
* Generate detailed incidents with specific types
*/
private static generateDetailedIncidents(position: number, totalDrivers: number): number {
// Base probability increases for lower positions (more aggressive driving)
const baseProbability = Math.min(0.85, position / totalDrivers + 0.1);
// Add some randomness
const randomFactor = Math.random();
if (randomFactor > baseProbability) {
return 0; // Clean race
}
// Determine incident severity based on position and randomness
const severityRoll = Math.random();
if (severityRoll < 0.4) {
// Minor incident (track limits, small contact)
return 1;
} else if (severityRoll < 0.7) {
// Moderate incident (off-track, contact with damage)
return 2;
} else if (severityRoll < 0.9) {
// Major incident (spin, collision)
return 3;
} else {
// Severe incident (multiple cars involved, safety car)
return Math.floor(Math.random() * 2) + 3; // 3-4 incidents
}
}
/**
* Get incident type description for a given incident count
*/
static getIncidentDescription(incidents: number): string {
switch (incidents) {
case 0:
return 'Clean race';
case 1:
return 'Track limits violation';
case 2:
return 'Contact with another car';
case 3:
return 'Off-track incident';
case 4:
return 'Collision requiring safety car';
default:
return `${incidents} incidents`;
}
}
/**
* Calculate incident penalty points for standings
*/
static getIncidentPenaltyPoints(incidents: number): number {
// Each incident deducts points from championship standings
return Math.max(0, incidents - 1) * 2; // First incident free, then 2 points each
}
}