Skip to content

Commit e1183a9

Browse files
authored
fix: standings now show more cars for player's class when player is missing (#45)
1 parent 8dc2b01 commit e1183a9

File tree

3 files changed

+109
-59
lines changed

3 files changed

+109
-59
lines changed

src/frontend/components/Standings/createStandings.spec.ts

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect } from 'vitest';
2-
import { createStandings, sliceRelevantDrivers } from './createStandings';
2+
import { createDriverStandings, groupStandingsByClass, sliceRelevantDrivers } from './createStandings';
33
import type { Session, Telemetry, SessionInfo } from '@irdashies/types';
44

55
describe('createStandings', () => {
@@ -150,7 +150,7 @@ describe('createStandings', () => {
150150

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

177-
const filteredDrivers = sliceRelevantDrivers(results);
177+
const filteredDrivers = sliceRelevantDrivers(results, 'GT4');
178178

179179
expect(filteredDrivers).toEqual([
180180
[
@@ -194,12 +194,58 @@ describe('createStandings', () => {
194194
]);
195195
});
196196

197+
it('should return all player\'s class even when player is not in standings', () => {
198+
const results: [string, DummyStanding[]][] = [
199+
[
200+
'GT3',
201+
[
202+
{ name: '1. Bob' },
203+
{ name: '2. Alice' },
204+
{ name: '3. Charlie' },
205+
{ name: '4. David' },
206+
{ name: '5. Eve' },
207+
],
208+
],
209+
[
210+
'GT4',
211+
[
212+
{ name: '1. Clark' },
213+
{ name: '2. Richard' },
214+
{ name: '3. Sam' },
215+
{ name: '4. Bingo' },
216+
{ name: '5. Tod' },
217+
{ name: '6. Wallace' },
218+
],
219+
],
220+
];
221+
222+
const filteredDrivers = sliceRelevantDrivers(results, 'GT4');
223+
224+
expect(filteredDrivers).toEqual([
225+
[
226+
'GT3',
227+
[{ name: '1. Bob' }, { name: '2. Alice' }, { name: '3. Charlie' }],
228+
],
229+
[
230+
'GT4',
231+
[
232+
{ name: '1. Clark' },
233+
{ name: '2. Richard' },
234+
{ name: '3. Sam' },
235+
{ name: '4. Bingo' },
236+
{ name: '5. Tod' },
237+
{ name: '6. Wallace' },
238+
],
239+
],
240+
]);
241+
});
242+
197243
it('should return all drivers when less than 3 available for class without player', () => {
198244
const results: [string, DummyStanding[]][] = [
199245
['GT3', [{ name: 'Bob' }, { name: 'Alice' }]],
200246
];
201247

202-
const filteredDrivers = sliceRelevantDrivers(results);
248+
const filteredDrivers = sliceRelevantDrivers(results, 'GT3');
203249

204250
expect(filteredDrivers).toEqual([
205251
['GT3', [{ name: 'Bob' }, { name: 'Alice' }]],
@@ -227,7 +273,7 @@ describe('createStandings', () => {
227273
],
228274
];
229275

230-
const filteredDrivers = sliceRelevantDrivers(results);
276+
const filteredDrivers = sliceRelevantDrivers(results, 'GT3');
231277

232278
expect(filteredDrivers).toEqual([
233279
[
@@ -267,7 +313,7 @@ describe('createStandings', () => {
267313
],
268314
];
269315

270-
const filteredDrivers = sliceRelevantDrivers(results);
316+
const filteredDrivers = sliceRelevantDrivers(results, 'GT3');
271317

272318
expect(filteredDrivers).toEqual([
273319
[
@@ -286,3 +332,44 @@ describe('createStandings', () => {
286332
});
287333
});
288334
});
335+
336+
337+
/**
338+
* This method will create the standings for the current session
339+
* It will group the standings by class and slice the relevant drivers
340+
*
341+
* Only used to simplify testing
342+
*/
343+
function createStandings(
344+
session?: Session,
345+
telemetry?: Telemetry,
346+
currentSession?: SessionInfo,
347+
options?: {
348+
buffer?: number;
349+
numNonClassDrivers?: number;
350+
}
351+
) {
352+
const standings = createDriverStandings(
353+
{
354+
playerIdx: session?.DriverInfo?.DriverCarIdx,
355+
drivers: session?.DriverInfo?.Drivers,
356+
qualifyingResults: session?.QualifyResultsInfo?.Results,
357+
},
358+
{
359+
carIdxF2TimeValue: telemetry?.CarIdxF2Time?.value,
360+
carIdxOnPitRoadValue: telemetry?.CarIdxOnPitRoad?.value,
361+
carIdxTrackSurfaceValue: telemetry?.CarIdxTrackSurface?.value,
362+
radioTransmitCarIdx: telemetry?.RadioTransmitCarIdx?.value,
363+
},
364+
{
365+
resultsPositions: currentSession?.ResultsPositions,
366+
resultsFastestLap: currentSession?.ResultsFastestLap,
367+
sessionType: currentSession?.SessionType,
368+
}
369+
);
370+
const driverClass = session?.DriverInfo?.Drivers?.find(
371+
(driver) => driver.CarIdx === session?.DriverInfo?.DriverCarIdx
372+
)?.CarClassID;
373+
const grouped = groupStandingsByClass(standings);
374+
return sliceRelevantDrivers(grouped, driverClass, options);
375+
};

src/frontend/components/Standings/createStandings.ts

Lines changed: 12 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,5 @@
1-
import type {
2-
SessionInfo,
3-
SessionResults,
4-
Driver,
5-
Session,
6-
Telemetry,
7-
} from '@irdashies/types';
8-
import {
9-
calculateIRatingGain,
10-
RaceResult,
11-
CalculationResult,
12-
} from '@irdashies/utils/iratingGain';
1+
import type { SessionResults, Driver } from '@irdashies/types';
2+
import { calculateIRatingGain, RaceResult, CalculationResult } from '@irdashies/utils/iratingGain';
133

144
export interface Standings {
155
carIdx: number;
@@ -207,18 +197,25 @@ export const augmentStandingsWithIRating = (
207197
* This method will slice up the standings and return only the relevant drivers
208198
* Top 3 drivers are always included for each class
209199
* Within the player's class it will include the player and 5 drivers before and after
200+
*
201+
* @param groupedStandings - The grouped standings to slice
202+
* @param driverClass - The class of the player
203+
* @param options.buffer - The number of drivers to include before and after the player
204+
* @param options.numNonClassDrivers - The number of drivers to include in classes outside of the player's class
205+
* @returns The sliced standings
210206
*/
211207
export const sliceRelevantDrivers = <T extends { isPlayer?: boolean }>(
212208
groupedStandings: [string, T[]][],
213-
{ buffer = 3 } = {}
209+
driverClass: string | number | undefined,
210+
{ buffer = 3, numNonClassDrivers = 3 } = {}
214211
): [string, T[]][] => {
215212
// this is honestly a bit too complicated to maintain so after some testing will
216213
// probably simplify it so its a bit more readable
217214
return groupedStandings.map(([classIdx, standings]) => {
218215
const playerIndex = standings.findIndex((driver) => driver.isPlayer);
219-
if (playerIndex < 0) {
216+
if (String(driverClass) !== classIdx) {
220217
// if player is not in this class, return only top 3 drivers in that class
221-
return [classIdx, standings.slice(0, 3)];
218+
return [classIdx, standings.slice(0, numNonClassDrivers)];
222219
}
223220

224221
// if there are less than 10 drivers, return all
@@ -252,40 +249,3 @@ export const sliceRelevantDrivers = <T extends { isPlayer?: boolean }>(
252249
return [classIdx, sliced];
253250
});
254251
};
255-
256-
/**
257-
* This method will create the standings for the current session
258-
* It will group the standings by class and slice the relevant drivers
259-
*/
260-
export const createStandings = (
261-
session?: Session,
262-
telemetry?: Telemetry,
263-
currentSession?: SessionInfo,
264-
options?: {
265-
sliceRelevantDrivers?: {
266-
buffer?: number;
267-
};
268-
}
269-
) => {
270-
const standings = createDriverStandings(
271-
{
272-
playerIdx: session?.DriverInfo?.DriverCarIdx,
273-
drivers: session?.DriverInfo?.Drivers,
274-
qualifyingResults: session?.QualifyResultsInfo?.Results,
275-
},
276-
{
277-
carIdxF2TimeValue: telemetry?.CarIdxF2Time?.value,
278-
carIdxOnPitRoadValue: telemetry?.CarIdxOnPitRoad?.value,
279-
carIdxTrackSurfaceValue: telemetry?.CarIdxTrackSurface?.value,
280-
radioTransmitCarIdx: telemetry?.RadioTransmitCarIdx?.value,
281-
},
282-
{
283-
resultsPositions: currentSession?.ResultsPositions,
284-
resultsFastestLap: currentSession?.ResultsFastestLap,
285-
sessionType: currentSession?.SessionType,
286-
}
287-
);
288-
289-
const grouped = groupStandingsByClass(standings);
290-
return sliceRelevantDrivers(grouped, options?.sliceRelevantDrivers);
291-
};

src/frontend/components/Standings/hooks/useDriverStandings.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,17 @@ export const useDriverStandings = ({ buffer }: { buffer: number }) => {
5151
}
5252
);
5353
const groupedByClass = groupStandingsByClass(initialStandings);
54+
const driverClass = sessionDrivers?.find(
55+
(driver) => driver.CarIdx === driverCarIdx
56+
)?.CarClassID;
5457

5558
// Calculate iRating changes for race sessions
5659
const augmentedGroupedByClass =
5760
sessionType === 'Race' && isOfficial
5861
? augmentStandingsWithIRating(groupedByClass)
5962
: groupedByClass;
6063

61-
return sliceRelevantDrivers(augmentedGroupedByClass, { buffer });
64+
return sliceRelevantDrivers(augmentedGroupedByClass, driverClass, { buffer });
6265
}, [
6366
driverCarIdx,
6467
sessionDrivers,

0 commit comments

Comments
 (0)