131 lines
4.1 KiB
TypeScript
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
|
|
}
|
|
} |