Skip to content

Commit

Permalink
fix: 🐛 getGroupOrder: preserve ordered subGroups when no subGroup r…
Browse files Browse the repository at this point in the history
…esolutions
  • Loading branch information
CourtHive committed Mar 26, 2024
1 parent 930271d commit 65ff85b
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 39 deletions.
86 changes: 51 additions & 35 deletions src/query/matchUps/roundRobinTally/getGroupOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,13 @@ const GEMScoreValueMap = {
setsPct: 12,
};

/**
*
* @param {object[]} participantResults - calculated results for each participant
* @param {number} participantsCount - number of participants in round robin group
* @param {object} subOrderMap - { [participantId]: subOrder }
*
*/

export function getGroupOrder(params) {
const { requireCompletion = true, participantResults, subOrderMap, tallyPolicy } = params;
const {
requireCompletion = true, // no order is provided unless all opponents have completed their matchUps
participantResults, // { participantId: { matchUpsWon, matchUpsLost, matchUpsCancelled, ... } }
subOrderMap, // { participantId: subOrder }
tallyPolicy, // array of attributes to use for tie-breaking
} = params;

const report: any[] = [];

Expand Down Expand Up @@ -97,17 +94,23 @@ export function getGroupOrder(params) {

report.push({ attribute, groups: orderedTallyGroups });

const groupOrder = Object.keys(orderedTallyGroups)
const sortedTallyGroups = Object.keys(orderedTallyGroups)
.map((key) => parseFloat(key))
.sort((a, b) => b - a)
.map((key) => orderedTallyGroups[key])
.map((participantIds) => {
const result = groupSubSort({ participantIds, ...params });
report.push(...(result.report ?? []));
return result.order;
})
.flat(Infinity);
.map((key) => orderedTallyGroups[key]);

const sortedOrder = sortedTallyGroups.map((participantIds) => {
const result = groupSubSort({ participantIds, ...params });
report.push(...(result.report ?? []));
return result.order;
});

const groupOrder = sortedOrder
.map((order, oi) => order.map((o) => (o.resolved ? o : { ...o, subGroup: [oi].concat(...(o.subGroup ?? [])) })))
.flat();

let lastSubGroup;
let subGroupCount = 0;
let groupPosition = 1;
let priorPositionResolution;
groupOrder.forEach((finishingPosition, index) => {
Expand All @@ -124,15 +127,24 @@ export function getGroupOrder(params) {
// update prior position resolution
priorPositionResolution = finishingPosition.resolved;

const subGroup = parseInt(finishingPosition.subGroup?.join('') || 0);

if (finishingPosition.resolved) {
// if a position is resolved, position is index + 1
finishingPosition.position = index + 1;
// if a position is resolved, update groupPosition
groupPosition = finishingPosition.position;
} else {
if (lastSubGroup && subGroup > lastSubGroup) {
groupPosition += subGroupCount;
subGroupCount = 0;
}
// if a position is unresovled, position is groupPosition
finishingPosition.position = groupPosition;
subGroupCount += 1;
}

lastSubGroup = subGroup;
});

const positions = groupOrder.map(({ position }) => position);
Expand Down Expand Up @@ -212,23 +224,27 @@ function processAttribute({

if (Object.keys(groups).length > 1 && participantIds.length) {
// separation by attribute was successful
order = Object.keys(groups)
const sortedTallyGroups = Object.keys(groups)
.map((key) => parseFloat(key))
.sort((a, b) => (reversed ? a - b : b - a))
.map((key) => groups[key])
.map((participantIds) => {
const result = groupSubSort({
participantResults,
disableHeadToHead,
participantIds,
matchUpFormat,
tallyPolicy,
matchUps,
});
report.push(...(result.report ?? []));
return result.order;
})
.flat(Infinity);
.map((key) => groups[key]);

const sortedOrder = sortedTallyGroups.map((participantIds) => {
const result = groupSubSort({
participantResults,
disableHeadToHead,
participantIds,
matchUpFormat,
tallyPolicy,
matchUps,
});
report.push(...(result.report ?? []));
return result.order;
});

order = sortedOrder
.map((order, oi) => order.map((o) => (o.resolved ? o : { ...o, subGroup: [oi].concat(...(o.subGroup ?? [])) })))
.flat();
}

return { order, report };
Expand All @@ -249,11 +265,11 @@ function groupSubSort({ participantResults, disableHeadToHead, participantIds, m
participantIds?.length === 2 &&
(!tallyPolicy?.headToHead || (!tallyPolicy.headToHead.disabled && !disableHeadToHead))
) {
const result = headToHeadWinner({ participantIds, participantResults });
const result = getHeadToHeadWinner({ participantIds, participantResults });
if (result) {
const headToHeadWinner = result[0].participantId;
report.push({ attribute: 'head2Head', participantIds, headToHeadWinner });
return { order: [result], headToHeadWinner, report };
return { order: result, headToHeadWinner, report };
}
}

Expand Down Expand Up @@ -295,7 +311,7 @@ function groupSubSort({ participantResults, disableHeadToHead, participantIds, m
}

// NOTE: This currently considers one victory rather than a head2head win/loss record (considering rounds of play where participants may encounter each other more than once)
function headToHeadWinner({ participantIds, participantResults }) {
function getHeadToHeadWinner({ participantIds, participantResults }) {
if (!participantIds) return;

if (participantResults[participantIds[0]].victories.includes(participantIds[1])) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { tallyParticipantResults } from '@Query/matchUps/roundRobinTally/tallyParticipantResults';
import { xa } from '@Tools/extractAttributes';
import tournamentEngine from '@Engines/syncEngine';
import { xa } from '@Tools/extractAttributes';
import { expect, it } from 'vitest';

// constants and fixtures
Expand All @@ -12,7 +12,7 @@ import { POLICY_TYPE_ROUND_ROBIN_TALLY } from '@Constants/policyConstants';
import tournamentRecord from './dominantDuo.tods.json';

it('supports multiple policy configurations', () => {
const result = tournamentEngine.setState(tournamentRecord);
let result = tournamentEngine.setState(tournamentRecord);
expect(result.success).toEqual(true);

const RR = tournamentRecord.events[0].drawDefinitions[0].structures[0];
Expand Down Expand Up @@ -42,12 +42,14 @@ it('supports multiple policy configurations', () => {
GEMscore: ['matchUpsPct', 'tieMatchUpsPct', 'gamesWon', 'gamesPct'],
};

participantResults = tallyParticipantResults({
result = tallyParticipantResults({
policyDefinitions: {
[POLICY_TYPE_ROUND_ROBIN_TALLY]: fewestGamesLostWinReversed,
},
matchUps: structure1MatchUps,
}).participantResults;
generateReport: true,
});
participantResults = result.participantResults;

expect(structureGroupOrder(RR.structures[0])).toEqual([4, 3, 2, 1]);

Expand Down

0 comments on commit 65ff85b

Please sign in to comment.