Skip to content

Commit

Permalink
feat: ✨ implement and export adHocPositionSwap
Browse files Browse the repository at this point in the history
  • Loading branch information
CourtHive committed Mar 20, 2024
1 parent c269808 commit 3fa5c62
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/assemblies/governors/drawsGovernor/mutate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { addVoluntaryConsolationStage } from '@Mutate/events/addVoluntaryConsola
export { swapDrawPositionAssignments } from '@Mutate/matchUps/drawPositions/positionSwap';
export { removeSeededParticipant } from '@Mutate/drawDefinitions/removeSeededParticipant';
export { addQualifyingStructure } from '@Mutate/drawDefinitions/addQualifyingStructure';
export { adHocPositionSwap } from '@Mutate/matchUps/drawPositions/adHocPositionSwap';
export { addPlayoffStructures } from '@Mutate/drawDefinitions/addPlayoffStructures';
export { automatedPositioning } from '@Mutate/drawDefinitions/automatedPositioning';
export { modifyDrawDefinition } from '@Mutate/drawDefinitions/modifyDrawDefinition';
Expand Down
2 changes: 1 addition & 1 deletion src/constants/positionActionConstants.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export const QUALIFYING_PARTICIPANT_METHOD = 'qualifierDrawPositionAssignment';
export const WITHDRAW_PARTICIPANT_METHOD = 'withdrawParticipantAtDrawPosition';
export const ALTERNATE_PARTICIPANT_METHOD = 'alternateDrawPositionAssignment';
export const SWAP_ADHOC_PARTICIPANT_METHOD = 'swapAdHocMatchUpAssignments';
export const LUCKY_PARTICIPANT_METHOD = 'luckyLoserDrawPositionAssignment';
export const REMOVE_ASSIGNMENT_METHOD = 'removeDrawPositionAssignment';
export const SWAP_PARTICIPANT_METHOD = 'swapDrawPositionAssignments';
export const MODIFY_PAIR_ASSIGNMENT_METHOD = 'modifyPairAssignment';
export const SWAP_ADHOC_PARTICIPANT_METHOD = 'adHocPositionSwap';
export const ADD_NICKNAME_METHOD = 'modifyParticipantOtherName';
export const ASSIGN_PARTICIPANT_METHOD = 'assignDrawPosition';
export const REMOVE_SEED_METHOD = 'removeSeededParticipant';
Expand Down
68 changes: 68 additions & 0 deletions src/mutate/matchUps/drawPositions/adHocPositionSwap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { modifyDrawNotice, modifyMatchUpNotice } from '@Mutate/notifications/drawNotifications';
import { checkRequiredParameters } from '@Helpers/parameters/checkRequiredParameters';
import { checkScoreHasValue } from '@Query/matchUp/checkScoreHasValue';
import { isAdHoc } from '@Query/drawDefinition/isAdHoc';
import { findStructure } from '@Acquire/findStructure';
import { isString } from '@Tools/objects';

// constants
import { INVALID_PARTICIPANT_IDS, INVALID_STRUCTURE, STRUCTURE_NOT_FOUND } from '@Constants/errorConditionConstants';
import { SUCCESS } from '@Constants/resultConstants';
import {
DRAW_DEFINITION,
INVALID,
MATCHUP_ID,
ROUND_NUMBER,
STRUCTURE_ID,
VALIDATE,
} from '@Constants/attributeConstants';

export function adHocPositionSwap(params) {
const paramsCheck = checkRequiredParameters(params, [
{ [DRAW_DEFINITION]: true, [STRUCTURE_ID]: true, [MATCHUP_ID]: true },
{ [ROUND_NUMBER]: true, [VALIDATE]: (value) => Number.isInteger(value) && value > 0 },
{
[VALIDATE]: (value) => Array.isArray(value) && value.length === 2 && value.every(isString),
[INVALID]: INVALID_PARTICIPANT_IDS,
participantIds: true,
},
]);
if (paramsCheck.error) return paramsCheck;

const { drawDefinition, structureId, tournamentRecord, event } = params;
const { structure } = findStructure({ drawDefinition, structureId });
if (!structure) return { error: STRUCTURE_NOT_FOUND };
if (!isAdHoc({ structure })) return { error: INVALID_STRUCTURE };

const hasParticipant = (matchUp) =>
matchUp.sides?.map((side) => side.participantId).some((id) => params.participantIds.includes(id));
const targetRoundNumber = (matchUp) => matchUp.roundNumber === params.roundNumber;
const noScoreValue = (matchUp) => !checkScoreHasValue(matchUp);

// find two unscored rounds matchUps that contain the two participants being swapped
const targetMatchUps = (structure?.matchUps ?? [])
.filter(targetRoundNumber)
.filter(noScoreValue)
.filter(hasParticipant);
if (targetMatchUps.length !== 2) return { error: INVALID_PARTICIPANT_IDS };

// swap the participantIds in the two matchUps
for (const matchUp of targetMatchUps) {
const side = matchUp?.sides?.find((side) => params.participantIds.includes(side.participantId));
if (side) {
const swappedParticipantId = params.participantIds.find((id) => id !== side?.participantId);
side.participantId = swappedParticipantId;

modifyMatchUpNotice({
tournamentId: tournamentRecord?.tournamentId,
eventId: event?.eventId,
drawDefinition,
matchUp,
});
}
}

modifyDrawNotice({ drawDefinition, structureIds: [structureId] });

return { ...SUCCESS };
}
3 changes: 2 additions & 1 deletion src/mutate/matchUps/drawPositions/positionSwap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { findStructure } from '@Acquire/findStructure';
import { makeDeepCopy } from '@Tools/makeDeepCopy';

// constants
import { SWAP_PARTICIPANT_METHOD } from '@Constants/positionActionConstants';
import { CONTAINER } from '@Constants/drawDefinitionConstants';
import { TEAM_MATCHUP } from '@Constants/matchUpTypes';
import { TEAM_EVENT } from '@Constants/eventConstants';
Expand Down Expand Up @@ -77,7 +78,7 @@ export function swapDrawPositionAssignments({ tournamentRecord, drawDefinition,

conditionallyDisableLinkPositioning({ structure, drawPositions });
const positionAction = {
name: 'swapDrawPositionAssignments',
name: SWAP_PARTICIPANT_METHOD,
drawPositions,
structureId,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,14 @@ export function adHocMatchUpActions({
?.filter((participant) => availableSwaps.includes(participant.participantId))
.map((participant) => makeDeepCopy(participant, undefined, true));
validActions.push({
payload: { drawId, matchUpId, structureId, sideNumber, roundNumber: matchUp.roundNumber },
payload: {
participantIds: [sideParticipantId],
roundNumber: matchUp.roundNumber,
structureId,
sideNumber,
matchUpId,
drawId,
},
swappableParticipantIds: availableSwaps,
method: SWAP_ADHOC_PARTICIPANT_METHOD,
type: SWAP_PARTICIPANTS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { expect, test } from 'vitest';

// constants
import { SWAP_PARTICIPANTS } from '@Constants/positionActionConstants';
import { INVALID_PARTICIPANT_IDS, INVALID_VALUES } from '@Constants/errorConditionConstants';
import { AD_HOC } from '@Constants/drawDefinitionConstants';
import { randomPop } from '@Tools/arrays';

test('adHocMatchUpActions', () => {
const drawId = 'did';
Expand Down Expand Up @@ -32,8 +34,15 @@ test('adHocMatchUpActions', () => {
},
];

const result = tournamentEngine.executionQueue(methods);
let result = tournamentEngine.executionQueue(methods);
expect(result.success).toBe(true);
const swapAction = result.results[0].validActions.find((action) => action.type === SWAP_PARTICIPANTS);
expect([9, 10].includes(swapAction.swappableParticipantIds.length)).toBe(true);
const swappableParticipantId = randomPop(swapAction.swappableParticipantIds);
const { method, payload } = swapAction;
result = tournamentEngine[method](payload);
expect(result.error).toEqual(INVALID_PARTICIPANT_IDS);
payload.participantIds.push(swappableParticipantId);
result = tournamentEngine[method](payload);
expect(result.success).toBe(true);
});

0 comments on commit 3fa5c62

Please sign in to comment.