Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 93 additions & 6 deletions src/frontend/components/Standings/createStandings.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect } from 'vitest';
import { createStandings, sliceRelevantDrivers } from './createStandings';
import { createDriverStandings, groupStandingsByClass, sliceRelevantDrivers } from './createStandings';
import type { Session, Telemetry, SessionInfo } from '@irdashies/types';

describe('createStandings', () => {
Expand Down Expand Up @@ -150,7 +150,7 @@ describe('createStandings', () => {

describe('sliceRelevantDrivers', () => {
interface DummyStanding { name: string; isPlayer?: boolean }
it('should return only top 3 drivers for class without player', () => {
it('should return only top 3 drivers for classes outside of player\'s class', () => {
const results: [string, DummyStanding[]][] = [
[
'GT3',
Expand All @@ -174,7 +174,7 @@ describe('createStandings', () => {
],
];

const filteredDrivers = sliceRelevantDrivers(results);
const filteredDrivers = sliceRelevantDrivers(results, 'GT4');

expect(filteredDrivers).toEqual([
[
Expand All @@ -194,12 +194,58 @@ describe('createStandings', () => {
]);
});

it('should return all player\'s class even when player is not in standings', () => {
const results: [string, DummyStanding[]][] = [
[
'GT3',
[
{ name: '1. Bob' },
{ name: '2. Alice' },
{ name: '3. Charlie' },
{ name: '4. David' },
{ name: '5. Eve' },
],
],
[
'GT4',
[
{ name: '1. Clark' },
{ name: '2. Richard' },
{ name: '3. Sam' },
{ name: '4. Bingo' },
{ name: '5. Tod' },
{ name: '6. Wallace' },
],
],
];

const filteredDrivers = sliceRelevantDrivers(results, 'GT4');

expect(filteredDrivers).toEqual([
[
'GT3',
[{ name: '1. Bob' }, { name: '2. Alice' }, { name: '3. Charlie' }],
],
[
'GT4',
[
{ name: '1. Clark' },
{ name: '2. Richard' },
{ name: '3. Sam' },
{ name: '4. Bingo' },
{ name: '5. Tod' },
{ name: '6. Wallace' },
],
],
]);
});

it('should return all drivers when less than 3 available for class without player', () => {
const results: [string, DummyStanding[]][] = [
['GT3', [{ name: 'Bob' }, { name: 'Alice' }]],
];

const filteredDrivers = sliceRelevantDrivers(results);
const filteredDrivers = sliceRelevantDrivers(results, 'GT3');

expect(filteredDrivers).toEqual([
['GT3', [{ name: 'Bob' }, { name: 'Alice' }]],
Expand Down Expand Up @@ -227,7 +273,7 @@ describe('createStandings', () => {
],
];

const filteredDrivers = sliceRelevantDrivers(results);
const filteredDrivers = sliceRelevantDrivers(results, 'GT3');

expect(filteredDrivers).toEqual([
[
Expand Down Expand Up @@ -267,7 +313,7 @@ describe('createStandings', () => {
],
];

const filteredDrivers = sliceRelevantDrivers(results);
const filteredDrivers = sliceRelevantDrivers(results, 'GT3');

expect(filteredDrivers).toEqual([
[
Expand All @@ -286,3 +332,44 @@ describe('createStandings', () => {
});
});
});


/**
* This method will create the standings for the current session
* It will group the standings by class and slice the relevant drivers
*
* Only used to simplify testing
*/
function createStandings(
session?: Session,
telemetry?: Telemetry,
currentSession?: SessionInfo,
options?: {
buffer?: number;
numNonClassDrivers?: number;
}
) {
const standings = createDriverStandings(
{
playerIdx: session?.DriverInfo?.DriverCarIdx,
drivers: session?.DriverInfo?.Drivers,
qualifyingResults: session?.QualifyResultsInfo?.Results,
},
{
carIdxF2TimeValue: telemetry?.CarIdxF2Time?.value,
carIdxOnPitRoadValue: telemetry?.CarIdxOnPitRoad?.value,
carIdxTrackSurfaceValue: telemetry?.CarIdxTrackSurface?.value,
radioTransmitCarIdx: telemetry?.RadioTransmitCarIdx?.value,
},
{
resultsPositions: currentSession?.ResultsPositions,
resultsFastestLap: currentSession?.ResultsFastestLap,
sessionType: currentSession?.SessionType,
}
);
const driverClass = session?.DriverInfo?.Drivers?.find(
(driver) => driver.CarIdx === session?.DriverInfo?.DriverCarIdx
)?.CarClassID;
const grouped = groupStandingsByClass(standings);
return sliceRelevantDrivers(grouped, driverClass, options);
};
64 changes: 12 additions & 52 deletions src/frontend/components/Standings/createStandings.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
import type {
SessionInfo,
SessionResults,
Driver,
Session,
Telemetry,
} from '@irdashies/types';
import {
calculateIRatingGain,
RaceResult,
CalculationResult,
} from '@irdashies/utils/iratingGain';
import type { SessionResults, Driver } from '@irdashies/types';
import { calculateIRatingGain, RaceResult, CalculationResult } from '@irdashies/utils/iratingGain';

export interface Standings {
carIdx: number;
Expand Down Expand Up @@ -207,18 +197,25 @@ export const augmentStandingsWithIRating = (
* This method will slice up the standings and return only the relevant drivers
* Top 3 drivers are always included for each class
* Within the player's class it will include the player and 5 drivers before and after
*
* @param groupedStandings - The grouped standings to slice
* @param driverClass - The class of the player
* @param options.buffer - The number of drivers to include before and after the player
* @param options.numNonClassDrivers - The number of drivers to include in classes outside of the player's class
* @returns The sliced standings
*/
export const sliceRelevantDrivers = <T extends { isPlayer?: boolean }>(
groupedStandings: [string, T[]][],
{ buffer = 3 } = {}
driverClass: string | number | undefined,
{ buffer = 3, numNonClassDrivers = 3 } = {}
): [string, T[]][] => {
// this is honestly a bit too complicated to maintain so after some testing will
// probably simplify it so its a bit more readable
return groupedStandings.map(([classIdx, standings]) => {
const playerIndex = standings.findIndex((driver) => driver.isPlayer);
if (playerIndex < 0) {
if (String(driverClass) !== classIdx) {
// if player is not in this class, return only top 3 drivers in that class
return [classIdx, standings.slice(0, 3)];
return [classIdx, standings.slice(0, numNonClassDrivers)];
}

// if there are less than 10 drivers, return all
Expand Down Expand Up @@ -252,40 +249,3 @@ export const sliceRelevantDrivers = <T extends { isPlayer?: boolean }>(
return [classIdx, sliced];
});
};

/**
* This method will create the standings for the current session
* It will group the standings by class and slice the relevant drivers
*/
export const createStandings = (
session?: Session,
telemetry?: Telemetry,
currentSession?: SessionInfo,
options?: {
sliceRelevantDrivers?: {
buffer?: number;
};
}
) => {
const standings = createDriverStandings(
{
playerIdx: session?.DriverInfo?.DriverCarIdx,
drivers: session?.DriverInfo?.Drivers,
qualifyingResults: session?.QualifyResultsInfo?.Results,
},
{
carIdxF2TimeValue: telemetry?.CarIdxF2Time?.value,
carIdxOnPitRoadValue: telemetry?.CarIdxOnPitRoad?.value,
carIdxTrackSurfaceValue: telemetry?.CarIdxTrackSurface?.value,
radioTransmitCarIdx: telemetry?.RadioTransmitCarIdx?.value,
},
{
resultsPositions: currentSession?.ResultsPositions,
resultsFastestLap: currentSession?.ResultsFastestLap,
sessionType: currentSession?.SessionType,
}
);

const grouped = groupStandingsByClass(standings);
return sliceRelevantDrivers(grouped, options?.sliceRelevantDrivers);
};
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,17 @@ export const useDriverStandings = ({ buffer }: { buffer: number }) => {
}
);
const groupedByClass = groupStandingsByClass(initialStandings);
const driverClass = sessionDrivers?.find(
(driver) => driver.CarIdx === driverCarIdx
)?.CarClassID;

// Calculate iRating changes for race sessions
const augmentedGroupedByClass =
sessionType === 'Race' && isOfficial
? augmentStandingsWithIRating(groupedByClass)
: groupedByClass;

return sliceRelevantDrivers(augmentedGroupedByClass, { buffer });
return sliceRelevantDrivers(augmentedGroupedByClass, driverClass, { buffer });
}, [
driverCarIdx,
sessionDrivers,
Expand Down