Skip to content

Commit a59044a

Browse files
committed
add minimum drivers to display to standings
1 parent aa88356 commit a59044a

File tree

2 files changed

+128
-27
lines changed

2 files changed

+128
-27
lines changed

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ describe('createStandings', () => {
315315

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

318+
expect(filteredDrivers[0][1]).toHaveLength(10);
319+
318320
expect(filteredDrivers).toEqual([
319321
[
320322
'GT3',
@@ -326,10 +328,88 @@ describe('createStandings', () => {
326328
{ name: '5. David' },
327329
{ name: '6. Sebastian' },
328330
{ name: '7. Nico' },
331+
{ name: '8. Eve' },
332+
{ name: '9. Frank' },
333+
{ name: '10. Max' },
329334
],
330335
],
331336
]);
332337
});
338+
339+
it('should return at least 10 drivers if available', () => {
340+
const results: [string, DummyStanding[]][] = [
341+
[
342+
'GT3',
343+
[
344+
{ name: '1. Bob' },
345+
{ name: '2. Alice' },
346+
{ name: '3. Charlie' },
347+
{ name: '4. David' },
348+
{ name: '5. Eve' },
349+
{ name: '6. Frank' },
350+
{ name: '7. Player', isPlayer: true },
351+
{ name: '8. Hannah' },
352+
{ name: '9. Irene' },
353+
{ name: '10. Jack' },
354+
{ name: '11. Kevin' },
355+
],
356+
],
357+
];
358+
const filteredDrivers = sliceRelevantDrivers(results, 'GT3');
359+
expect(filteredDrivers[0][1].length).toBe(10);
360+
});
361+
362+
it('should allow minPlayerClassDrivers to be configured', () => {
363+
const results: [string, DummyStanding[]][] = [
364+
[
365+
'GT3',
366+
[
367+
{ name: '1. Bob' },
368+
{ name: '2. Alice' },
369+
{ name: '3. Charlie' },
370+
{ name: '4. David' },
371+
{ name: '5. Eve' },
372+
{ name: '6. Frank' },
373+
{ name: '7. Player', isPlayer: true },
374+
{ name: '8. Hannah' },
375+
{ name: '9. Irene' },
376+
{ name: '10. Jack' },
377+
{ name: '11. Kevin' },
378+
],
379+
],
380+
];
381+
const filteredDrivers = sliceRelevantDrivers(results, 'GT3', {
382+
minPlayerClassDrivers: 5,
383+
});
384+
expect(filteredDrivers[0][1].length).toBe(10);
385+
});
386+
387+
it('should allow numTopDrivers to be configured', () => {
388+
const results: [string, DummyStanding[]][] = [
389+
[
390+
'GT3',
391+
[
392+
{ name: '1. Bob' },
393+
{ name: '2. Alice' },
394+
{ name: '3. Charlie' },
395+
{ name: '4. David' },
396+
{ name: '5. Eve' },
397+
{ name: '6. Frank' },
398+
{ name: '7. Player', isPlayer: true },
399+
{ name: '8. Hannah' },
400+
{ name: '9. Irene' },
401+
{ name: '10. Jack' },
402+
{ name: '11. Kevin' },
403+
],
404+
],
405+
];
406+
const filteredDrivers = sliceRelevantDrivers(results, 'GT3', {
407+
numTopDrivers: 1,
408+
});
409+
expect(filteredDrivers[0][1].length).toBe(9);
410+
expect(filteredDrivers[0][1][0].name).toBe('1. Bob');
411+
expect(filteredDrivers[0][1][1].name).toBe('4. David');
412+
});
333413
});
334414
});
335415

@@ -347,6 +427,8 @@ function createStandings(
347427
options?: {
348428
buffer?: number;
349429
numNonClassDrivers?: number;
430+
minPlayerClassDrivers?: number;
431+
numTopDrivers?: number;
350432
}
351433
) {
352434
const standings = createDriverStandings(

src/frontend/components/Standings/createStandings.ts

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -202,50 +202,69 @@ export const augmentStandingsWithIRating = (
202202
* @param driverClass - The class of the player
203203
* @param options.buffer - The number of drivers to include before and after the player
204204
* @param options.numNonClassDrivers - The number of drivers to include in classes outside of the player's class
205+
* @param options.minPlayerClassDrivers - The minimum number of drivers to include in the player's class
206+
* @param options.numTopDrivers - The number of top drivers to always include in the player's class
205207
* @returns The sliced standings
206208
*/
207209
export const sliceRelevantDrivers = <T extends { isPlayer?: boolean }>(
208210
groupedStandings: [string, T[]][],
209211
driverClass: string | number | undefined,
210-
{ buffer = 3, numNonClassDrivers = 3 } = {}
212+
{
213+
buffer = 3,
214+
numNonClassDrivers = 3,
215+
minPlayerClassDrivers = 10,
216+
numTopDrivers = 3,
217+
} = {}
211218
): [string, T[]][] => {
212-
// this is honestly a bit too complicated to maintain so after some testing will
213-
// probably simplify it so its a bit more readable
214219
return groupedStandings.map(([classIdx, standings]) => {
215220
const playerIndex = standings.findIndex((driver) => driver.isPlayer);
216221
if (String(driverClass) !== classIdx) {
217-
// if player is not in this class, return only top 3 drivers in that class
222+
// if player is not in this class, return only top N drivers in that class
218223
return [classIdx, standings.slice(0, numNonClassDrivers)];
219224
}
220225

221-
// if there are less than 10 drivers, return all
222-
if (standings.length <= 10) return [classIdx, standings];
226+
// if there are less than minPlayerClassDrivers drivers, return all of them
227+
if (standings.length <= minPlayerClassDrivers) {
228+
return [classIdx, standings];
229+
}
223230

224-
// take the player and a buffer of drivers before and after the player
225-
const start = Math.max(playerIndex - buffer, 0);
226-
let end = Math.min(playerIndex + buffer + 1, standings.length);
231+
// when no player is found, just return the top `minPlayerClassDrivers`
232+
if (playerIndex === -1) {
233+
return [classIdx, standings.slice(0, minPlayerClassDrivers)];
234+
}
227235

228-
if (playerIndex <= 3) {
229-
// if player is in top 3, include more drivers at the end
230-
end = Math.min(
231-
playerIndex + buffer + 3 - playerIndex + 1,
232-
standings.length
233-
);
236+
const relevantDrivers = new Set<T>();
237+
238+
// Add top drivers
239+
for (let i = 0; i < numTopDrivers; i++) {
240+
if (standings[i]) {
241+
relevantDrivers.add(standings[i]);
242+
}
234243
}
235244

236-
const sliced = standings.slice(start, end);
237-
238-
if (playerIndex > 3) {
239-
// add back top 3 but don't include overlapping indexes
240-
// reverse to add in correct order when doing array unshift
241-
standings
242-
.slice(0, 3)
243-
.reverse()
244-
.forEach((driver) => {
245-
if (!sliced.includes(driver)) sliced.unshift(driver);
246-
});
245+
// Add drivers around the player
246+
const start = Math.max(0, playerIndex - buffer);
247+
const end = Math.min(standings.length, playerIndex + buffer + 1);
248+
for (let i = start; i < end; i++) {
249+
if (standings[i]) {
250+
relevantDrivers.add(standings[i]);
251+
}
252+
}
253+
254+
// Ensure we have at least `minPlayerClassDrivers`
255+
let lastIndex = end;
256+
while (
257+
relevantDrivers.size < minPlayerClassDrivers &&
258+
lastIndex < standings.length
259+
) {
260+
relevantDrivers.add(standings[lastIndex]);
261+
lastIndex++;
247262
}
248263

249-
return [classIdx, sliced];
264+
const sortedDrivers = [...relevantDrivers].sort(
265+
(a, b) => standings.indexOf(a) - standings.indexOf(b)
266+
);
267+
268+
return [classIdx, sortedDrivers];
250269
});
251270
};

0 commit comments

Comments
 (0)